[WTL/ATL]_[初级]_[DispatchAsync使用lambda表达式来简化发送数据到界面线程]
程序员文章站
2022-06-10 23:48:16
...
场景
-
在进行
Win32
界面编程时,更新界面内容只能通过主线程进行更新. 那么如果是在工作线程执行完逻辑后如何通过把数据传递给界面线程更新数据? -
在
Windows
上开发界面我是使用轻量级Win32
框架,MFC
的精简版WTL
进行开发,那么在WTL
开发时如何把数据传递给界面?
说明
-
我在这篇博客里《Win32实现Cocoa的dispatch_async到主线程的异步消息处理》有写如何利用
DispatchAsync
来执行主线程任务. -
在这里例子任务里,我们定义了一个全局函, 在主线程执行函数
PrintStr
。实际上我们可以利用C++11
的 lambda 表达式来实现函数功能,避免定义一个只用一次的函数. 可以说用lambda
表达式我们可以使用类似同步的方式进行编码。 -
我们也可以使用
std::bind
来绑定成员函数,或者绑定预定义的函数参数,非常方便。
以下例子使用在 Win32实现Cocoa的dispatch_async到主线程的异步消息处理 里使用的 DispatchAsync
工具函数来在主线程执行弹出对话框。
例子
- 使用
vs2017
和WTL
写的项目里子
2… 在点击菜单File
里选择DispatchAsync
,DispatchAsync2
,PostMessage
对应着不同的发送消息到主线程的用法。
MainFrm.cpp
// MainFrm.cpp : implmentation of the CMainFrame class
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <iostream>
#include <atlstr.h>
#include <assert.h>
#include "resource.h"
#include "aboutdlg.h"
#include "View.h"
#include "MainFrm.h"
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{
if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
return TRUE;
return m_view.PreTranslateMessage(pMsg);
}
BOOL CMainFrame::OnIdle()
{
UIUpdateToolBar();
return FALSE;
}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
// create command bar window
HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE);
// attach menu
m_CmdBar.AttachMenu(GetMenu());
// load command bar images
m_CmdBar.LoadImages(IDR_MAINFRAME);
// remove old menu
SetMenu(NULL);
HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);
CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
AddSimpleReBarBand(hWndCmdBar);
AddSimpleReBarBand(hWndToolBar, NULL, TRUE);
CreateSimpleStatusBar();
m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);
UIAddToolBar(hWndToolBar);
UISetCheck(ID_VIEW_TOOLBAR, 1);
UISetCheck(ID_VIEW_STATUS_BAR, 1);
// register object for message filtering and idle updates
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->AddMessageFilter(this);
pLoop->AddIdleHandler(this);
DispatchQueueInit(m_hWnd);
return 0;
}
LRESULT CMainFrame::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
// unregister message filtering and idle updates
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->RemoveMessageFilter(this);
pLoop->RemoveIdleHandler(this);
bHandled = FALSE;
return 1;
}
LRESULT CMainFrame::OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
PostMessage(WM_CLOSE);
return 0;
}
LRESULT CMainFrame::OnViewToolBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
static BOOL bVisible = TRUE; // initially visible
bVisible = !bVisible;
CReBarCtrl rebar = m_hWndToolBar;
int nBandIndex = rebar.IdToIndex(ATL_IDW_BAND_FIRST + 1); // toolbar is 2nd added band
rebar.ShowBand(nBandIndex, bVisible);
UISetCheck(ID_VIEW_TOOLBAR, bVisible);
UpdateLayout();
return 0;
}
LRESULT CMainFrame::OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
BOOL bVisible = !::IsWindowVisible(m_hWndStatusBar);
::ShowWindow(m_hWndStatusBar, bVisible ? SW_SHOWNOACTIVATE : SW_HIDE);
UISetCheck(ID_VIEW_STATUS_BAR, bVisible);
UpdateLayout();
return 0;
}
LRESULT CMainFrame::OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
CAboutDlg dlg;
dlg.DoModal();
return 0;
}
void CMainFrame::DoDispatchMessage(wchar_t* message)
{
assert(gMainThreadId == GetCurrentThreadId());
MessageBox(message,L"DoDispatchMessage Main Thread",0);
free(message);
}
static DWORD WINAPI DoDispatchWorkThread(void* lpThreadParameter)
{
auto frame = (CMainFrame*)lpThreadParameter;
auto callback = new std::function<void()>(std::bind(&CMainFrame::DoDispatchMessage,
frame,wcsdup(L"DispatchAsync 主线程(界面线程)执行代码1-使用成员函数")));
DispatchAsync(DispatchGetMainQueue(),callback);
return NULL;
}
LRESULT CMainFrame::OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
DWORD threadId = 0;
auto handle = ::CreateThread(NULL,0,&DoDispatchWorkThread,this,0,&threadId);
CloseHandle(handle);
return 0;
}
LRESULT CMainFrame::OnTestPostMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
MessageBox((wchar_t*)wParam,(wchar_t*)lParam,0);
free((wchar_t*)wParam);
free((wchar_t*)lParam);
return 0;
}
static DWORD WINAPI DoWorkThread(void* lpThreadParameter)
{
auto frame = (CMainFrame*)lpThreadParameter;
DispatchAsync(DispatchGetMainQueue(), new std::function<void()>(std::bind([](CMainFrame* frame, wchar_t* message) {
assert(gMainThreadId == GetCurrentThreadId());
::MessageBox(frame->m_hWnd, message, L"DoDispatchMessage Main Thread", 0);
free(message);
}, frame, wcsdup(L"DispatchAsync 主线程(界面线程)执行代码2-使用lambda表达式"))));
return 0;
}
LRESULT CMainFrame::OnFileOpen(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
DWORD threadId = 0;
auto handle = ::CreateThread(NULL,0,&DoWorkThread,this,0,&threadId);
CloseHandle(handle);
return 0;
}
LRESULT CMainFrame::OnFileSave(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
DWORD threadId = 0;
auto func = [](LPVOID data)->DWORD {
// 测试工作线程通过常规的通过PostMessage函数发送数据到界面线程.
auto frame = (CMainFrame*)data;
::PostMessage(frame->m_hWnd, USER_MESSAGE_POST_MESSAGE_1,
(WPARAM)wcsdup(L"不使用DispatchAsync"), (LPARAM)wcsdup(L"测试PostMessage"));
return 0;
};
//auto func2 = [this](LPVOID data)->DWORD {
// ::PostMessage(m_hWnd, USER_MESSAGE_POST_MESSAGE_1,
// (WPARAM)wcsdup(L"不是使用DispatchAsync"), (LPARAM)wcsdup(L"测试PostMessage"));
// return 0;
//};
//auto func3 = std::function<DWORD(LPVOID)>(std::bind([](CMainFrame* frame,LPVOID data)->DWORD {
// ::PostMessage(frame->m_hWnd, USER_MESSAGE_POST_MESSAGE_1,
// (WPARAM)wcsdup(L"不是使用DispatchAsync"), (LPARAM)wcsdup(L"测试PostMessage"));
// return 0;
//},this,std::placeholders::_1));
// 1. C++11只支持lambda类型到函数指针类型的转换,不支持std::function到普通函数类型的转换.
// 2. 对于lambda表达式转换为函数指针类型,也不能使用捕抓变量特性.
auto handle = ::CreateThread(NULL, 0, func,this ,0,&threadId);
CloseHandle(handle);
return 0;
}
MainFrm.h
// MainFrm.h : interface of the CMainFrame class
//
/////////////////////////////////////////////////////////////////////////////
#pragma once
#include <functional>
#include "platform.h"
#include "dispatch_queue.h"
class CMainFrame :
public CFrameWindowImpl<CMainFrame>,
public CUpdateUI<CMainFrame>,
public CMessageFilter, public CIdleHandler
{
public:
DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
CView m_view;
CCommandBarCtrl m_CmdBar;
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual BOOL OnIdle();
BEGIN_UPDATE_UI_MAP(CMainFrame)
UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP)
UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)
END_UPDATE_UI_MAP()
BEGIN_MSG_MAP_EX(CMainFrame)
MESSAGE_HANDLER(USER_MESSAGE_POST_MESSAGE_1,OnTestPostMessage)
MESSAGE_HANDLER(WMC_DISPATCH_MAIN_QUEUE, OnDispatchMainQueueEvent)
MSG_WM_CREATE(OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
COMMAND_ID_HANDLER(ID_FILE_OPEN, OnFileOpen)
COMMAND_ID_HANDLER(ID_FILE_SAVE, OnFileSave)
COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
END_MSG_MAP()
// Handler prototypes (uncomment arguments if needed):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
LRESULT OnTestPostMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
int OnCreate(LPCREATESTRUCT lpCreateStruct);
LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
LRESULT OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnFileOpen(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnFileSave(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnViewToolBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
void DoDispatchMessage(wchar_t* message);
LRESULT OnDispatchMainQueueEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// std::function 使用完之后需要删除.
std::function<void()>* func = (std::function<void()>*)wParam;
(*func)();
delete func;
return 0;
}
};
dispatch_queue.h
#ifndef __DISPATCH_QUEUE_H
#define __DISPATCH_QUEUE_H
#include <Windows.h>
#include <WinUser.h>
#include <functional>
#include <atlbase.h>
#include <atlapp.h>
enum
{
WMC_DISPATCH_MAIN_QUEUE = WM_USER+1000
};
typedef struct DispatchQueueObject1
{
DWORD threadId;
HWND m_hwnd;
}DispatchQueueObject;
extern void DispatchQueueInit(HWND hwnd);
extern DispatchQueueObject* DispatchGetMainQueue();
extern void DestroyMainQueue(DispatchQueueObject* object);
inline void DispatchAsync(DispatchQueueObject* queue,std::function<void()>* callback)
{
if(queue->threadId){
::PostThreadMessage(queue->threadId,
WMC_DISPATCH_MAIN_QUEUE,(WPARAM)callback,0);
}else{
::PostMessage(queue->m_hwnd,
WMC_DISPATCH_MAIN_QUEUE,(WPARAM)callback,0);
}
DestroyMainQueue(queue);
}
#endif
dispatch_queue.h
#include "stdafx.h"
#include "dispatch_queue.h"
static HWND gMainFrameHwnd = NULL;
void DispatchQueueInit(HWND hwnd)
{
gMainFrameHwnd = hwnd;
}
DispatchQueueObject* DispatchGetMainQueue()
{
DispatchQueueObject* object = (DispatchQueueObject*)malloc(sizeof(DispatchQueueObject));
memset(object,0,sizeof(DispatchQueueObject));
object->m_hwnd = gMainFrameHwnd;
return object;
}
void DestroyMainQueue(DispatchQueueObject* object)
{
free(object);
}
输出
使用 lambda
表达式代替函数
使用成员方法
使用原始的 PostMessage
下载
DispatchAsync使用lambda表达式来简化发送数据