11

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

这个dll:

  • 公开使用 Uniface 制作的大型遗留系统的方法,该系统别无选择,只能调用 COM 对象。
  • 充当此遗留系统与另一个系统的 API 之间的链接。
  • 在某些情况下,它的 UI 使用 WinForm。

更直观地,据我了解组件:

*[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
}

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

更新 1:

遵循下面的答案和线索(尤其是关于SynchronizationContext,并在此示例的帮助下),我能够重构我的代码并使其工作,但仅当从 C# 中的另一个 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 服务中并在系统和服务之间创建接口。它应该更可持续..

4

1 回答 1

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();
  }
}

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

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

此外,请阅读此答案以获取一些警告。

于 2013-03-11T16:01:31.780 回答