【发布时间】:2015-09-09 11:48:12
【问题描述】:
我有一个用于第三方应用程序的多线程 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 是否会卡在被关闭的背景光纤中?在光纤处于非活动状态或其他情况之前在光纤上,并且只有通过强制上下文切换,该光纤才能处理其消息?我真的不明白线和纤维是如何相互作用的,所以我有点想抓住稻草。
什么会导致消息像这样未经处理而坐在那里?更重要的是,有没有办法防止这种情况发生?或者至少,我该如何调试这种情况?
【问题讨论】:
-
您很可能将消息发送到一个窗口,该窗口由一个没有消息泵的线程拥有
-
不,我发布了拥有 HWND 的线程的调用堆栈,它在 _NtUserGetMessage() 上闲置等待。可能有 9999/10000 条消息得到了正确处理,只是偶尔有一条消息挂在那里被遗忘,等待处理。
-
根据您的描述,该消息永远不会进入队列。有时这可能是一种误导性症状(意味着正在发生的其他事情没有正确报告)。您的输出窗口是否显示任何值得注意的项目?
-
只是一堆消息“线程'Win32 线程'(0x???)已退出,代码为0(0x0)。”
-
SendMessage() 是dangerous 并且容易导致死锁。请改用 PostMessage()。
标签: c++ multithreading winapi fibers