MFC下一个通用非阻塞的等待执行结束的对话框类

头文件:CPictureEx用于显示一个等待动画

#pragma once
#include "afxwin.h"

#include "resource.h"

#include "PictureEx.h"

#include <thread>

// CWaitDlg 对话框
class CWaitSingleEvent {
public:
    CWaitSingleEvent()
        : handle_(nullptr){
            handle_ = CreateEvent(nullptr, TRUE, FALSE, nullptr);
    }

    ~CWaitSingleEvent(){
        if(handle_) {
            CloseHandle(handle_);
        }
    }

    operator bool()const{
        return handle_ != nullptr;
    }

    int Wait(const DWORD wait_time) {
        DWORD retval = 0;
        if(handle_) {
            retval = WaitForSingleObject(handle_, wait_time);
            ResetEvent(handle_);

        }
        return retval;
    }

    void Notify() {
        if(handle_) {
            SetEvent(handle_);
        }
    }

protected:
    CWaitSingleEvent(const CWaitSingleEvent& rhs){

    }

    CWaitSingleEvent& operator=(const CWaitSingleEvent& rhs){
        return *this;
    }
private:
    HANDLE handle_;
};

typedef UINT32 (CDialogEx::*WaitToExec)(void* param);

class CWaitDlg : public CDialogEx
{
    DECLARE_DYNAMIC(CWaitDlg)

public:
    CWaitDlg(CWnd* pParent = NULL);   // 标准构造函数
    virtual ~CWaitDlg();

// 对话框数据
    enum { IDD = IDD_DIALOG4 };

    //设置执行等待函数
    void SetWaitToExec(CWnd* pParent, void* pInnerParam, CDialogEx* pThis, WaitToExec pWaitToExec);

private:
    void* exec_inner_param_;
    CDialogEx* dialog_this;
    WaitToExec wait_to_exec_func_pt_;

    volatile bool is_quit_thread_;
    std::thread*  wait_to_exec_thread_;
    CWaitSingleEvent wait_to_exec_condition_;

    void wait_to_exec_callback();

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

    DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
    CPictureEx  m_show_gif_;
    virtual void OnCancel();
    virtual void OnOK();
};

代码实现:

// WaitDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "WaitDlg.h"
#include "afxdialogex.h"

// CWaitDlg 对话框

IMPLEMENT_DYNAMIC(CWaitDlg, CDialogEx)

CWaitDlg::CWaitDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(CWaitDlg::IDD, pParent), wait_to_exec_func_pt_(nullptr), dialog_this(nullptr)
{
    is_quit_thread_ = false;
    exec_inner_param_ = NULL;
    wait_to_exec_thread_ = nullptr;
}

CWaitDlg::~CWaitDlg()
{
    is_quit_thread_ = true;
    wait_to_exec_condition_.Notify();

    if (nullptr != wait_to_exec_thread_){
        if(wait_to_exec_thread_->joinable()) {
            wait_to_exec_thread_->join();
        }

        wait_to_exec_thread_ = nullptr;
    }
}

void CWaitDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_SHOW_GIF, m_show_gif_);
}


BEGIN_MESSAGE_MAP(CWaitDlg, CDialogEx)
END_MESSAGE_MAP()


// CWaitDlg 消息处理程序
BOOL GetResGifSize(long nResId, LPCTSTR name, long *lnWidth, long *lnHeight)
{
    HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(nResId), name);
    if (NULL == hRsrc) {
        return FALSE;
    }

    DWORD dwSize = SizeofResource(NULL, hRsrc);
    HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
    if (NULL == hGlobal) {
        CloseHandle(hRsrc);
        return FALSE;
    }

    unsigned char* pBuffer = (unsigned char*)LockResource(hGlobal);
    if (NULL == pBuffer) {
        CloseHandle(hRsrc);
        FreeResource(hGlobal);
        return FALSE;
    }

    //判断是否为GIF文件
    if(pBuffer[0] != 0x47 && pBuffer[1] != 0x49 && pBuffer[2] != 0x46 && pBuffer[3] != 0x38){
        return FALSE;
    }

    //读取宽高
    for(DWORD i = 4; i < dwSize ; i++)
    {
        if(pBuffer[i] == 0x00 && pBuffer[i+1] == 0x2c)
        {
            *lnWidth = (pBuffer[i+7]<<8) | pBuffer[i+6];
            *lnHeight = (pBuffer[i+9]<<8) | pBuffer[i+8];
            UnlockResource(hGlobal);
            FreeResource(hGlobal);
            return TRUE;
        }
    }

    UnlockResource(hGlobal);
    FreeResource(hGlobal);

    return FALSE;
}

