使我的COM程序集调用异步

Dav*_*vid 10 c# com multithreading

我刚刚"获得"在我目前的工作中维护用C#编码的遗留库的特权.

这个dll:

  • 公开使用Uniface制作的大型遗留系统的方法,除了调用COM对象之外别无选择.
  • 用作此遗留系统与另一个系统API之间的链接.
  • 在某些情况下,使用WinForm作为其UI.

更直观,据我了解组件:

*[Big legacy system in Uniface]* == [COM] ==> [C# Library] == [托管API] ==> *[Big EDM Management System]*

问题是:这个C#库中的一个方法运行时间太长而且我"应该"使它异步!

我习惯了C#,但根本不习惯COM.我已经完成了并发编程,但是COM似乎增加了很多复杂性,到目前为止我所有的试验都结束了:

  • 崩溃时根本没有错误消息
  • 我的Dll只能部分工作(只显示部分UI,然后关闭),但仍然没有给我任何错误

我没有关于如何在COM DLL中处理线程的想法和资源,我将不胜感激任何提示或帮助.

到目前为止,代码的最大部分我已经改变,使我的方法异步:

// 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
}
Run Code Online (Sandbox Code Playgroud)

任何提示或有用的资源将不胜感激.谢谢.

更新1:

下面的答案和线索(特别是关于SynchronizationContext,并在此示例的帮助下)我能够重构我的代码并使其工作,但仅在从C#中的另一个Window应用程序调用时,而不是通过COM调用.遗留系统遇到一个非常模糊的错误,当我调用该函数并且没有提供有关崩溃的任何细节时.

更新2:

我的试验中的最新更新:当我从测试项目调用而不是Uniface系统调用时,我设法使多线程工作.经过多次试验后,我们倾向于认为我们的遗留系统在其当前配置中不支持良好的多线程.但这不再是问题的重点:)

这是代码的一个似乎工作的exerpt:

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);
}
Run Code Online (Sandbox Code Playgroud)

我们现在正在考虑其他解决方案而不是修改此COM程序集,例如将此库封装在Windows服务中并在系统和服务之间创建接口.它应该更具可持续性..

Zde*_*vic 3

在不了解更多细节的情况下很难判断,但这里存在一些问题。

您通过另一个线程执行委托BeginInvoke,但不等待它。您的try\catch块不会捕获任何内容,因为它已经通过,而远程调用仍在执行。相反,您应该将try\catchblock 放在里面AsyncComparedSearch

由于您不等待远程方法执行结束(EndInvoke或通过回调),我不确定如何处理 COM 调用的结果。我猜你是从 内部更新 GUI 的AsyncComparedSearch。如果是这样,那么它是错误的,因为它正在另一个线程上运行,并且您不应该从 GUI 线程之外的任何地方更新 GUI - 它很可能会导致崩溃或其他意外行为。因此,您需要将 GUI 更新工作同步到 GUI 线程。在WinForms中,您需要使用Control.BeginInvoke(不要将其与Delegate.BeginInvoke混淆)或其他方式(例如SynchronizationContext)将代码同步到GUI线程。我使用类似的东西:

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

然后我从任何线程这样称呼它:

theForm.ExecuteOnUiThread( () => theForm.SomeMethodWhichUpdatesControls() );
Run Code Online (Sandbox Code Playgroud)

此外,请阅读此答案以了解一些注意事项。