User32 SendMessage зависает, когда насос сообщений находится в режиме ожидания

У меня многопоточная DLL для стороннего приложения. Моя DLL вызывает сообщения в основной поток пользовательского интерфейса, вызывая SendMessage с пользовательским типом сообщения:

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 (), наконец, пройдет и возобновит обработку.

Основное приложение использует Microsoft Fibers, 1 волокно на документ. Может ли мой SendMessage застрять в фоновом волокне, которое переключается или что-то в этом роде? в оптоволокне перед тем, как оно станет неактивным или что-то в этом роде, и только путем принудительного переключения контекста это волокно когда-нибудь сможет обрабатывать свои сообщения? Я действительно не понимаю, как нити и волокна взаимодействуют друг с другом, поэтому я как бы цепляюсь за соломинку.

Что может привести к тому, что сообщения останутся необработанными? Что еще более важно, есть ли способ предотвратить возникновение этой ситуации? Или, по крайней мере, как мне отладить такую ​​ситуацию?

2 ответа

Решение

Я реализовал собственную очередь сообщений и формат сообщений, в котором используется семафор для уведомления о получении сообщения, а другой - после его завершения, а затем повторяю PostMessage каждую 1 секунду, пока сообщение "сообщение не получено" не получит сигнал, затем дождитесь "сообщения завершено" с бесконечным таймаутом.

Любые дополнительные сообщения PostMessages игнорируются, поскольку они больше не содержат полезной нагрузки для выполнения, они просто сообщают основному потоку проверить очередь на наличие входящих событий.

Поскольку я внес эти изменения, я больше не сталкивался с ситуацией. Насколько я могу судить, отправленное сообщение должно заканчиваться в очереди коммутируемого волокна и забываться до тех пор, пока это волокно не будет снова включено. Повторно отправляя сообщение, оно может просто повторять попытки, пока активное волокно не заметит находящееся там сообщение.

Проверьте аргументы GetMessage, Третье и четвертое - это диапазон идентификаторов сообщений. Ваше сообщение будет счастливо находиться в очереди, если его идентификатор находится за пределами этого диапазона.

Другие вопросы по тегам