0

サードパーティ アプリケーション用のマルチスレッド dll があります。私の dll は、カスタム メッセージ タイプで SendMessage を呼び出すことにより、メイン UI スレッドにメッセージを呼び出します。

typedef void (*CallbackFunctionType)();
DWORD _wm;
HANDLE _hwnd;
DWORD threadId;

Initialize()
{
    _wm = RegisterWindowMessage("MyInvokeMessage");
    WNDCLASS wndclass = {0};
    wndclass.hInstance = (HINSTANCE)&__ImageBase;
    wndclass.lpfnWndProc = wndProcedure;
    wndclass.lpszClassName = "MessageOnlyWindow";
    RegisterClass(&wndclass);
    _hwnd = CreateWindow(
         "MessageOnlyWindow",
         NULL,
         NULL,
         CW_USEDEFAULT,
         CW_USEDEFAULT,
         CW_USEDEFAULT,
         CW_USEDEFAULT,
         NULL,
         NULL,
         (HINSTANCE)&__ImageBase,
         NULL);
    threadId = GetCurrentThreadId();
}

void InvokeSync(CallbackFunctionType funcPtr)
{
    if (_hwnd != NULL && threadId != GetCurrentThreadId())
        SendMessage(_hwnd, _wm, 0, (LPARAM)funcPtr);
    else
        funcPtr();
}
static LRESULT CALLBACK wndProcedure(
    HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    if (Msg == _wm)
    {
        CallbackFunctionType funcPtr = (CallbackFunctionType)lParam;
        (*funcPtr)();
    }
    return DefWindowProc(hWnd, Msg, wParam, lParam);
}

アプリケーションは MDI で、ドキュメントを開く/コンテンツを抽出する/バックグラウンドで処理する/大量のドキュメントを保存するため、常にアクティブなドキュメントを切り替えたり、新しいドキュメントを開いたり閉じたりしています。

私の問題は、上記の InvokeSync() 関数を使用してメイン スレッドにメッセージを呼び出そうとすると、処理が停止することがあることです。

デバッガーで一時停止すると、メイン スレッドに次のコール スタックがあることがわかります。

user32.dll!_NtUserGetMessage@16() + 0x15 bytes
user32.dll!_NtUserGetMessage@16() + 0x15 bytes
mfc42.dll!CWinThread::PumpMessage() + 0x16 bytes
// the rest is normal application stuff

ロックされているバックグラウンド スレッドには、次のような呼び出し履歴があります。

user32.dll!_NtUserMessageCall@28() + 0x15 bytes
user32.dll!_NtUserMessageCall@28() + 0x15 bytes
mydll!InvokeSync(funcPtr)
// the rest is expected dll stuff

そのため、「SendMessage()」呼び出しでスタックしているように見えますが、私が見る限り、メイン スレッドのメッセージ ポンプはアイドル状態になっています。

ただし、非アクティブなドキュメントを (アクティブにするために) 手動でクリックすると、どういうわけかすべてが起動し、SendMessage() イベントが最終的に通過し、処理が再開されます。

メイン アプリケーションは、ドキュメントごとに 1 ファイバーの Microsoft ファイバーを使用します。SendMessage がバックグラウンド ファイバーでスタックして、スイッチ アウトされたりすることはありますか? 非アクティブまたは何かになる直前のファイバーで、コンテキストスイッチを強制することによってのみ、そのファイバーはメッセージを処理することができますか? 糸と繊維の相互作用がよくわからないので、ストローをつかんでいます。

メッセージがこのように未処理のまま放置される原因は何ですか? さらに重要なことに、この状況が発生するのを防ぐ方法はありますか? または、少なくとも、そのような状況をどのようにデバッグしますか?

4

2 に答える 2

0

先に進んで、独自のメッセージキューと、セマフォを使用してメッセージが受信されたときに通知し、別のメッセージが完了したときに通知するメッセージ形式を実装し、「メッセージ受信」が通知されるまで1秒ごとに PostMessage を繰り返します、無限のタイムアウトで「メッセージ完了」を待ちます。

余分な PostMessage は無視されます。実行するペイロードが含まれていないためです。受信イベントのキューをチェックするようにメイン スレッドに指示するだけです。

これらの変更を行って以来、このような状況に再び遭遇することはありません。私が知る限り、送信されたメッセージは、切り替えられたファイバーのキューに入れられ、そのファイバーが再び切り替えられるまで忘れられているに違いありません。メッセージを再投稿することにより、アクティブなファイバーがメッセージがそこにあることに気付くまで再試行を続けることができます。

于 2015-06-23T19:17:17.503 に答える
0

への引数を確認してくださいGetMessage。3 番目と 4 番目はメッセージ ID の範囲です。ID がこの範囲外の場合、メッセージは問題なくキューに入れられます。

于 2015-06-23T19:53:00.277 に答える