单个 wndproc 如何让每个窗口知道它的序列号?

Posted

技术标签:

【中文标题】单个 wndproc 如何让每个窗口知道它的序列号?【英文标题】:How does a single wndproc let each window know its serial number? 【发布时间】:2022-01-05 15:08:19 【问题描述】:
int Num = 0;
LRESULT CALLBACK TestWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

    RECT rc;
    GetClientRect(hWnd, &rc);
    RECT Winrc;
    GetWindowRect(hWnd, &Winrc);
    SYSTEMTIME time;
    GetLocalTime(&time);
    static const wchar_t* BoxTxt = L"";
    static int MeIs = Num;
    switch (message)
    

    case WM_CREATE:
    
        SetWindowLong(hWnd, GWL_EXSTYLE,
            GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
        SetLayeredWindowAttributes(hWnd, RGB(255, 255, 255), 220, LWA_ALPHA);
        //GhWnd = hWnd;
        break;
    
    case WM_LBUTTONUP:
    
            wchar_t meChar[20] = L"";
            _itow(MeIs, meChar, 10);
            MessageBox(0, meChar, meChar, 0);
    
    case WM_SIZE:
    
        InvalidateRect(hWnd, &rc, 1);
        break;
    
    case WM_NCLBUTTONDBLCLK:
    
        break;
    
    case WM_COMMAND:
    
        int wmId = LOWORD(wParam);
        switch (wmId)
        

        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        
    
    break;
    case WM_CLOSE:
    
        Num -= 1;
        DestroyWindow(hWnd);
    
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    
    return 0;

int CreateTestWindow()

//Call testwndproc. To reduce the length of the problem description, omit these codes
Num+=1;
return 0;

在上面的代码中,当我创建多个窗口并点击它时,应该会弹出“1”、“2”、“3”……但实际上都是弹出“1”。

static int MeIs = 0;
case WM_CREATE:

MeIs = Num;

改成上面的代码,会弹出最后一个窗口的序列号。比如第四个窗口创建时,所有窗口都会弹出“4”

在实际应用中,每个窗口都有自己的设置,并存储在向量中。每个窗口根据自己的序列号找到自己的设置:

struct Data

int x;
int y;
int width;
int height;
const wchar_t* text;

std::vector<data>UserData(32);//Max:32
//then read them from file,But the window must know which window it is:UserData[i].

例如,第一个窗口会将它们的坐标设置为UserData[1].x和UserData[1].y,关闭时也需要保存文件。 有什么想法吗?谢谢!

【问题讨论】:

static int MeIs - 你在程序中只得到其中一个,而不是每个窗口一个。如果您想要每个窗口的数据,您可以在注册窗口类时添加存储空间。 但是如何增加存储空间呢?我希望每个窗口数据是独立的,但是WndProc只有一个。 你应该阅读 Petzold 的书。像这样学习真的不会有成效。 cbWndExtra. hwnd不是已经表示窗口了吗? 【参考方案1】:

有几种方法可以使用 Win32 API 维护每个窗口的数据。

最简单的方法是使用可通过GetWindowLongPtr(...)SetWindowLongPtr(...) 访问的GWL_USERDATA 插槽。初始化此用户数据值的典型方法是使用通过CreateWindow 调用传递给WM_CREATE 消息的创建参数。

代码如下:

#include <windows.h>
#include <string>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

struct SomeData 
    int n;
    std::wstring str;
;

int RegisterWindow(HINSTANCE hInstance, const wchar_t* wnd_class) 
    MSG msg =  0 ;
    WNDCLASS wc =  0 ;
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hbrBackground = (HBRUSH)(WHITE_BRUSH);
    wc.lpszClassName = wnd_class;
    if (!RegisterClass(&wc)) 
        return 1;
    
    return 0;


int CreateWindowWithUserData(HINSTANCE hInstance, const wchar_t* wnd_class, int n, const std::wstring& str) 
    auto* data_ptr = new SomeData n, str ;

    if (!CreateWindow(wnd_class,  L"Window text",  WS_OVERLAPPEDWINDOW | WS_VISIBLE,  0, 0, 640, 480, 0, 0, hInstance, data_ptr))
        return 2;


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

    const auto* wnd_class = L"foobar";
    RegisterWindow(hInstance, wnd_class);

    for (int i = 1; i <= 5; i++) 
        CreateWindowWithUserData(hInstance, wnd_class, i, L"blah");
    
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0) > 0) 
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    
    return 0;


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

    switch (message)
    
    case WM_CREATE: 
            CREATESTRUCT* create_struct = reinterpret_cast<CREATESTRUCT*>(lParam);
            SomeData* user_data = reinterpret_cast<SomeData*>(create_struct->lpCreateParams);
            SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG>(user_data));
         
        return 0;

    case WM_CLOSE:
        PostQuitMessage(0);
        break;

    case WM_PAINT: 
        SomeData* user_data = reinterpret_cast<SomeData*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        RECT r =  20, 20, 300, 35 ;
        auto msg = user_data->str + L" " + std::to_wstring(user_data->n);
        DrawText(hdc, msg.c_str(), -1, &r, DT_SINGLELINE);
        EndPaint(hWnd, &ps);
        
        return 0;

    case WM_DESTROY: 
        SomeData* user_data = reinterpret_cast<SomeData*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
        delete user_data;
        
        return 0;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    
    return 0;


做类似事情的另一种方法是在注册窗口类时使用cbWndExtra 额外字段as discussed in this answer。

【讨论】:

谢谢,但还有一个问题。如果窗口关闭,我如何通知程序重新排序所有窗口?比如我关闭第二个窗口(假设一共有五个),第三个窗口会被排序为“2”,以此类推 我不确定你的意思。它们是独立的窗口。用户可以按照用户想要的任何顺序放置它们。但它们仍会显示创建时使用的正确的每个窗口数据。 对不起,我没有说清楚。表示一共创建了序号为1,2,3,4,5的窗口,但是例如如果序号为3的窗口关闭了,则使用后面的窗口序号补充关闭的窗口,像4变成3,5变成4。有没有办法实现呢?感谢您的辛勤工作。【参考方案2】:

你可以:

确实在Window中存储数据。 SetProp, SetWindowLong+GWL_USERDATA, SetWindowLong+cbWndExtra 将 HWND 映射到您的数据,例如使用 c++ std::map 使用 thunk 来获得关联对象,例如 ATL,请参阅 ATL thunk header 了解可用 API(对于较旧的操作系统,必须手动完成)

【讨论】:

以上是关于单个 wndproc 如何让每个窗口知道它的序列号?的主要内容,如果未能解决你的问题,请参考以下文章

在 wndproc 和 MSG 中处理窗口消息,有啥区别?

WndProc 消息 = 49619 - 它是啥?

没有 wndproc 的窗口

子窗口没有收到 WM_DESTROY?

单击单个JavaScript弹出窗口

如何在 C# 中为多个进程提供单个命令窗口