65.9K
CodeProject 正在变化。 阅读更多。
Home

你好,我已准备好沟通!

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2012年6月20日

CPOL

4分钟阅读

viewsIcon

28879

downloadIcon

395

使用窗口消息进行双向通信的 CWinThread。

引言

昨天晚上,我正在做一个个人项目,其中一个需求是使用UI线程。由于我可以使用MFC,所以我考虑使用CWinThread。当我深入研究它的概念时,我萌生了写一篇关于这项非常宏伟的旧技术的文章的念头,它与.NET的世界相去甚远。

Using the Code

现在,这里是我们的问题陈述。

问题陈述 #1

“创建一个UI线程,该线程每秒向主线程发送消息,将计数器加1。”

解决问题 #1 的分步指南

  1. 如今,VS向导非常强大,您无需手动编写MFC派生类,只需在类视图中右键单击项目,然后添加派生自CWinThread的类即可。所以,只需右键单击项目名称 -> 添加 -> 类
  2. 打开对话框后,将新类命名为CCountingThread。以下是向导将生成的骨架
    class CCountingThread : public CWinThread
    {
        DECLARE_DYNCREATE(CCountingThread)
    protected:
        CCountingThread();           // protected constructor used by dynamic creation
        virtual ~CCountingThread();
     
    public:
        virtual BOOL InitInstance();
        virtual int ExitInstance();
    protected:
        DECLARE_MESSAGE_MAP()
    };
  3. 现在,向类添加三个变量以服务我们的目的
    • UINT_PTR m_uTimerID:将跟踪SetTimer的ID
    • int m_iCount:将跟踪下一个增量值
    • CWnd* m_pParentWnd:将指向主窗口的指针。我知道CWinThread本身还有一个变量用于此任务(m_pMainWnd)。但是,我只想保持简单。
  4. 现在,由于主对话框必须每秒用新值更新,我想使用WM_TIMER消息,设置1秒的 elapsed time,并在每次收到timer消息时,向主窗口发送一条带有新值的新消息。所以,现在,在CCountingThread中添加新函数来处理WM_TIMER消息。
    void CCountingThread::OnTimer(WPARAM wParam, LPARAM lParam)
    {
        m_pParentWnd->PostMessageW(WM_USER+2,0,++m_iCount); ---- (1)
    }

    在这里,我为向主窗口发送新值的消息编写了代码(1)。

  5. 现在,为了使我们的CCountingThread::OnTimer (…) 函数对WM_TIMER消息的MessageLoop可见,请在BEGIN_MESSAGE_MAP() END_MESSAGE_MAP()之间添加以下代码。
    ON_THREAD_MESSAGE(WM_TIMER,&CCountingThread::OnTimer)
  6. 现在,在CCountingThread::InitInstance() 中编写timer的激活代码,并在CCountingThread::ExitInstance()中编写de-activation代码,如下所示:
    BOOL CCountingThread::InitInstance(){
        m_uTimerID = SetTimer(NULL,2001,1000,NULL);
        return TRUE;
    }
    int CCountingThread::ExitInstance(){
        KillTimer(NULL,m_uTimerID);
        return CWinThread::ExitInstance();
    }

    在为我们的WinThread派生类完成上述任务后。

  7. 现在,设计主窗口UI,包含一个编辑框(用于显示来自Thread的值)和两个按钮(用于启动和停止线程),并为主编辑框添加相关的处理程序和控件变量。还要在类中添加CCountingThread的指针。在执行以上操作之前,不要忘记在您的mainwindow类头文件中添加CountingThread.h
  8. 将以下代码添加到您的Start按钮处理程序中
    void CUserThread1Dlg::OnBnClickedStartThread()
    {
        if(m_pRunningThread== NULL)
        {
            m_pRunningThread = (CCountingThread*)AfxBeginThread(
                RUNTIME_CLASS(CCountingThread),
                0,
                0,
                CREATE_SUSPENDED,
                NULL); --- (a)
     
            m_pRunningThread->m_pParentWnd = this; -- (b)
            m_pRunningThread->ResumeThread();  --- (c) 
        }
    }
    1. 使用AfxBeginThread API以挂起模式创建我们的UI线程,将CCountingThread的运行时类作为第一个参数,将CREATE_SUSPENDED作为第四个参数传递。
    2. 为通信提供m_pRunningThread->m_pParentWnd,即我们的主窗口指针。
    3. m_pRunningThread->ResumeThread():将启动我们的线程。
  9. 现在,编写以下代码来停止我们的UI线程。
    void CUserThread1Dlg::OnBnClickedStopThread()
    {
    	if(m_pRunningThread!= NULL)
    	{
    		m_pRunningThread->PostThreadMessageW(WM_QUIT,0,0); --- (a)
    
    		m_pRunningThread = NULL; --(b)
    	}
    }
    1. 关闭UI线程的最佳方法是向线程发送WM_QUIT消息,它将优雅地关闭。因为WM_QUIT会使UI线程的消息泵退出。
    2. 我将m_pRunningThread设置为NULL,这在DEMO场景中是可以的,但在实际问题中,您需要编程实现同步和线程的正确退出,然后将m_pRunningThread赋值为NULL
  10. 我们的UI线程每秒发送WM_USER+2消息,其中包含更新的计数器值,因此请在我们的对话框类中添加一个函数来处理它。所以添加以下代码
    LRESULT CUserThread1Dlg::OnCountingIncrease(WPARAM wParam, LPARAM lParam)
    {
    	CString strText;
    	strText.Format(_T("%d"),lParam);
    	m_edtCounting.SetWindowTextW(strText);
    	return LRESULT(0);
    }

    并在BEGIN_MESSAGE_MAP()中添加消息监听器。

    ON_MESSAGE(WM_USER+2, &CUserThread1Dlg::OnCountingIncrease)
  11. 构建并运行您的应用程序,看看它是否正常工作。

……等等,我告诉过您会有双向通信,但在这里它只是一向通信,即UI线程发送消息,主窗口监听。所以,实现这一点,即双向通信,让我们扩展问题陈述#1并得出以下新陈述:

问题陈述 #2

“添加重置计数器按钮,将计数重置为零。”

分步指南

  1. 在主对话框中,添加一个新的按钮“重置计数器”,并在您的代码中添加处理程序。
  2. 现在,将以下代码添加到“重置计数器”按钮的OnClick处理程序中。
    void CUserThread1Dlg::OnBnClickedResetThreadcounter()
    {
    	if(m_pRunningThread!= NULL) –(a)
    	{
    		m_pRunningThread->PostThreadMessageW(WM_USER+1,0,0); -- (b)
    	}
    }
    1. m_pRunningThreadCCountingThread类的对象,在单击“开始线程”按钮时创建。
    2. 使用PostThreadMessageW 我们将向UI线程发送消息。
  3. 现在,在CCountingThread类中添加函数来处理主对话框发送的WM_USER+1用户消息。
    void CCountingThread::ResetCounter(WPARAM wParam, LPARAM lParam){
    	m_iCount =0; -- reset the counter
    }
  4. 还在BEGIN_MESSAGE_MAP() END_MESSAGE_MAP()中添加以下消息监听器。
    ON_THREAD_MESSAGE(WM_USER+1,&CCountingThread::ResetCounter)
  5. 编译并运行应用程序。

历史

  • 2012年6月20日:第一个版本
© . All rights reserved.