Сделать ассемблер сборки COM
Я только что "заработал" привилегию сохранить устаревшую библиотеку, закодированную на С#, в моей текущей работе.
Эта dll:
- Предоставляет методы для большой старой системы, созданной с помощью Uniface, у которой нет выбора, кроме вызова COM-объектов.
- Служит как ссылка между этой унаследованной системой и другим системным API.
- В некоторых случаях WinForm использует свой интерфейс.
Более визуально, поскольку я понимаю компоненты:
*[Big legacy system in Uniface]*
== [COM] == > [C# Library]
== [Управляемый API] == > *[Big EDM Management System]*
Вопрос в том, что один из методов в этой библиотеке С# слишком длинен для запуска, и я должен "сделать" его асинхронным!
Я привык к С#, но не к COM вообще. Я уже выполнял параллельное программирование, но COM, похоже, добавляет к нему много сложностей, и все мои испытания до сих пор заканчиваются:
- Авария без сообщения об ошибке
- Моя Dll работает только частично (отображает только часть пользовательского интерфейса, а затем закрывается) и до сих пор не дает мне никакой ошибки.
У меня нет идей и ресурсов о том, как обрабатывать потоки в DLL COM, и я был бы признателен за любой намек или помощь.
До сих пор большая часть кода, который я изменил, чтобы сделать мой метод асинхронным:
// my public method called by the external system
public int ComparedSearch(string application, out string errMsg) {
errMsg = "";
try {
Action<string> asyncOp = AsyncComparedSearch;
asyncOp.BeginInvoke(application, null, null);
} catch (ex) {
// ...
}
return 0;
}
private int AsyncComparedSearch(string application) {
// my actual method doing the work, that was the called method before
}
Любой намек или полезный ресурс будут оценены.
Спасибо.
ОБНОВЛЕНИЕ 1:
Ниже приведены ответы и подсказки ниже (особенно о SynchronizationContext
, и с помощью этот пример) мне удалось реорганизовать мой код и заставить его работать, но только при вызове из другого приложения Window в С#, а не через COM.
Унаследованная система сталкивается с довольно неясной ошибкой при вызове функции и не дает никаких подробностей о сбое.
ОБНОВЛЕНИЕ 2:
Последние обновления в моих тестах: мне удалось сделать многопоточную работу, когда вызовы сделаны из тестового проекта, и не из системы Uniface.
После нескольких испытаний мы склонны думать, что наша устаревшая система не поддерживает многопоточность в текущей конфигурации. Но это не вопрос вопроса:)
Вот пример кода, который, кажется, работает:
string application;
SynchronizationContext context;
// my public method called by the external system
public int ComparedSearch(string application, out string errMsg) {
this.application = application;
context = WindowsFormsSynchronizationContext.Current;
Thread t = new Thread(new ThreadStart(AsyncComparedSearchAndShowDocs));
t.Start();
errMsg = "";
return 0;
}
private void AsyncComparedSearch() {
// ANY WORK THAT AS NOTHING TO DO WITH UI
context.Send(new SendOrPostCallback(
delegate(object state)
{
// METHODS THAT MANAGE UI SOMEHOW
}
), null);
}
Мы рассматриваем другие решения, кроме модификации этой сборки COM, такие как инкапсуляция этой библиотеки в службу Windows и создание интерфейса между системой и службой. Он должен быть более устойчивым..
Ответы
Ответ 1
Трудно сказать, не зная подробностей, но здесь есть несколько проблем.
Вы выполняете делегат в другом потоке через BeginInvoke
, но вы не ждете его. Ваш блок try\catch
не поймает ничего, поскольку он уже прошел, пока удаленный вызов все еще выполняется. Вместо этого вы должны положить try\catch
в блок AsyncComparedSearch
.
Поскольку вы не дождались окончания выполнения удаленного метода (EndInvoke
или через обратный вызов), я не уверен, как вы обрабатываете результаты COM-вызова. Я предполагаю, что вы обновляете графический интерфейс из AsyncComparedSearch
. Если это так, это неправильно, поскольку он работает в другом потоке, и вы никогда не должны обновлять графический интерфейс нигде, кроме потока графического интерфейса - это, скорее всего, приведет к сбою или другому непредвиденному поведению. Поэтому вам нужно синхронизировать работу обновления графического интерфейса с графическим интерфейсом. В WinForms вам нужно использовать Control.BeginInvoke (не путайте его с Delegate.BeginInvoke) или каким-либо другим способом (например, SynchronizationContext), чтобы синхронизировать код с потоком графического интерфейса. Я использую что-то похожее на это:
private delegate void ExecuteActionHandler(Action action);
public static void ExecuteOnUiThread(this Form form, Action action)
{
if (form.InvokeRequired) { // we are not on UI thread
// Invoke or BeginInvoke, depending on what you need
form.Invoke(new ExecuteActionHandler(ExecuteOnUiThread), action);
}
else { // we are on UI thread so just execute the action
action();
}
}
то я вызываю его так из любого потока:
theForm.ExecuteOnUiThread( () => theForm.SomeMethodWhichUpdatesControls() );
Кроме того, прочитайте этот ответ для некоторых оговорок.