0

簡単に言うと、COM inproc-server(dll)で動作するC#アプリケーションで、「0x80010100:システムコールに失敗しました」という例外が発生し、デバッグモードでもContextSwitchDeadlock例外が発生します。

詳細は次のとおりです。

1)C#アプリはSTAを初期化し、COMオブジェクト(「Apartment」として登録)を作成します。次に、inはその接続ポイントにサブスクライブし、オブジェクトの操作を開始します。

2)ある段階で、COMオブジェクトは多くのイベントを生成し、同じアパートで作成されたCOMオブジェクトの非常に大きなコレクションを引数として渡します。

3)C#側のイベントハンドラーは上記のコレクションを処理し、オブジェクトのいくつかのメソッドを呼び出すことがあります。ある段階で、後者の呼び出しは上記の例外を除いて失敗し始めます。

COM側では、アパートはwinprocが次のように見える非表示のウィンドウを使用します。

typedef std::function<void(void)> Functor;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)   
{   
  switch(msg)   
  {   
    case AM_FUNCTOR:
    {
      Functor *f = reinterpret_cast<Functor *>(lParam);
      (*f)();
      delete f;
    }
    break;   
    case WM_CLOSE:   
      DestroyWindow(hwnd);   
    break;   
    default:   
      return DefWindowProc(hwnd, msg, wParam, lParam);   
  }   
  return 0;   
} 

イベントは、COMサーバーの他の部分からこのウィンドウに投稿されます。

void post(const Functor &func)
{
  Functor *f = new Functor(func);
  PostMessage(hWind_, AM_FUNCTOR, 0, reinterpret_cast<LPARAM>(f));
}

イベントは、実際のパラメータにバインドされた標準のATL CP実装であり、要約すると次のようになります。

pConnection->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL);

C#では、ハンドラーは次のようになります。

private void onEvent(IMyCollection objs)
{
  int len = objs.Count; // usually 10000 - 25000
  foreach (IMyObj obj in objs)
  {
    // some of the following calls fail with 0x80010100
    int id = obj.id;
    string name = obj.name;
    // etc...
  }
}

==================

それで、アパートのメッセージキューが配信しようとするイベントでいっぱいになっているという理由だけで、上記の問題が発生する可能性がありますか?または、そのような動作を引き起こすために、メッセージループを完全にブロックする必要がありますか?

メッセージキューに、「onEvent」呼び出しと評価される2つの連続したイベントがあると仮定します。最初のコードはC#マネージコードを入力します。これは、同じアパートであるアンマネージコードを再入力しようとします。通常、これは許可されており、私たちはこれを頻繁に行います。いつ、どのような状況で失敗する可能性がありますか?

ありがとう。

4

1 に答える 1

2

これは、複数のアパートでも機能するはずです。

  • スレッドの1つだけが、ネットワークトラフィック、タイマー、投稿されたメッセージなどの外部イベントに応答します。
  • 他のスレッドは、COM要求のみを処理します(処理中にメインスレッドにコールバックする場合でも)。

  • どちらのスレッドキューもいっぱいにならず、COMがスレッドと通信できなくなります。

まず 、一部のオブジェクトが他のオブジェクトと同じアパートにないようです。すべてのオブジェクトがSTAで作成されていることを確認しますか?

あなたが説明しているのは、古典的なデッドロックです。2つの独立したスレッドが、それぞれが互いに待機しています。これは、異なるスレッドのC#側とCOM側で動作するその設計で発生すると予想されることです。

すべてのオブジェクトが同じスレッド上にあり、非表示のウィンドウがそのスレッド上にある場合は問題ないはずなので、それを確認する必要があると思います。(明らかに、これには、COM側で作成され、C#側に渡される他のオブジェクトが含まれます。)

デバッガーで「一時停止」を押し、各スレッドにどのコードが含まれているかを確認することで、これをデバッグしてみることができます(RPCRT * .DLLが表示されている場合、これはプロキシを見ていることを意味します)。または、C#側とCOM側の両方のさまざまな重要なポイントとWndProcから現在のスレッドIDをDebugPrintすることもできます。これらはすべて同じである必要があります。

次に、スレッドの1つだけが作業項目を生成し、もう1つは呼び出しに応答するCOMオブジェクトをホストするだけである場合(つまり、タイマー、ネットワークトラフィック、投稿されたメッセージなどからの呼び出しを生成しない)、複数のスレッドで動作する必要があります。 、この場合、スレッドキューがいっぱいで、COMが呼び出しに応答できない可能性があります。

スレッドキューを使用する代わりに、クリティカルセクションで保護された両端キューを使用する必要があります。

  • http://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx

    メッセージキューごとに投稿されるメッセージは10,000件に制限されています。この制限は十分に大きくする必要があります。アプリケーションが制限を超える場合は、システムリソースを大量に消費しないように、アプリケーションを再設計する必要があります。

これが問題であるかどうかを確認するために、キューのオン/オフのアイテムのカウンターを維持することができます。

于 2012-02-21T15:31:37.773 に答える