Win32编程中传递和保存this指针的一般方法

对于窗口,使用CreateWindowEx

WINUSERAPI
HWND WINAPI CreateWindowExW(
    DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle,
    int X, int Y, int nWidth, int nHeight,
    HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);

最后一个参数LPVOID lpParam传入this指针,在窗口过程WndProc的WM_CREATE消息中使用lParam获取类型为LPCREATESTRUCT的结构体指针lpcs,然后使用lpcs->lpCreateParams获取传入的this指针,并使用SetWindowLongPtr保存到GWLP_USERDATA窗口字段。

LPCREATESTRUCT lpcs = nullptr;
MyWindow* pThis = nullptr;
switch (message)
{
case WM_CREATE:
    lpcs = (LPCREATESTRUCT)lParam;
    pThis = (MyWindow*)lpcs->lpCreateParams;
    SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
    // ...

对于对话框,使用DialogBoxParam(模态对话框)或CreateDialogParam(非模态对话框)

WINUSERAPI
INT_PTR WINAPI DialogBoxParamW(
    HINSTANCE hInstance, LPCWSTR lpTemplateName, HWND hWndParent,
    DLGPROC lpDialogFunc, LPARAM dwInitParam);
WINUSERAPI
HWND WINAPI CreateDialogParamW(
    HINSTANCE hInstance, LPCWSTR lpTemplateName, HWND hWndParent,
    DLGPROC lpDialogFunc, LPARAM dwInitParam);

最后一个参数LPARAM dwInitParam传入this指针,在对话框过程DlgProc的WM_INITDIALOG消息中使用lParam获取窗口字段,并使用SetWindowLongPtr保存到GWLP_USERDATA窗口字段。

MyWindow* pThis = nullptr;
switch (message)
{
case WM_INITDIALOG:
    pThis = (MyWindow*)lParam;
    SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
    // ...

在其它窗口消息中,使用GetWindowLongPtr获取GWLP_USERDATA中保存的this指针。

MyWindow* pThis = nullptr;
switch (message)
{
    // ...
case WM_CHAR:
    pThis = (MyWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
    // ...

【对GWLP_USERDATA窗口字段的分析】

Windows提供了SetWindowLongPtr、GetWindowLongPtr修改窗口字段,其中GWLP_USERDATA可以放置this指针等数据。不过也有人不推荐放置在这里,担心对窗口超类化或子类化有可能覆盖这个字段。实际这种担心并没有必要。

1、使用GWLP_USERDATA字段存储this指针是一种常见用法,很多软件包括Windows操作系统都在使用。

2、子类化的程序有义务在适当时机保存及恢复GWLP_USERDATA,否则程序肯定跑飞,这一般意味着他们的程序出了bug,而不是我们程序出了bug。

3、超类化的程序可以使用GetClassInfoEx获取WNDCLASSEX,然后调整cbWndExtra扩充一个指针大小的字段,并使用所扩充字段的字节偏移调用SetWindowLongPtr和GetWindowLongPtr,而不是使用GWLP_USERDATA。

【对WM_CREATE之前的几个消息的分析】

根据网上众多资料以及写程序试验,WM_CREATE之前WndProc会收到下面的消息:

1、WM_GETMINMAXINFO——查询窗口大小约束

2、WM_NCCREATE——非客户区创建消息

3、WM_NCCALCSIZE——查询客户区尺寸

4、其它未公开的消息——这些消息应该调用DefWindowProc处理

写程序时一般通过CreateWindow(Ex)最后一个参数传入this指针,这个传入的参数一般要等到WM_NCCREATE或WM_CREATE才可以访问,实际上也是有解决办法的:

1、如果需要在处理第一次WM_NCCALCSIZE时使用this指针,可以在WM_NCCREATE而不是在WM_CREATE中获取传入的this指针并保存。注意WM_NCCREATE应该返回TRUE表示成功FALSE表示失败,而WM_CREATE返回0表示成功-1表示失败。

2、在处理WM_GETMINMAXINFO和WM_NCCALCSIZE时,如果this指针为nullptr,则直接调用DefWindowProc,可以通过这种方法跳过第一次消息。

然后可以在WM_CREATE中先获取传入的this指针并保存,再调用MoveWindow重新应用窗口大小,MoveWindow无论窗口大小的值是否改变,都会重新发送WM_GETMINMAXINFO和WM_NCCALCSIZE这两个查询消息。由于收到WM_CREATE时窗口还未显示在屏幕上,因此视觉效果是一样的。

唯一的不同是第一次WM_NCCALCSIZE参数是FALSE,RECT,而MoveWindow发送的WM_NCCALCSIZE参数是TRUE,NCCALCSIZE_PARAMS,不过实际效果并没有什么不同。

// 让系统再次查询WM_GETMINMAXINFO和WM_NCCALCSIZE
// 第一次查询是在WM_CREATE之前,但是由于需要this指针所以无法处理
RECT wrc;
GetWindowRect(hwnd, &wrc);
MoveWindow(hwnd, wrc.left, wrc.top, wrc.right - wrc.left, wrc.bottom - wrc.top, FALSE);
发布了29 篇原创文章 · 获赞 1 · 访问量 3406

猜你喜欢

转载自blog.csdn.net/defrag257/article/details/103411168