У меня многопоточная DLL для стороннего приложения. My 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 застрять в фоновом волокне, которое переключается или что-то в этом роде? в оптоволокне перед тем, как оно станет неактивным или что-то в этом роде, и только путем принудительного переключения контекста это волокно когда-нибудь сможет обрабатывать свои сообщения? Я действительно не понимаю, как нити и волокна взаимодействуют друг с другом, поэтому я как бы цепляюсь за соломинку.
Что может привести к тому, что сообщения останутся необработанными? Что еще более важно, есть ли способ предотвратить возникновение этой ситуации? Или, по крайней мере, как мне отладить такую ситуацию?
Проверьте аргументы GetMessage
, Третье и четвертое — это диапазон идентификаторов сообщений. Ваше сообщение с радостью будет находиться в очереди, если его идентификатор находится вне этого диапазона.
Я реализовал собственную очередь сообщений и формат сообщений, в котором семафор используется для уведомления о получении сообщения, а другой — после его завершения, а затем повторяет PostMessage каждую 1 секунду, пока сообщение «сообщение не получено» не будет сигнализировано , затем дождитесь «сообщения завершено» с бесконечным таймаутом.
Любые дополнительные сообщения PostMessages игнорируются, поскольку они больше не содержат полезной нагрузки для выполнения, они просто сообщают основному потоку проверить очередь на наличие входящих событий.
Поскольку я внес эти изменения, я больше не сталкивался с ситуацией. Насколько я могу судить, отправленное сообщение должно заканчиваться в очереди коммутируемого волокна и забываться до тех пор, пока это волокно не будет снова включено. Повторно отправляя сообщение, оно может просто повторять попытки, пока активное волокно не заметит находящееся там сообщение.