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;
}