我有一个用于第三方应用程序的多线程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()事件最终通过,并恢复处理。
主要应用程序使用Microsoft Fibers,每个文档1个光纤。我的SendMessage可能会陷入背景光纤或其他问题吗?在它变为非活动状态之前的光纤上,并且仅通过强制上下文切换,光纤是否能够处理其消息?我真的不明白线和纤维是如何相互作用的,所以我有点抓住稻草。
什么可能导致邮件像这样未处理?更重要的是,有没有办法防止这种情况发生?或者至少,我该如何调试这种情况?
答案 0 :(得分:0)
我继续实现了自己的消息队列,以及一种消息格式,它使用信号量在收到消息时通知,另一个在消息完成时通知,然后每隔1秒重复一次PostMessage,直到收到“消息” “收到信号,然后等待”消息完成“,无限超时。
任何额外的PostMessages都会被忽略,因为它们不再包含要执行的有效负载,它们只是告诉主线程检查队列是否有传入的事件。
由于我做了这些修改,我没有再遇到这种情况。我能说的最好,发送的消息必须以交换光纤的队列结束,并且忘记直到光纤再次接通为止。通过重新发布消息,它可以继续重试,直到活动光纤注意到那里的消息。
答案 1 :(得分:0)
检查GetMessage
的参数。第三个和第四个是消息ID范围。如果您的ID超出此范围,您的消息将很乐意排在队列中。