[WTL/ATL]_[C/C++]_[DispatchAsync使用lambda表達式來簡化發送數據到界面線程]

場景

  1. 在進行 Win32 界面編程時,更新界面內容只能通過主線程進行更新. 那麼如果是在工作線程執行完邏輯後如何通過把數據傳遞給界面線程更新數據?

  2. Windows 上開發界面我是使用輕量級 Win32框架, MFC 的精簡版 WTL 進行開發,那麼在 WTL 開發時如何把數據傳遞給界面?

說明

  1. 我在這篇博客裏《Win32實現Cocoa的dispatch_async到主線程的異步消息處理》有寫如何利用 DispatchAsync 來執行主線程任務.

  2. 在這裏例子任務裏,我們定義了一個全局函數, 在主線程執行函數 PrintStr。實際上我們可以利用 C++11lambda 表達式來實現函數功能,避免定義一個只用一次的函數. 可以說用 lambda 表達式我們可以使用類似同步的方式進行編碼。

是不是很優雅.^_^

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;
}
  1. 我們也可以使用 std::bind 來綁定成員函數,或者綁定預定義的函數參數,非常方便。

以下例子使用在 Win32實現Cocoa的dispatch_async到主線程的異步消息處理 裏使用的 DispatchAsync 工具函數來在主線程執行彈出對話框。

例子

  1. 使用 vs2017WTL 寫的項目裏子
  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表達式來簡化發送數據

參考

Win32實現Cocoa的dispatch_async到主線程的異步消息處理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章