windows 未调用 MFC 的 CWinThread::PostThreadMessage 处理程序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12764300/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MFC's CWinThread::PostThreadMessage handler not called
提问by eladidan
I am working on some legacy code which uses MFC's UI Threads to implement a manager thread-worker thread mechanism. The code used to run under an MFC GUI application but now its in a separate dll and is run both from the GUI application and from a console application.
我正在处理一些使用 MFC 的 UI 线程来实现管理器线程工作者线程机制的遗留代码。曾经在 MFC GUI 应用程序下运行的代码,但现在它在一个单独的 dll 中,并且从 GUI 应用程序和控制台应用程序运行。
The manager thread, worker threads and main application communicate via Thread messages (the worker thread doesn't really need to send messages to the manager thread but this is the way it was implemented originally and worked, so there you go).
管理器线程、工作线程和主应用程序通过 Thread 消息进行通信(工作线程并不真正需要向管理器线程发送消息,但这是它最初实现和工作的方式,所以你去)。
Now, when I run my code from the console app, messages sent from the main thread to the manager thread are handled and my handler is called. It is only when I try to send a message from the manager thread to the worker threads that I have problems. The call to PostThreadMessagesucceeds but the handler is never invoked.
This behavior was reproduced in both a plain-old console application as well as in a Win32 console application (that includes a pre-compiled header with all the MFC goodies).
现在,当我从控制台应用程序运行我的代码时,会处理从主线程发送到管理器线程的消息并调用我的处理程序。只有当我尝试从管理器线程向工作线程发送消息时,我才会遇到问题。调用PostThreadMessage成功但从不调用处理程序。此行为在普通控制台应用程序和 Win32 控制台应用程序(包括带有所有 MFC 优点的预编译标头)中均重现。
I found this old Microsoft article: http://support.microsoft.com/kb/142415but I have to admit I didn't really understand it. I tried as it suggests to override the PreTranslateMessagefunction and to explicitly handle my custom messages there but the function was never invoked after calling PostThreadMessage
我找到了这篇旧的 Microsoft 文章:http: //support.microsoft.com/kb/142415但我不得不承认我并没有真正理解它。我按照它的建议尝试覆盖该PreTranslateMessage函数并在那里显式处理我的自定义消息,但该函数在调用后从未被调用PostThreadMessage
I tried to reproduce the problem in the sample below and in my sample even the message to the manager thread is never handled, which confirms my suspicion that I'm doing something wrong.
我试图在下面的示例中重现该问题,在我的示例中,甚至从未处理过发送到经理线程的消息,这证实了我对我做错了什么的怀疑。
EDIT: I added the missing InitInstanceand ExitInstanceto overload ManagerThreadthat was missing from my sample code, as MarsRover suggested and indeed ManagerThreadmessages are now pumped, but WorkerThreadmessages are not, which acurately reproduces the problem I'm having in my original code.
Sample code:
编辑:我添加了我的示例代码中缺少的缺失InitInstance和ExitInstance重载ManagerThread,正如 MarsRover 所建议的,ManagerThread现在确实消息被泵送,但WorkerThread消息不是,这准确地再现了我在原始代码中遇到的问题。示例代码:
//Common.h
//update the progress message
#define WM_START_RUN (WM_USER + 1)
//update the progress message
#define WM_JOB_DONE (WM_USER + 2)
//run thread has finished
#define WM_RUN (WM_USER + 3)
// ManagerThread.h
class ManagerThread : public CWinThread
{
DECLARE_DYNCREATE(ManagerThread)
protected:
ManagerThread(){} // protected constructor used by dynamic creation
virtual ~ManagerThread();
BOOL InitInstance();
int ExitInstance();
std::vector<WorkerThread*> m_WorkerThreads;
int numOfJobs;
DECLARE_MESSAGE_MAP()
afx_msg void OnStartRun(WPARAM wParam, LPARAM lParam);
afx_msg void OnJobDone(WPARAM wParam, LPARAM lParam);
afx_msg void OnQuit(WPARAM wParam, LPARAM lParam);
};
//WorkerThread.h
class WorkerThread : public CWinThread
{
DECLARE_DYNCREATE(WorkerThread)
protected:
WorkerThread(){} // protected constructor used by dynamic creation
virtual ~WorkerThread(){}
virtual BOOL InitInstance();
virtual int ExitInstance();
public:
void SetManager(CWinThread* pManager) {m_Manager = pManager;}
void SetID(int _id) {id = _id;}
protected:
int id;
CWinThread* m_Manager;
DECLARE_MESSAGE_MAP()
afx_msg void OnRun(WPARAM wParam, LPARAM lParam);
afx_msg void OnQuit(WPARAM wParam, LPARAM lParam);
};
// ManagerThread.cpp
IMPLEMENT_DYNCREATE(ManagerThread, CWinThread)
ManagerThread::~ManagerThread() {
while(!m_WorkerThreads.empty()) {
std::vector<WorkerThread*>::iterator it = m_WorkerThreads.begin();
(*it)->PostThreadMessage(WM_QUIT, 0, 0);
m_WorkerThreads.erase(it);
}
}
BOOL CFilterManagerThread::InitInstance()
{
return CWinThread::InitInstance();
}
int CFilterManagerThread::ExitInstance()
{
return CWinThread::ExitInstance();
}
BEGIN_MESSAGE_MAP(ManagerThread, CWinThread)
ON_THREAD_MESSAGE(WM_START_RUN, OnStartRun)
ON_THREAD_MESSAGE(WM_JOB_DONE, OnJobDone)
ON_THREAD_MESSAGE(WM_QUIT, OnQuit)
END_MESSAGE_MAP()
void ManagerThread::OnJobDone( WPARAM wParam, LPARAM lParam) {
numOfJobs--;
if (!numOfJobs) {
OnQuit(0,0);
}
}
void ManagerThread::OnStartRun(WPARAM wParam, LPARAM lParam) {
numOfJobs = (int) wParam;
for (int i = 0; i < numOfJobs; i++) {
WorkerThread *newThread = (WorkerThread*)AfxBeginThread(RUNTIME_CLASS(WorkerThread), THREAD_PRIORITY_LOWEST, 0, CREATE_SUSPENDED);
newThread->SetID(i);
newThread->SetManager(this);
m_WorkerThreads.push_back(newThread);
newThread->ResumeThread();
Sleep(1000); //sleep 1 second before sending message to allow the thread to strat running
newThread->PostThreadMessage(WM_RUN, 0, 0);
}
}
void ManagerThread::OnQuit(WPARAM wParam, LPARAM lParam) {
AfxEndThread(0);
}
// WorkerThread.cpp
IMPLEMENT_DYNCREATE(WorkerThread, CWinThread)
BOOL WorkerThread::InitInstance() {
// TODO: perform and per-thread initialization here
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
return TRUE;
}
int WorkerThread::ExitInstance() {
// TODO: perform any per-thread cleanup here
//uninitialize the COM library
CoUninitialize();
return CWinThread::ExitInstance();
}
BEGIN_MESSAGE_MAP(WorkerThread, CWinThread)
ON_THREAD_MESSAGE(WM_RUN, OnRun)
ON_THREAD_MESSAGE(WM_QUIT, OnQuit)
END_MESSAGE_MAP()
void WorkerThread::OnRun(WPARAM wParam, LPARAM lParam) {
cout << id <<endl;
m_Manager->PostThreadMessage(WM_JOB_DONE, id, 0);
}
void WorkerThread::OnQuit(WPARAM wParam, LPARAM lParam) {
AfxEndThread(0);
}
and in main:
并在main:
ManagerThread *manager = (ManagerThread*)AfxBeginThread(RUNTIME_CLASS(ManagerThread), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
manager->ResumeThread();
Sleep(1000); //sleep 1 second before sending message to allow the thread to start running
manager->PostThreadMessage(WM_START_RUN, 10, 0);
while(true){}
This is a rough sample. Of course in my original codeI use better mechanisms than Sleepand while(true)to ensure synchronization and to avoid the program ending before the manager thread has ended. But it reproduces the problem I'm having so I didn't see the point in adding any more complexity.
这是一个粗略的样本。当然,在我的原始代码中,我使用了比Sleepand更好的机制while(true)来确保同步并避免程序在管理器线程结束之前结束。但它重现了我遇到的问题,所以我没有看到增加更多复杂性的意义。
回答by eladidan
Figured out what the problem was.
The problem was the call to CoInitializeExin WorkerThread::initInstance. Apparently the call blocked the initialization of the thread for a LONG time, even more than my Sleep(1000) in the sample code. And so I was posting the messages before the message queue was created. So following the instructions from MSDN:
想通了是什么问题。问题是调用CoInitializeExin WorkerThread::initInstance。显然,该调用阻塞了线程的初始化很长一段时间,甚至比示例代码中的 Sleep(1000) 还多。所以我在创建消息队列之前发布消息。因此,请按照MSDN的说明进行操作:
The thread to which the message is posted must have created a message queue, or else the call to PostThreadMessage fails. Use the following method to handle this situation.
Create an event object, then create the thread.
Use the WaitForSingleObject function to wait for the event to be set to the signaled state before calling PostThreadMessage.
In the thread to which the message will be posted, call PeekMessage as shown here to force the system to create the message queue.
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE)
Set the event, to indicate that the thread is ready to receive posted messages.
消息被发送到的线程必须已经创建了一个消息队列,否则对 PostThreadMessage 的调用将失败。使用以下方法来处理这种情况。
创建一个事件对象,然后创建线程。
在调用 PostThreadMessage 之前,使用 WaitForSingleObject 函数等待事件设置为信号状态。
在消息将被发送到的线程中,调用 PeekMessage,如下所示,以强制系统创建消息队列。
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE)
设置事件,以指示线程已准备好接收发布的消息。
and from this previous question, I created a member CEventfor each thread class and changed the InitInstanceto:
从上一个问题,我CEvent为每个线程类创建了一个成员并将其更改InitInstance为:
BOOL CFilterWorkerThread::InitInstance()
{
BOOL worked=CWinThread::InitInstance();
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
MSG msg;
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
m_ControllerThreadReady.SetEvent();
return TRUE;
}
In order to force the message queue to be initialized before I set the even to true. Then in ManagerThreadI call WaitForSingleObjectbefore posting any messages to WorkerThread.
为了在我将 even 设置为 true 之前强制初始化消息队列。然后在ManagerThread我将WaitForSingleObject任何消息发布到WorkerThread.