BOOL CWaitDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    //设置窗口大小
    long nGifWidth = 75;
    long nGifHeight = 75;
    if (!GetResGifSize(IDR_WAIT_GIF_1, _T("GIF"), &nGifWidth, &nGifHeight)){
        MessageBox(_T("内部错误"),_T("错误"), MB_OK | MB_ICONERROR);
    }

    RECT rt;
    GetClientRect(&rt);

    //判断窗口是否太大,或者太小,放不下
    if ( (rt.right - rt.left - nGifWidth > 64) || (rt.bottom - rt.top - nGifHeight > 64) ||
         (rt.right - rt.left < nGifWidth) || (rt.bottom - rt.top < nGifHeight) ) {
        RECT rtWin;
        GetWindowRect(&rtWin);

        //调整窗口大小
        rtWin.right = rtWin.left + nGifWidth + 64;
        rtWin.bottom = rtWin.top + nGifHeight + 64;
        MoveWindow(&rtWin);

        //重新获取客户区大小
        GetClientRect(&rt);
    }

    CenterWindow();

    //计算位置
    rt.left = (rt.right - rt.left - nGifWidth)/2;
    rt.right = rt.left + nGifWidth;
    rt.top = (rt.bottom - rt.top - nGifHeight)/2;
    rt.bottom = rt.left + nGifHeight;

    //转化成屏幕坐标
    //ClientToScreen(&rt);
    m_show_gif_.MoveWindow(&rt, TRUE);

    SetBackgroundColor(RGB(240,240,240), TRUE);
    m_show_gif_.SetBkColor(RGB(240,240,240));
    if (m_show_gif_.Load(MAKEINTRESOURCE(IDR_WAIT_GIF_1),_T("GIF"))){
        m_show_gif_.Draw();
    }

    return TRUE;  // return TRUE unless you set the focus to a control
    // 异常: OCX 属性页应返回 FALSE
}

void CWaitDlg::wait_to_exec_callback()
{
    if(is_quit_thread_) {
        return ;
    }
    while (!is_quit_thread_){

        //等待事件是否过来
        wait_to_exec_condition_.Wait(INFINITE);
        if(is_quit_thread_) {break;}

        //判断参数是否OK
        if ((nullptr != dialog_this) && (nullptr != wait_to_exec_func_pt_)){
            (dialog_this ->*wait_to_exec_func_pt_)(exec_inner_param_);

            //执行完一次,那就隐藏一下
            if (NULL != m_hWnd)
            {
                ShowWindow(SW_HIDE);
            }
        }
    }
}

//设置执行等待函数
void CWaitDlg::SetWaitToExec(CWnd* pParent, void* pInnerParam, CDialogEx* pThis, WaitToExec pWaitToExec)
{
    exec_inner_param_ = pInnerParam;
    dialog_this = pThis;
    wait_to_exec_func_pt_ = pWaitToExec;

    //通知线程执行
    is_quit_thread_ = false;

    if (wait_to_exec_thread_ == nullptr) {
        wait_to_exec_thread_ = new std::thread;
    }
    if(!wait_to_exec_thread_->joinable()) {
        *wait_to_exec_thread_ = std::move(std::thread(&CWaitDlg::wait_to_exec_callback, this));
    }
    //显示本窗口
    if (NULL == m_hWnd) {
        Create(IDD_DIALOG4, pParent);
    }

    ShowWindow(SW_SHOW);

    //触发事件
    wait_to_exec_condition_.Notify();
}

void CWaitDlg::OnCancel()
{
    // TODO: 在此添加专用代码和/或调用基类

    //CDialogEx::OnCancel();
}

void CWaitDlg::OnOK()
{
    // TODO: 在此添加专用代码和/或调用基类

    //暂时允许关闭
    CDialogEx::OnOK();
}

使用示例:

typedef struct set_xxx_info_st {
    int index;
    CString strXxxInfo;
}set_xxx_info_st;

void CXXXParamDlg::OnClickedBtnWriteXxx()
{
    CString strXxxC = _T("strXxxC");

    // 必须使用堆内存,调用会立即返回,但对象的使用,却在线程中
    set_xxx_info_st* inn_param = new set_xxx_info_st;
    inn_param->index = m_set_cert_index_combo_.GetCurSel() + 1;
    inn_param->strXxxInfo.Append(strXxxC);

    //调用线程接口去获取
    m_wait_dlg_.SetWaitToExec(GetParent(), inn_param, this, (WaitToExec)&CXXXParamDlg::WaitToExecSetXXXInfo);
}

UINT32 CXXXParamDlg::WaitToExecSetXXXInfo(void* inn_param)
{
    UINT32 ulRet = -1;

    set_xxx_info_st* set_param = (set_xxx_info_st*)(inn_param);

    //禁止父窗口控件
    GetParent()->GetParent()->EnableWindow(FALSE);

    //设置进去
    ExecSetXXX(set_param->index, set_param->strXxxInfo);

    //禁止父窗口控件
    GetParent()->GetParent()->EnableWindow(TRUE);

    if (nullptr != set_param) {
        delete set_param;
    }

    return 0;
}
posted @ 2019-08-24 16:05  日月王  阅读(1492)  评论(7编辑  收藏  举报