一种比较完美的Win32消息-C++对象分发
acmilan2016/07/14软件综合 IP:四川

最初想用lpCreateParams分发,但是后来发现WM_CREATE之前还有几个消息,不完美。

后来想到这几个消息都是用SendMessage发送的,而SendMessage在同一个线程之内是用直接调用WndProc的方式发送的,也就是说CreateWindow与WndProc被调用之间不会被其它窗口的消息打断,所以直接用【单线程技巧】使用静态变量传递this指针即可。

头文件:

<code class="language-cpp">// MessageWindow.h

#pragma once

#include <cstdint>
#include <windows.h>

namespace Win32
{
	using std::intptr_t;
	using std::uintptr_t;

	struct Win32Error
	{
		unsigned errcode;
		Win32Error(unsigned errcode = GetLastError()) {
			this->errcode = errcode;
		};
	};

	struct UnexpectedError
	{
	};

	///////////////////////////////////////////////////////////////////
	// 脏活开始

	class HwndMessageSink
	{
	public:
		// 窗口句柄,由于常常需要在外部访问,因此定义为public
		HWND m_hwnd;

	public:
		// 可以转换为HWND
		operator HWND()
		{
			return m_hwnd;
		}
		// 构造函数
		HwndMessageSink() : m_hwnd(nullptr)
		{
		}
		// 析构前我们需要尽可能地销毁窗口,以避免指针悬空
		virtual ~HwndMessageSink()
		{
			if (m_hwnd)
				DestroyWindow(m_hwnd);
		}

	protected:
		// 消息分发目的函数
		virtual intptr_t MessageHandler(unsigned msg, uintptr_t wParam, intptr_t lParam) = 0; // TODO

	public:
		// 获取静态WNDPROC/DLGPROC
		static void *GetMessageDispatcher()
		{
			return MsgDispatcher;
		}
		// 获取所需cbWndExtra大小
		static int GetRequiredWindowExtra()
		{
			return DLGWINDOWEXTRA;
		}
		
	protected:
		// 设置静态变量
		static void SetMessageDispatcherThisPointer(HwndMessageSink *obj)
		{
			RefStaticThisPointer() = obj;
		}

	private:
		// 静态变量定义(类中的静态变量其实是全局变量,因此这里只能定义在函数中)
		static HwndMessageSink *&RefStaticThisPointer()
		{
			__declspec(thread) static HwndMessageSink *s_pobj = nullptr;
			return s_pobj;
		}

