未调用 MFC 的 CWinThread::PostThreadMessage 处理程序

Posted

技术标签:

【中文标题】未调用 MFC 的 CWinThread::PostThreadMessage 处理程序【英文标题】:MFC's CWinThread::PostThreadMessage handler not called 【发布时间】:2012-10-06 22:06:27 【问题描述】:

我正在处理一些使用 MFC 的 UI 线程来实现管理器线程工作线程机制的遗留代码。以前在 MFC GUI 应用程序下运行的代码,但现在它在一个单独的 dll 中,并且可以从 GUI 应用程序和控制台应用程序运行。

管理线程、工作线程和主应用程序通过线程消息进行通信(工作线程实际上并不需要向管理线程发送消息,但这是它最初实现和工作的方式,所以你去吧)。

现在,当我从控制台应用程序运行代码时,会处理从主线程发送到管理器线程的消息并调用我的处理程序。只有当我尝试从管理线程向工作线程发送消息时,我才会遇到问题。对PostThreadMessage 的调用成功,但从未调用处理程序。 这种行为在一个普通的控制台应用程序和一个 Win32 控制台应用程序(包括一个带有所有 MFC 好东西的预编译头)中都重现了。

我找到了这篇旧的 Microsoft 文章:http://support.microsoft.com/kb/142415,但我不得不承认我并没有真正理解它。我尝试按照它的建议覆盖 PreTranslateMessage 函数并在那里显式处理我的自定义消息,但在调用 PostThreadMessage 后从未调用过该函数

我试图在下面的示例中重现该问题,在我的示例中,甚至从未处理到管理器线程的消息,这证实了我的怀疑,即我做错了什么。

编辑:我添加了丢失的 InitInstanceExitInstance 以重载我的示例代码中缺少的 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);

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)

这是一个粗略的示例。当然,在我的原始代码中,我使用比Sleepwhile(true) 更好的机制来确保同步并避免程序在管理器线程结束之前结束。但它重现了我遇到的问题,所以我没有看到增加任何复杂性的意义。

【问题讨论】:

为什么不在“ManagerThread”类中覆盖“InitInstance”。 @MarsRover:你是对的,见编辑。它也让我解决了 WorkerThread 的问题,很快就会发布解决方案 【参考方案1】:

找出问题所在。 问题是在WorkerThread::initInstance 中对CoInitializeEx 的调用。显然,该调用长时间阻塞了线程的初始化,甚至超过了示例代码中的 Sleep(1000)。所以我在创建消息队列之前发布了消息。所以按照MSDN的指示:

发布消息的线程必须已创建消息 队列,否则对 PostThreadMessage 的调用将失败。使用以下 处理这种情况的方法。

创建一个事件对象,然后创建线程。

使用 WaitForSingleObject 函数等待事件被设置 到调用 PostThreadMessage 之前的信号状态。

在要发布消息的线程中,调用 PeekMessage 为 此处显示强制系统创建消息队列。

PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE)

设置事件,表示线程准备好接收posted 消息。

从这个previous question,我为每个线程类创建了一个成员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;

为了在我将 even 设置为 true 之前强制初始化消息队列。然后在ManagerThread 中,我在向WorkerThread 发布任何消息之前致电WaitForSingleObject

【讨论】:

以上是关于未调用 MFC 的 CWinThread::PostThreadMessage 处理程序的主要内容,如果未能解决你的问题,请参考以下文章

在 MFC 应用程序中未调用 ExitInstance

MFC 工作线程在意外关闭时未清理

MFC 对话框应用程序未关闭

MFC编写DLL怎么用C风格

借助复选框控件 (MFC) 启用/禁用编辑框

MFC 对话框窗体冻结