11

現在の仕事で、C# でコーディングされたレガシー ライブラリを維持する特権を「獲得」しました。

この dll:

  • COM オブジェクトを呼び出すしかない、Uniface で作成された大規模なレガシー システムのメソッドを公開します。
  • このレガシー システムと別のシステムの API との間のリンクとして機能します。
  • 場合によっては UI に WinForm を使用します。

コンポーネントを理解しているので、より視覚的に:

*[Big legacy system in Uniface]* ==[COM]==> [C# Library] ==[マネージ API]==> *[Big EDM Management System]*

問題は、この C# ライブラリのメソッドの 1 つが実行に時間がかかりすぎるため、非同期にする必要があるということです。

私は 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の助けを借りて)、コードをリファクタリングして機能させることができましたが、それは COM ではなく C# の別の Window アプリケーションから呼び出された場合のみでした。関数を呼び出すと、従来のシステムで非常にあいまいなエラーが発生し、クラッシュに関する詳細が表示されません。

更新 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);
}

現在、このライブラリを Windows サービスにカプセル化し、システムとサービス間のインターフェイスを作成するなど、この COM アセンブリを変更する以外の解決策を検討しています。もっとサステイナブルになればいいのに..

4

1 に答える 1

3

詳細を知らずに判断するのは難しいですが、ここにはいくつかの問題があります。

別のスレッドでデリゲートを実行しBeginInvokeますが、それを待ちません。try\catchリモート呼び出しがまだ実行されている間にブロックが既に通過しているため、ブロックは何もキャッチしません。try\catch代わりに、ブロックを 内に配置する必要があります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 に答える