• MFC模式窗口 不及格的程序员


    #if !defined(AFX_BASEWORKTHREAD_H__D104C15C_8BCD_475B_91C4_4960EBE866A4__INCLUDED_)
    #define AFX_BASEWORKTHREAD_H__D104C15C_8BCD_475B_91C4_4960EBE866A4__INCLUDED_
    
    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000
    // BaseWorkThread.h : header file
    #include "DialogLoading.h"
    //
    
    /////////////////////////////////////////////////////////////////////////////
    // BaseWorkThread window
    
    //iori 多线程
    #define WM_ONTHREADFINISHED (WM_USER + 199)
    #define WM_ONWorkFinishPercent (WM_USER + 201)
    
    class AFX_EXT_CLASS BaseWorkThread : public CWnd
    {
    // Construction
    public:
        BaseWorkThread(CWnd* cWnd = NULL);
    // Attributes
    public:
        CDialogLoading dlgLoading;
        bool bIsShowLoading;
        bool bIsShowModal;
    // Operations
    public:
        CWinThread* startWork();
        static UINT innerBeginThread(BaseWorkThread* instance); //内部静态方法,用于启动工作线程    
    
    // Overrides
        // ClassWizard generated virtual function overrides
        //{{AFX_VIRTUAL(BaseWorkThread)
        //}}AFX_VIRTUAL
    
    // Implementation
    public:
        virtual ~BaseWorkThread();
    
        // Generated message map functions
    protected:
        CWinThread *m_pThread;
        void workFinish();
        void postPercent(float percent); //发送消息到主循环 消息回调到onFinishPercent 之后会执行onPercent
        virtual void onPercent(float percent);//子类拿到进度完成百分比
        virtual UINT worker() ; // 工作线程调用的实现体 2022-03-11
        virtual void onWorkerFinished(); // 线程加载完成的回调函数体
        //{{AFX_MSG(BaseWorkThread)
            // NOTE - the ClassWizard will add and remove member functions here.
            afx_msg LRESULT workder_onFinished(WPARAM wParam, LPARAM lParam);
            afx_msg void onFinishPercent();
        //}}AFX_MSG    
        DECLARE_MESSAGE_MAP()
    private:
        volatile float mPercent; // atomic<float> c++11
    };
    
    /////////////////////////////////////////////////////////////////////////////
    
    //{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ will insert additional declarations immediately before the previous line.
    
    #endif // !defined(AFX_BASEWORKTHREAD_H__D104C15C_8BCD_475B_91C4_4960EBE866A4__INCLUDED_)
    // BaseWorkThread.cpp : implementation file
    //
    
    #include "stdafx.h"
    #include "BaseWorkThread.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif
    
    /////////////////////////////////////////////////////////////////////////////
    // BaseWorkThread
    
    BaseWorkThread::BaseWorkThread(CWnd* pParent)
    {
        this->bIsShowLoading = true;
        this->bIsShowModal = false;
        //初始化 m_hWnd;
        CWnd::Create(NULL,_T("MySocketManage"),WS_CHILD,CRect(0,0,0,0), pParent,1234);
    }
    
    BaseWorkThread::~BaseWorkThread()
    {
    }
    
    CWinThread* BaseWorkThread::startWork()
    {
        CWinThread *m_pThread = NULL;
        if(this->bIsShowModal)
        {
            m_pThread = ::AfxBeginThread((AFX_THREADPROC)BaseWorkThread::innerBeginThread, this);
            if(this->bIsShowLoading)
            {
                dlgLoading.workThread = m_pThread;
                dlgLoading.ShowModal();
            }
            return m_pThread;
        }
        if(this->bIsShowLoading)
        {
            BOOL result = dlgLoading.Show();
            if(result)
            {
                m_pThread = ::AfxBeginThread((AFX_THREADPROC)BaseWorkThread::innerBeginThread, this);
                dlgLoading.workThread = m_pThread;
            }
        }
        else
        {
            m_pThread = ::AfxBeginThread((AFX_THREADPROC)BaseWorkThread::innerBeginThread, this);
        }
        return m_pThread;
    }
    
    UINT BaseWorkThread::innerBeginThread(BaseWorkThread* instance)
    {
        return instance->worker(); 
    }
    
    UINT BaseWorkThread::worker()
    {
        this->postPercent(0.3f);
        Sleep(5000);
        this->postPercent(0.6f);
        Sleep(5000);
        HINSTANCE hInst=0;
         SHELLEXECUTEINFO sInfo;
         sInfo.hwnd=NULL;
         sInfo.lpVerb="open";
         sInfo.lpFile="SETUP.exe";
         sInfo.lpParameters="";
         sInfo.lpDirectory="./debug/";
         sInfo.nShow=SW_SHOWNORMAL;
         sInfo.hInstApp=hInst;
         sInfo.cbSize=sizeof(SHELLEXECUTEINFO);
         sInfo.fMask=SEE_MASK_NOCLOSEPROCESS;
         this->postPercent(1.0f);
         Sleep(2000);
         //ShellExecuteEx(&sInfo);
    
         //WaitForSingleObject(sInfo.hProcess, INFINITE );
         /*
         如果某个软件是用 Windows Installer 打包的,那你就应该能在文件夹中看到 *.msi 文件。这是最典型的特征,这些文件通常可以使用 /QB 和 /QN 参数进行自动安装。
    
      /qb 会在窗口中显示一个基本的安装进程。
      /qn 参数则不会显示任何窗口,直接在后台自动安装。
    
      为了阻止某些程序安装成功后自动重启动(例如 Kerio Personal Firewall 4),你可以在 /qn 或者 /qb参数后使用REBOOT=Suppress标记。
    
      例如:安装虚拟光驱 DaemonTools:msiexec /i dtools.msi /qb REBOOT=SUPPRESS
    */
        this->workFinish();
        return 0; //成功返回
    }
    
    void BaseWorkThread::workFinish()
    {
        ::PostMessage(this->m_hWnd, WM_ONTHREADFINISHED, 1, 1);
    }
    
    void BaseWorkThread::postPercent(float percent)
    {
        this->mPercent = percent;
        ::PostMessage(this->m_hWnd, WM_ONWorkFinishPercent, 1, 1);
    }
    
    void BaseWorkThread::onPercent(float percent)
    {
        CString strResult;
        strResult.Format("work finish Percent:%.2f\r\n", percent);
        TRACE(strResult);
        this->dlgLoading.setProgress(percent);
    }
    
    LRESULT BaseWorkThread::workder_onFinished(WPARAM wParam, LPARAM lParam)
    {
        if(this->dlgLoading && this->bIsShowLoading)
        {
            if(this->bIsShowModal)
            {
                //this->dlgLoading.EndDialog(0);
                this->dlgLoading.cancel();
            }
            else
            {
                this->dlgLoading.ShowWindow(0);
                //this->dlgLoading.DestroyWindow(); 非模式执行完一次扣 对话框的 Hwnd 被清除,导至下次产生不了窗口
            }
        }
        this->onWorkerFinished();
        return 0;
    }
    
    void BaseWorkThread::onWorkerFinished()
    {
        //::AfxMessageBox("线程完成!", 0, 0);
        TRACE("BaseWorkThread::onWorkerFinished() 线程完成\r\n");
    }
    
    void BaseWorkThread::onFinishPercent()
    {
        //可以像委托那样, 再声明一个指针也可以继承此类,调用针指将百分比传递出去
        //本例并没有提供函数指针,可以采用多重继承然后调用子类成员函数传递百分比。
        onPercent(this->mPercent);
    }
    
    BEGIN_MESSAGE_MAP(BaseWorkThread, CWnd)
        //{{AFX_MSG_MAP(BaseWorkThread)
            // NOTE - the ClassWizard will add and remove mapping macros here.
        //}}AFX_MSG_MAP
        ON_MESSAGE(WM_ONTHREADFINISHED, workder_onFinished)
        ON_MESSAGE(WM_ONWorkFinishPercent, onFinishPercent)
    END_MESSAGE_MAP()
    
    
    /////////////////////////////////////////////////////////////////////////////
    // BaseWorkThread message handlers
    int CDialog::DoModal()
    {
        // can be constructed with a resource template or InitModalIndirect
        ASSERT(m_lpszTemplateName != NULL || m_hDialogTemplate != NULL ||
            m_lpDialogTemplate != NULL);
    
        // load resource as necessary
        LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate;
        HGLOBAL hDialogTemplate = m_hDialogTemplate;
        HINSTANCE hInst = AfxGetResourceHandle();
        if (m_lpszTemplateName != NULL)
        {
            hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
            HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
            hDialogTemplate = LoadResource(hInst, hResource);
        }
        if (hDialogTemplate != NULL)
            lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);
    
        // return -1 in case of failure to load the dialog template resource
        if (lpDialogTemplate == NULL)
            return -1;
    
        // disable parent (before creating dialog)
        HWND hWndParent = PreModal();
        AfxUnhookWindowCreate();
        BOOL bEnableParent = FALSE;
        if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
        {
            ::EnableWindow(hWndParent, FALSE);
            bEnableParent = TRUE;
        }
    
        TRY
        {
            // create modeless dialog
            AfxHookWindowCreate(this);
            if (CreateDlgIndirect(lpDialogTemplate,
                            CWnd::FromHandle(hWndParent), hInst))
            {
                if (m_nFlags & WF_CONTINUEMODAL)
                {
                    // enter modal loop
                    DWORD dwFlags = MLF_SHOWONIDLE;
                    if (GetStyle() & DS_NOIDLEMSG)
                        dwFlags |= MLF_NOIDLEMSG;
                    VERIFY(RunModalLoop(dwFlags) == m_nModalResult);
                }
    
                // hide the window before enabling the parent, etc.
                if (m_hWnd != NULL)
                    SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|
                        SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
            }
        }
        CATCH_ALL(e)
        {
            DELETE_EXCEPTION(e);
            m_nModalResult = -1;
        }
        END_CATCH_ALL
    
        if (bEnableParent)
            ::EnableWindow(hWndParent, TRUE);
        if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
            ::SetActiveWindow(hWndParent);
    
        // destroy modal window
        DestroyWindow();
        PostModal();
    
        // unlock/free resources as necessary
        if (m_lpszTemplateName != NULL || m_hDialogTemplate != NULL)
            UnlockResource(hDialogTemplate);
        if (m_lpszTemplateName != NULL)
            FreeResource(hDialogTemplate);
    
        return m_nModalResult;
    }
    int CWnd::RunModalLoop(DWORD dwFlags)
    {
        ASSERT(::IsWindow(m_hWnd)); // window must be created
        ASSERT(!(m_nFlags & WF_MODALLOOP)); // window must not already be in modal state
    
        // for tracking the idle time state
        BOOL bIdle = TRUE;
        LONG lIdleCount = 0;
        BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle() & WS_VISIBLE);
        HWND hWndParent = ::GetParent(m_hWnd);
        m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);
        MSG* pMsg = &AfxGetThread()->m_msgCur;
    
        // acquire and dispatch messages until the modal state is done
        for (;;)
        {
            ASSERT(ContinueModal());
    
            // phase1: check to see if we can do idle work
            while (bIdle &&
                !::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))
            {
                ASSERT(ContinueModal());
    
                // show the dialog when the message queue goes idle
                if (bShowIdle)
                {
                    ShowWindow(SW_SHOWNORMAL);
                    UpdateWindow();
                    bShowIdle = FALSE;
                }
    
                // call OnIdle while in bIdle state
                if (!(dwFlags & MLF_NOIDLEMSG) && hWndParent != NULL && lIdleCount == 0)
                {
                    // send WM_ENTERIDLE to the parent
                    ::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_hWnd);
                }
                if ((dwFlags & MLF_NOKICKIDLE) ||
                    !SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))
                {
                    // stop idle processing next time
                    bIdle = FALSE;
                }
            }
    
            // phase2: pump messages while available
            do
            {
                ASSERT(ContinueModal());
    
                // pump message, but quit on WM_QUIT
                if (!AfxGetThread()->PumpMessage())
                {
                    AfxPostQuitMessage(0);
                    return -1;
                }
    
                // show the window when certain special messages rec'd
                if (bShowIdle &&
                    (pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))
                {
                    ShowWindow(SW_SHOWNORMAL);
                    UpdateWindow();
                    bShowIdle = FALSE;
                }
    
                if (!ContinueModal())
                    goto ExitModal;
    
                // reset "no idle" state after pumping "normal" message
                if (AfxGetThread()->IsIdleMessage(pMsg))
                {
                    bIdle = TRUE;
                    lIdleCount = 0;
                }
    
            } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));
        }
    
    ExitModal:
        m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);
        return m_nModalResult;
    }

    CWnd的派生类-3、CDialog类

    对话框与普通窗口的区别仅在于,对话框是通过对话框模板建立起来的。只需要一个以模板为实参的创建命令,如CDialog::Create(),就可以完成对话框窗口及其子控件的创建工作,所有创建细节都由对话框模板来指示。而对于普通窗口,窗口及其包含的子控件必须逐一创建,而且要指定窗口风格等详细参数。对话框是最基本的可视化编程方法,一个应用程序往往包含众多的对话框资源模板和封装类,而普通窗体(包括框架窗体)却寥寥无几。但对话框的使用,只是方便了窗体和控件的创建过程,其本质与普通窗体无任何区别。

    下面并不准备陈述对话框的技术细节,只与读者讨论两个相关问题:一是模态对话框的消息循环,二是对话框的命令消息路由。

    7.4  模态对话框的消息循环
    模态对话框是程序中最常用的窗口,当调用对话框的DoModal()成员后,就创建了一个模态对话框。其特点是,除了这个对话框窗体外,几乎不能操作程序的其他部分。但如果此时已经打开了两个以上的主窗体,只能禁止模态对话框所在的主窗口及其子窗口,包括主窗口下属的弹出对话框,但不包括下属的重叠窗口和普通弹出窗口。即当模态对话框弹出时,禁止了它的父窗口及大部分兄弟窗口的操作;模态对话框关闭后,被禁用的窗口将恢复使用。

    7.4.1  模态对话框的创建与模式循环
    其实,“模态”并不是对话框的专利,模态特性是封装在CWnd中的。所以,如果采取与模态对话框相同的创建方法,普通窗体也可以是模态的。这个方法就是在创建窗体后,调用CWnd::RunModalLoop()模式循环函数。该函数与前面讲过的CWinThread::Run()非常相似,也是一个消息循环泵,而且CWnd:: RunModalLoop()的消息处理还要稍复杂一些。在学习这个模式循环函数之前,首先来了解模态对话框的创建与销毁过程。下面是对CDialog::DoModal()函数的简单缩写。

    int CDialog::DoModal()

    {  //装入对话框模板资源

    HINSTANCE hInst = AfxGetResourceHandle();   

    hDialogTemplate = LoadResource(hInst, hResource);

             if (lpDialogTemplate == NULL)

                      return -1;

             //在建立模态对话框之前,禁止父窗口的鼠标和键盘输入

             HWND hWndParent = PreModal();//取得父窗口句柄(一般是程序主窗口,如主框架)

             BOOL bEnableParent = FALSE;

             if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))

             {

    //禁止父窗口也将间接地禁止父窗口的下属窗口,但不包括下属的重叠窗口和普通弹出窗口

                      ::EnableWindow(hWndParent, FALSE);

                      bEnableParent = TRUE;

             }

             //通过资源模板创建对话框及其子控件

             if (CreateDlgIndirect(lpDialogTemplate,       CWnd::FromHandle(hWndParent), hInst))

                      { //创建成功

                                       //进入模式循环

                                       DWORD dwFlags = MLF_SHOWONIDLE;

                                       VERIFY(RunModalLoop(dwFlags) == m_nModalResult);

    //当用户选择IDOK或IDCANCEL时,模式循环退出,对话框将被销毁

                      }       

             if (bEnableParent)

                      ::EnableWindow(hWndParent, TRUE);//恢复父窗口的工作状态,间接地恢复其兄弟窗口

             if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)

                      ::SetActiveWindow(hWndParent);//激活父窗口

             //销毁该模式对话框

             DestroyWindow();

             return m_nModalResult;

    }

    从以上代码可知,在模态对话框创建之前,首先要将该程序的主窗口(也是该对话框未来的宿主窗口)禁止。这样,该主窗口以及主窗口下属的所有子窗口和弹出对话框都被禁止。然后调用CreateDlgIndirect()创建对话框。注意,因为该对话框是在禁止主窗口之后创建的,所以它是活动的;也就是说,当前主窗口及其下属的所有窗口中,除重叠窗口和普通弹出窗口外,只有它是活动的。这是模态对话框的特点。可见,只要在该对话框销毁时重新激活主窗口就可以了,至此,已经完成了模态对话框的创建工作。但阅读以上代码会发现,事情并不这么简单,在创建对话框后还需进入模式循环,对话框关闭后,模式循环才退出。模式循环究竟有什么作用呢?

    其实,由RunModalLoop()实现的模态循环,并不是创建模态窗口或模态对话框的方式。如上所述,只要在对话框创建之前禁止主窗口,在对话框销毁时激活主窗口,在形式上就已经实现了所谓的模态对话框。模式循环是专为模态窗口设计的一个消息循环,这个消息循环完成UI线程消息循环(由CWinThread::Run()封装)的全部功能,同时为处理模态窗口的特殊消息,增加了必要的处理代码。当模态窗口创建后,就进入这个消息循环,其中的消息循环泵暂时代替了UI线程的消息循环泵,为所有的窗口提取并分发消息。但所有被禁止的窗口无法接收鼠标和键盘消息,除非使用PostMessage()命令。

    下面讲解CWnd::RunModalLoop()是如何工作的。

    /*******************形参dwFlags可以是下列值的组合*****************

    MLF_NOIDLEMSG     当消息队列空闲时,不发送WM_ENTERIDLE消息给主窗口

    MLF_NOKICKIDLE    当消息队列空闲时,不发送WM_KICKIDLE消息给当前模态窗口

    MLF_SHOWONIDLE当消息队列空闲时,刷新显示当前对话框(仅一次)*/

    int CWnd::RunModalLoop(DWORD dwFlags)

    {

             ASSERT(::IsWindow(m_hWnd)); // window must be created

    //m_nFlags标志当前对话框的状态,值WF_MODALLOOP标志已经进入模态

             ASSERT(!(m_nFlags & WF_MODALLOOP));

             //标志空闲处理入口的状态

             BOOL bIdle = TRUE;

             //连续处理WM_KICKIDLE消息的次数

             LONG lIdleCount = 0;

    //空闲时是否刷新显示当前对话框(仅一次)

             BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle() & WS_VISIBLE);

             HWND hWndParent = ::GetParent(m_hWnd);

             //设置对话框状态标志

             m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);

             MSG* pMsg = &AfxGetThread()->m_msgCur;//取得存储当前消息的缓冲

             for (;;)

             {

                      ASSERT(ContinueModal());//检查是否错误地结束了模式循环

                      //循环1:用于调度空闲处理

                      while (bIdle &&

                              !::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))

                      {

                              ASSERT(ContinueModal());

                              if (bShowIdle)

                              {        //显示刷新当前窗口

                                       ShowWindow(SW_SHOWNORMAL);

                                       UpdateWindow();

                                       bShowIdle = FALSE;

                              }

                              if (!(dwFlags & MLF_NOIDLEMSG) && hWndParent != NULL && lIdleCount == 0)

                              {

                                       //给父窗口发送WM_ENTERIDLE 消息

             ::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_ hWnd);

                              }

                              //或关系,如果第一个条件不成立,执行第二个条件

                              if ((dwFlags & MLF_NOKICKIDLE) ||

                                       !SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))

                              {        //可见,在模态对话框内,可以将WM_KICKIDLE消息作为空闲消息进行处理

                                       bIdle = FALSE;

                              }

                      }

                      //循环2:提取并分发消息 

                      do

                      {        ASSERT(ContinueModal());

                              // pump message, but quit on WM_QUIT

                              if (!AfxGetThread()->PumpMessage())

                              {        AfxPostQuitMessage(0);

                                       return-1;

                              }

                              //收到特殊消息,是否刷新显示该对话框

                              if (bShowIdle &&

                                       (pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))

                              {        ShowWindow(SW_SHOWNORMAL);

                                       UpdateWindow();

                                       bShowIdle = FALSE;

                              }

                              if (!ContinueModal())  //可能是关闭当前对话框的消息,判断是否该结束模式循环

                                       goto ExitModal;

                              //根据刚刚处理的消息类型,判断是否应该在没有消息到来时立即进行空闲处理

                              if (AfxGetThread()->IsIdleMessage(pMsg))

                              {

             bIdle = TRUE;

                                       lIdleCount = 0;

                              }

                      } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));

             }

    ExitModal: //用户已关闭对话框,结束模式循环

             m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL); //清空对话框的模态标志

             return m_nModalResult;     //返回对话框的关闭代码(如IDOK、IDCANCEL)

    }

    通过比较CWinThread::Run()与CWnd::RunModalLoop()两个消息循环的差异,不难发现后者为模态对话框做了哪些工作。模式循环既可以向父窗口发送WM_ENTERIDLE消息,也可以向当前窗口发送与空闲消息等同的WM_KICKIDLE消息,使得模态对话框有能力在空闲时完成一定的操作。同时允许刷新显示对话框。但注意,CWinThread::OnIdle()在模式循环中不被调用。

    在对CWinThread::PumpMessage()的阐述中,曾经提及WM_KICKIDLE消息,它在消息泵中不被分发处理。所以,在模式循环中使用SendMessage()而不是PostMessage()发送该消息。WM_KICKIDLE消息像一个未公开的秘密,没有正式的文档说明,它在afxpriv.h头文件中定义。如果你的模态对话框需要空闲处理,应包含这个头文件,然后手工添加消息映射即可。

    7.4.2  结束模式循环
    阅读RunModalLoop()代码可知,当调用ContinueModal()返回FALSE时,模式循环结束。该函数只是检查m_nFlags状态标志。

    BOOL CWnd::ContinueModal()

    {

             return m_nFlags & WF_CONTINUEMODAL;

    }

    显然,当用户单击IDOK或IDCANCEL时,改变了成员m_nFlags的状态,使得循环结束。下面列出相关的几个成员函数:

    void CDialog::OnOK()

    {        if (!UpdateData(TRUE))

             {        return;

             } //以IDOK为结束代码

             EndDialog(IDOK);

    }

    void CDialog::OnCancel()

    {        //以IDCANCEL为结束代码

             EndDialog(IDCANCEL);}

    void CDialog::EndDialog(int nResult)

    {

             ASSERT(::IsWindow(m_hWnd));

             if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL))

    //如果当前对话框是模态的,则结束模式循环

             EndModalLoop(nResult);

             ::EndDialog(m_hWnd, nResult);//调用API结束有关本对话框的系统处理

    }

    void CWnd::EndModalLoop(int nResult)

    {       

    ASSERT(::IsWindow(m_hWnd));

             //设置返回代码

             m_nModalResult = nResult;

             if (m_nFlags & WF_CONTINUEMODAL)

             {        //设置模式循环结束标志,发送空消息通知消息泵

                      m_nFlags &= ~WF_CONTINUEMODAL;

                      PostMessage(WM_NULL);

             } }

    可见,只要在对话框中调用CDialog::EndDialog()就可以结束模式循环。但结束模式循环后,还必须调用DestroyWindow()销毁对话框,这个工作在DoModal()退出前已经完成。但如果使用CDialog::Create()创建了一个非模态对话框,就不得不在直接或间接调用EndDialog()关闭对话框后,亲自调用DestroyWindow()了。

    7.4.3  创建普通的模态窗口
    通过以上对模态对话框的学习,已经掌握了创建模态窗口的技术。如果需要一个普通的模态窗口,可以参考以下步骤进行操作。

    (1)调用EnableWindow()禁止程序主窗口。如果当前存在多个主窗口,禁止与该模态窗口有所属关系的主窗口。

    (2)使用CWnd::Create()等创建命令,创建该窗口。可以是弹出窗口,也可以是重叠窗口。

    (3)调用模式循环函数RunModalLoop(DWORD dwFlags),根据实际需要设置实参。如果需要空闲处理,还须手工添加消息映射。

    (4)当关闭窗口时调用EndModalLoop(int nResult),根据实际需要设置结束代码。

    (5)激活主窗口,调用DestroyWindow()摧毁当前模态窗口。一定要确保在窗口销毁前已经结束了模式循环。

  • 相关阅读:
    pycharm的list的应用
    pycharm的list中copy的应用
    pycharm的list中clear的应用
    pycharm的list中append的应用
    crontab 管理指定用户的定时任务
    vsftp 搭建及虚拟账号配置
    MySQL 主从配置
    Ant 学习及常用任务
    ansible 小试
    微信H5页面分享
  • 原文地址:https://www.cnblogs.com/ioriwellings/p/16365874.html
Copyright © 2020-2023  润新知