。。。。。。
引用
评论
最初想用lpCreateParams分发,但是后来发现WM_CREATE之前还有几个消息,不完美。
后来想到这几个消息都是用SendMessage发送的,而SendMessage在同一个线程之内是用直接调用WndProc的方式发送的,也就是说CreateWindow与WndProc被调用之间不会被其它窗口的消息打断,所以直接用【单线程技巧】使用静态变量传递this指针即可。
头文件:
// 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>
主程序:
// 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>