		// 消息对象分发的核心函数,本程序中最脏的活
		static intptr_t __stdcall MsgDispatcher(
			HWND hwnd, unsigned msg,
			uintptr_t wParam, intptr_t lParam)
		{
			// 本函数中用来当做this指针的变量,先试图从DWLP_USER取出
			HwndMessageSink *pthis = (HwndMessageSink *)GetWindowLongPtr(hwnd, DWLP_USER);
			if (RefStaticThisPointer() != nullptr && pthis == nullptr)
			{
				// 如果没有设置DWLP_USER但设置了静态变量
				// 试图从静态变量取出
				pthis = RefStaticThisPointer();
				// 然后将静态变量置零
				RefStaticThisPointer() = nullptr;
				// 最后将pthis存入DWLP_USER
				SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)pthis);
			}
			if (pthis)
			{
				// 如果这时候解决了pthis
				// 更新m_hwnd,保证分发之后可以立即使用m_hwnd
				pthis->m_hwnd = hwnd;
				// 调用MessageHandler
				intptr_t retval = pthis->MessageHandler(msg, wParam, lParam);
				if (msg == WM_DESTROY)
				{
					// 如果是WM_DESTROY,则表示窗口已经销毁
					// 置空m_hwnd以防止悬空的句柄和重复销毁
					pthis->m_hwnd = nullptr;
				}
				// 返回应该返回的值
				return retval;
			}
			else
			{
				// 如果这时候还没有获取到有效的pthis,说明没有执行SetMessageDispatcherThisPointer
				throw UnexpectedError();
			}
		}

	private:
		// 对象不允许被复制
		HwndMessageSink(HwndMessageSink const&);
		HwndMessageSink &operator=(HwndMessageSink const&);
	};
	 
	// 脏活结束
	//////////////////////////////////////////////

	class Win32Window : public HwndMessageSink
	{
	public:
		static ATOM Register(unsigned dwClassStyle, 
			HINSTANCE hInstance, HICON hIcon, HCURSOR hCursor, HBRUSH hbrBackground,
			const wchar_t *lpszMenuName, const wchar_t *lpszClassName, HICON hIconSm,
			int cbClsExtra = 0, int cbWndExtraAddition = 0)
		{
			WNDCLASSEX wcex = {
				sizeof wcex, dwClassStyle, (WNDPROC)GetMessageDispatcher(),
				cbClsExtra, GetRequiredWindowExtra() + cbWndExtraAddition,
				hInstance, hIcon, hCursor, hbrBackground, lpszMenuName, lpszClassName, hIconSm
			};
			return RegisterClassEx(&wcex);
		}

		bool Create(unsigned dwExStyle, const wchar_t *lpszClassName,
			const wchar_t *lpszWindowName, unsigned dwWindowStyle,
			int x, int y, int cx, int cy,
			HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, void *lpParam)
		{
			SetMessageDispatcherThisPointer(this);
			m_hwnd = CreateWindowEx(dwExStyle, lpszClassName, lpszWindowName, dwWindowStyle,
				x, y, cx, cy, hWndParent, hMenu, hInstance, lpParam);
			return m_hwnd != nullptr;
		}

	protected:
		virtual intptr_t MessageHandler(unsigned msg, uintptr_t wParam, intptr_t lParam)
		{
			return DefWindowProc(m_hwnd, msg, wParam, lParam);
		}
	};

	class Win32Dialog : public HwndMessageSink
	{
	protected:
		bool m_ismodal;
		
	public:
		Win32Dialog() : m_ismodal(false) {}

		intptr_t DoModal(HINSTANCE hInstance, const wchar_t *lpTemplateName, HWND hWndParent, intptr_t dwInitParam)
		{
			m_ismodal = true;
			SetMessageDispatcherThisPointer(this);
			return DialogBoxParam(hInstance, lpTemplateName, hWndParent, (DLGPROC)GetMessageDispatcher(), dwInitParam);
		}
		intptr_t DoModal(HINSTANCE hInstance, unsigned short idTemplateName, HWND hWndParent, intptr_t dwInitParam)
		{
			m_ismodal = true;
			SetMessageDispatcherThisPointer(this);
			return DialogBoxParam(hInstance, MAKEINTRESOURCE(idTemplateName), hWndParent, (DLGPROC)GetMessageDispatcher(), dwInitParam);
		}

		bool Create(HINSTANCE hInstance, const wchar_t *lpTemplateName, HWND hWndParent, intptr_t dwInitParam)
		{
			m_ismodal = false;
			SetMessageDispatcherThisPointer(this);
			return nullptr == CreateDialogParam(hInstance, lpTemplateName, hWndParent, (DLGPROC)GetMessageDispatcher(), dwInitParam);
		}
		bool Create(HINSTANCE hInstance, unsigned short idTemplateName, HWND hWndParent, intptr_t dwInitParam)
		{
			m_ismodal = false;
			SetMessageDispatcherThisPointer(this);
			return nullptr == CreateDialogParam(hInstance, MAKEINTRESOURCE(idTemplateName), hWndParent, (DLGPROC)GetMessageDispatcher(), dwInitParam);
		}

	protected:
		virtual intptr_t MessageHandler(unsigned msg, uintptr_t wParam, intptr_t lParam)
		{
			if (msg == WM_CLOSE)
			{
				if (m_ismodal)
					EndDialog(m_hwnd, IDCANCEL);
				else
					DestroyWindow(m_hwnd);
				return true;
			}
			return false;
		}
	};
}
</windows.h></cstdint></code>

主程序:

<code class="language-cpp">// main.cpp

#include <windows.h>
#include "MessageWindow.h"
#include "resource.h"

HINSTANCE hInst;

class MyWindow : public Win32::Win32Window
{
	intptr_t MessageHandler(unsigned msg, uintptr_t wParam, intptr_t lParam)
	{
		if (msg == WM_RBUTTONDOWN)
		{
			Win32::Win32Dialog dlg;
			dlg.DoModal(hInst, IDD_DIALOG1, m_hwnd, 0);
		}
		if (msg == WM_DESTROY)
		{
			PostQuitMessage(0);
			return 0;
		}
		return Win32::Win32Window::MessageHandler(msg, wParam, lParam);
	}
};

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE, char *szCmdLine, int nShowCmd)
{
	hInst = hInstance;

	MyWindow wnd;
	wnd.Register(CS_VREDRAW|CS_HREDRAW, hInstance, LoadIcon(nullptr, IDI_APPLICATION),
		LoadCursor(nullptr, IDC_ARROW), (HBRUSH)GetStockObject(WHITE_BRUSH), nullptr,
		L"MainWndClass", LoadIcon(nullptr, IDI_APPLICATION));
	wnd.Create(0, L"MainWndClass", L"Main Window", WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		nullptr, nullptr, hInstance, nullptr);

	ShowWindow(wnd, nShowCmd);
	UpdateWindow(wnd);

	Win32::Win32Dialog dlg;
	dlg.Create(hInst, IDD_DIALOG1, wnd, 0);
	ShowWindow(dlg, SW_SHOW);

	MSG msg = {};
	while (GetMessage(&msg, nullptr, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return (int)msg.wParam;
}
</windows.h></code>

[修改于 8年0个月前 - 2016/12/19 03:01:11]

来自:计算机科学 / 软件综合
1
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
acmilan 作者
8年5个月前 修改于 8年0个月前 IP:四川
822808

。。。。。。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年10个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}