C# 使COM程序集调用异步

C# 使COM程序集调用异步,c#,multithreading,com,C#,Multithreading,Com,在我目前的工作中,我刚刚“赢得”了维护一个用C#编码的遗留库的特权 此dll: 公开使用Uniface创建的大型遗留系统的方法,该系统除了调用COM对象之外别无选择 用作此遗留系统和另一个系统的API之间的链接 在某些情况下,将WinForm用作其UI 更直观地说,正如我对组件的理解: *[Uniface中的大型遗留系统]*=[COM]=>[C#库]=[Managed API]==>*[Big EDM管理系统]* 问题是:这个C#库中的一个方法运行时间太长,我“应该”让它异步 我习惯了C#,

在我目前的工作中,我刚刚“赢得”了维护一个用C#编码的遗留库的特权

此dll:

  • 公开使用Uniface创建的大型遗留系统的方法,该系统除了调用COM对象之外别无选择
  • 用作此遗留系统和另一个系统的API之间的链接
  • 在某些情况下,将WinForm用作其UI
更直观地说,正如我对组件的理解:

*[Uniface中的大型遗留系统]*
=[COM]=>
[C#库]
=[Managed API]==>
*[Big EDM管理系统]*

问题是:这个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
}

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

如果不知道更多细节,很难判断,但这里的问题很少

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

由于您不等待远程方法(
EndInvoke
或通过回调)的执行结束,因此我不确定如何处理COM调用的结果。我猜您应该从
AsyncComparedSearch
中更新GUI。如果是这样,它是错误的,因为它正在另一个线程上运行,您不应该从GUI线程之外的任何地方更新GUI—它很可能导致崩溃或其他意外行为。因此,您需要将GUI更新工作同步到GUI线程。在WinForms中,您需要使用(不要将其与Delegate.BeginInvoke混淆)或其他方式(例如)将代码同步到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();
  }
}
然后我从任何线程中这样称呼它:

theForm.ExecuteOnUiThread( () => theForm.SomeMethodWhichUpdatesControls() );

此外,请阅读一些注意事项。

+1关于这个组织良好的问题。您能提供一个真正小的会崩溃的示例吗?我不确定你到底在做什么来使它崩溃:)(如果是你的错,或者是旧系统不能处理线程)您是说从未捕获异常还是说它根本不包含任何消息?您的
try/catch
块应该进入
AsyncComparedSearch
方法,因为这是在另一个线程上执行的。因为你没有阻挡。你的应用程序如何知道COM呼叫何时完成?您是否在
AsyncComparedSearch
中更新GUI?任意将线程放入一大块代码中从来都不是问题。特别是对于COM,它关心线程。大的红旗也表示缺少对EndInvoke()的调用,EndInvoke()是一个返回int的方法,其值将不会被使用,而
out字符串可能不会被设置。感谢您的回复和链接。
AsyncComparedSearch
中的逻辑非常复杂,不适合在文章中使用。我在其中有多个
try/catch
块,但他们什么也没抓到。我会尽量把重要的细节放在问题的编辑部分。关于GUI的句柄,您是对的,这是从
AsyncComparedSearch
中处理的。我今天会朝这个方向搜索,并让您知道。我已经在回答中添加了一条关于同步的注释。根据您的答案中给出的线索和有关同步上下文的链接,我能够使我的函数正确工作,但只有当调用来自另一个C#项目时,而不是通过COM从旧系统发出调用时。至少,这里有一个积极的发展。将其标记为“答案”,因为它有助于理解多线程问题,即使我们的体系结构中还有其他问题。。。
theForm.ExecuteOnUiThread( () => theForm.SomeMethodWhichUpdatesControls() );