Сделать ассемблер сборки 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() );

Кроме того, прочитайте этот ответ для некоторых оговорок.