使我的COM程序集调用异步
本文关键字:调用 异步 程序集 COM 我的 | 更新日期: 2023-09-27 18:03:32
我刚刚"赢得"了在我目前的工作中维护一个用c#编写的遗留库的特权。
这个dll:
- 为Uniface制作的大型遗留系统公开方法,该系统除了调用COM对象之外别无选择。
- 作为这个遗留系统和另一个系统的API之间的链接。
- 在某些情况下使用WinForm作为UI
更直观,正如我对组件的理解:
*[Big legacy system in Uniface]*
= = (COM) = => [C# Library]
= =(管理API) = => *[Big EDM Management System]*
我习惯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
}
任何提示或有用的资源将不胜感激。谢谢你。
更新1:下面的答案和线索(特别是关于SynchronizationContext
,并在这个例子的帮助下)我能够重构我的代码并使其工作,但只有在c#中从另一个窗口应用程序调用时,而不是通过COM。当我调用这个函数时,遗留系统遇到了一个相当模糊的错误,并且没有给出任何关于崩溃的细节。
我试验中的最新更新:当从测试项目进行调用时,我设法使多线程工作,而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服务中,并在系统和服务之间创建一个接口。
在不了解更多细节的情况下很难判断,但这里有几个问题。
你通过BeginInvoke
在另一个线程上执行委托,但你不等待它。您的try'catch
块不会捕获任何东西,因为它已经通过,而远程调用仍在执行中。相反,您应该将try'catch
块放在AsyncComparedSearch
中。
当你不等待远程方法(EndInvoke
或通过回调)的执行结束时,我不确定你如何处理COM调用的结果。我猜你从AsyncComparedSearch
内更新GUI。如果是这样,这是错误的,因为它运行在另一个线程上,您不应该从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();
}
}
那么我在任何线程中都像这样调用它:
theForm.ExecuteOnUiThread( () => theForm.SomeMethodWhichUpdatesControls() );