窗口句柄为何未正确存储?

huangapple go评论73阅读模式
英文:

Why does the window handle not get stored properly?

问题

首先,您获得了一个MCWE(可能是一种自定义窗口编辑器)。您可能比我更了解如何使用它,但我建议从头开始构建一个C++项目,然后将其添加进去。它使用Unicode字符集,请确保将子系统设置为“windows”。

WM_CREATE消息的父窗口中,通过迭代Objects来创建窗口。

还注册了一个自定义窗口类,以便能够在该对象上进行自定义事件处理。在这个“子”过程的WM_LBUTTONDOWN中,您希望通过向其发送消息来修改另一个子窗口的文本。

输出是“Is not..”是因为窗口句柄没有正确传递,但我做错了什么?

我尝试将变量作为指针传递,但没有任何区别。

您遇到的问题可能与窗口句柄的作用域和生存期有关。在WM_CREATE消息处理程序中创建的窗口句柄只在该函数内部有效。要在其他消息处理程序中使用这些窗口句柄,您需要确保它们的生存期足够长。

一种解决方法是将窗口句柄存储在全局或静态变量中,以便在整个应用程序中访问它们。例如,在UiObjects 类中,您可以将 CanvasOutput 对象的窗口句柄存储为静态变量:

static HWND hObjectCanvasWnd;
static HWND hObjectOutputWnd;

然后,在WM_CREATE消息处理程序中将它们设置为相应窗口的句柄:

hObjectCanvasWnd = CreateWindow(
    Object.lpClassName, // Window class
    Object.lpWindowName,    // Window text
    Object.dwStyle, // Styles 
    Object.x, // Horizontal position 
    Object.y, // Vertical position 
    Object.nWidth, // Width
    Object.nHeigth, // Height
    hWnd, // Parent window
    Object.hMenu, // No menu.
    hInstance,
    NULL // Pointer not needed.
);

MyObjects.Objects[i].hObjectWnd = hObjectCanvasWnd;

if (lstrcmp(Object.lpWindowName, L"Output") == 0)
{
    hObjectOutputWnd = hObjectCanvasWnd;
}

现在,您可以在其他消息处理程序中使用 hObjectOutputWnd 来操作 Output 窗口。确保将相应的条件检查添加到 WM_LBUTTONDOWN 消息处理程序中,以确定您是否操作了正确的窗口。

希望这可以帮助您解决问题。如果还有其他问题,请随时提出。

英文:

First you get a MCWE. You know probably better than me how to use it but I would recommend to build a C++ project from scratch and add it then. It uses Unicode character set, make sure to use windows as the SubSystem.

#include <vector>
#include <windows.h>
#include <windowsx.h>

#using <System.dll>

const int CANVAS_HOR_OFFSET = 10;
const int CANVAS_VER_OFFSET = 10;
const int CANVAS_WIDTH = 640;
const int CANVAS_HEIGTH = 480;

const int OUTPUT_VER_OFFSET = 10;
const int OUTPUT_HEIGTH = 120;

static class UiObjects
{
public:
    enum ObjectClass { Custom };

    struct Object {
        LPWSTR lpClassName;
        LPWSTR lpWindowName;
        DWORD dwStyle;
        LONG x;
        LONG y;
        LONG nWidth;
        LONG nHeigth;
        HMENU hMenu;
        HWND hObjectWnd;
        ObjectClass objectType;
        COLORREF color;
    };

    Object Canvas, Output;

    std::vector<Object> Objects;

    UiObjects();
} MyObjects;

UiObjects::UiObjects() {
    Canvas = {
        L"myVirtualSandbox Canvas Class",
        L"Canvas",
        WS_VISIBLE | WS_CHILD | WS_BORDER,
        CANVAS_HOR_OFFSET,
        CANVAS_VER_OFFSET,
        CANVAS_WIDTH,
        CANVAS_HEIGTH,
        NULL,
        NULL,
        ObjectClass::Custom
    };
    Objects.push_back(Canvas);

    Output = {
        L"EDIT",
        L"Output",
        WS_VISIBLE | WS_CHILD | WS_BORDER,
        CANVAS_HOR_OFFSET,
        CANVAS_VER_OFFSET + CANVAS_HEIGTH + OUTPUT_VER_OFFSET,
        CANVAS_WIDTH,
        OUTPUT_HEIGTH,
        NULL,
        NULL,
        ObjectClass::Custom
    };
    Objects.push_back(Output);
}

LRESULT CALLBACK wndProc(
    HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);

LRESULT CALLBACK canvasWndProc(
    HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);

int __stdcall wWinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR lpCmdLine,
    _In_ int nShowCmd
) {
    const wchar_t CLASS_NAME[] = L"myVirtualSandbox Window Class";

    HWND hApplicationWnd;
    MSG msg = { };
    // A window class defines a set of behaviors that several windows 
    // might have in common. Every window must be associated with a 
    // window class. To register a window class, fill in a "WNDCLASS" 
    // structure and call "RegisterClass" function afterwards.
    WNDCLASS applicationWndClass = { };

    applicationWndClass.lpfnWndProc = wndProc;
    applicationWndClass.hInstance = hInstance;
    applicationWndClass.lpszClassName = CLASS_NAME;

    RegisterClass(&applicationWndClass);

    // Create the window.
    hApplicationWnd = CreateWindow(
        CLASS_NAME,                     // Window class
        L"Learn to Program Windows",    // Window text
        WS_OVERLAPPEDWINDOW,            // Window style
        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL,       // Parent window    
        NULL,       // Menu
        hInstance,  // Instance handle
        NULL        // Additional application data
    );

    if (hApplicationWnd == NULL)
    {
        return 0;
    }

    ShowWindow(hApplicationWnd, nShowCmd);

    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

LRESULT CALLBACK wndProc(
    HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
) {
    switch (uMsg)
    {
    case WM_CREATE:
    {
        HINSTANCE hInstance;
        WNDCLASS canvasWndClass;

        hInstance = (HINSTANCE)GetModuleHandle(NULL);

        canvasWndClass = { };

        canvasWndClass.lpfnWndProc = canvasWndProc;
        canvasWndClass.hInstance = hInstance;
        canvasWndClass.lpszClassName = L"myVirtualSandbox Canvas Class";

        RegisterClass(&canvasWndClass);

        // The following loop iterates through the container of user 
        // interface objects.
        for (int i = 0; i < MyObjects.Objects.size(); i++) {
            UiObjects::Object& Object = MyObjects.Objects[i];

            HWND hObjectWnd;

            hObjectWnd = CreateWindow(
                Object.lpClassName, // Window class
                Object.lpWindowName,    // Window text
                Object.dwStyle, // Styles 
                Object.x, // Horizontal position 
                Object.y, // Vertical position 
                Object.nWidth, // Width
                Object.nHeigth, // Height
                hWnd, // Parent window
                Object.hMenu, // No menu.
                hInstance,
                NULL // Pointer not needed.
            );

            MyObjects.Objects[i].hObjectWnd = hObjectWnd;
        }

        break;
    }

    case WM_DESTROY:
    {
        PostQuitMessage(0);
        break;
    }

    // To paint the window the "WM_PAINT" message has to be 
    // received by the window. It is sent by either the program 
    // itself or the operating system.
    case WM_PAINT:
    {
        // The "rcPaint" member of the "PAINTSTRCUT" structure 
        // returns a "RECT" structure that specifies the upper 
        // left and lower right corners of the rectangle in which 
        // the painting is requested.
        PAINTSTRUCT paintStruct;
        HDC hDeviceContext;

        HBRUSH hBrush;

        hDeviceContext = BeginPaint(hWnd, &paintStruct);
        hBrush = CreateSolidBrush(RGB(66,66,66));

        // Paint application background.
        FillRect(
            hDeviceContext,
            &paintStruct.rcPaint,
            hBrush
        );

        DeleteObject(hBrush);

        EndPaint(hWnd, &paintStruct);

        break;
    }
    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK canvasWndProc(
    HWND hCanvasWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
) {
    switch (uMsg)
    {

    case WM_LBUTTONDOWN:
    {
        POINT point = { };

        point.x = GET_X_LPARAM(lParam);
        point.y = GET_Y_LPARAM(lParam);

        System::Diagnostics::Debug::WriteLine("Clicked in Canvas.");

        HWND hOutputWnd = MyObjects.Output.hObjectWnd;

        // Check if hObjectWnd is a valid handle
        if (IsWindow(MyObjects.Output.hObjectWnd))
        {
            SendMessage(MyObjects.Output.hObjectWnd, WM_SETTEXT, NULL, (LPARAM)L"Test.");
        }
        else {
            System::Diagnostics::Debug::WriteLine("Is not..");
        }
        break;
    }
    }

    return DefWindowProc(hCanvasWnd, uMsg, wParam, lParam);
}

I create windows by iterating through Objects in the WM_CREATE message of the parent window.

I also register a custom window class to be able to have custom event handling on that object. In case of WM_LBUTTONDOWN of this "sub"-procedure I want to modify another child windows text by sending a message to it.

The Output is "Is not.." since the window handle is not passed properly but what am I doing wrong?

I tried to pass the variable as a pointer but that didn´t make a difference at all.

答案1

得分: 0

在你的WM_CREATE循环中,你将新的HWND分配给了来自向量的Object副本。你没有更新向量中的原始Object

将这个:

UiObjects::Object Object = MyObjects.Objects[i];

改成这样:

UiObjects::Object& Object = MyObjects.Objects[i];
                 ^

另外,请注意,CanvasOutput等都是与存储在Objects向量中的Object不同的独立Object。因此,你的WM_CREATE循环也没有正确更新Output.hObjectWnd等。


更新:事实上,你正在填充CanvasOutput等数据,然后将那些对象的副本推送到Objects向量中。因此,即使你的WM_CREATE代码正确地将新的HWND分配给了Objects元素,CanvasOutput等也不会正确获取HWND

为了实现你尝试的功能,你需要将Objects向量更改为保存Object*指针而不是Object实例,例如:

#include <vector>
#include <windows.h>
#include <windowsx.h>

#using <System.dll>

const int CANVAS_HOR_OFFSET = 10;
const int CANVAS_VER_OFFSET = 10;
const int CANVAS_WIDTH = 640;
const int CANVAS_HEIGTH = 480;

const int OUTPUT_VER_OFFSET = 10;
const int OUTPUT_HEIGTH = 120;

static class UiObjects
{
public:
    enum ObjectClass { Custom };

    struct Object {
        LPWSTR lpClassName;
        LPWSTR lpWindowName;
        DWORD dwStyle;
        LONG x;
        LONG y;
        LONG nWidth;
        LONG nHeigth;
        HMENU hMenu;
        HWND hObjectWnd;
        ObjectClass objectType;
        COLORREF color;
    };

    Object Canvas, Output;

    std::vector<Object*> Objects;

    UiObjects();
} MyObjects;

UiObjects::UiObjects() {
    Canvas = {
        L"myVirtualSandbox Canvas Class",
        L"Canvas",
        WS_VISIBLE | WS_CHILD | WS_BORDER,
        CANVAS_HOR_OFFSET,
        CANVAS_VER_OFFSET,
        CANVAS_WIDTH,
        CANVAS_HEIGTH,
        NULL,
        NULL,
        ObjectClass::Custom
    };
    Objects.push_back(&Canvas);

    Output = {
        L"EDIT",
        L"Output",
        WS_VISIBLE | WS_CHILD | WS_BORDER,
        CANVAS_HOR_OFFSET,
        CANVAS_VER_OFFSET + CANVAS_HEIGTH + OUTPUT_VER_OFFSET,
        CANVAS_WIDTH,
        OUTPUT_HEIGTH,
        NULL,
        NULL,
        ObjectClass::Custom
    };
    Objects.push_back(&Output);
}

LRESULT CALLBACK wndProc(
    HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);

LRESULT CALLBACK canvasWndProc(
    HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);

int __stdcall wWinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR lpCmdLine,
    _In_ int nShowCmd
) {
    const wchar_t CLASS_NAME[] = L"myVirtualSandbox Window Class";

    // 窗口类定义了几个窗口可能具有的行为。每个窗口必须与窗口类相关联。
    // 要注册窗口类,请填写“WNDCLASS”结构,然后调用“RegisterClass”函数。
    WNDCLASS applicationWndClass = { };

    applicationWndClass.lpfnWndProc = wndProc;
    applicationWndClass.hInstance = hInstance;
    applicationWndClass.lpszClassName = CLASS_NAME;

    if (!RegisterClass(&applicationWndClass))
    {
        return 0;
    }

    // 创建窗口。
    HWND hApplicationWnd = CreateWindow(
        CLASS_NAME,                     // 窗口类
        L"Learn to Program Windows",    // 窗口文本
        WS_OVERLAPPEDWINDOW,            // 窗口样式
        // 大小和位置
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL,       // 父窗口
        NULL,       // 菜单
        hInstance,  // 实例句柄
        NULL        // 附加应用程序数据
    );

    if (hApplicationWnd == NULL)
    {
        return 0;
    }

    ShowWindow(hApplicationWnd, nShowCmd);

    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

LRESULT CALLBACK wndProc(
    HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
) {
    switch (uMsg)
    {
    case WM_CREATE:
    {
        HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);

        WNDCLASS canvasWndClass = { };
        canvasWndClass.lpfnWndProc = canvasWndProc;
        canvasWndClass.hInstance = hInstance;
        canvasWndClass.lpszClassName = L"myVirtualSandbox Canvas Class";

        RegisterClass(&canvasWndClass);

        // 以下循环遍历用户界面对象的容器。
        for (int i = 0; i < MyObjects.Objects.size(); ++i) {
            UiObjects::Object* pObject = MyObjects.Objects[i];

            pObject->hObjectWnd = CreateWindow(
                pObject->lpClassName, // 窗口类
                pObject->lpWindowName,    // 窗口文本
                pObject->dwStyle, // 样式
                pObject->x, // 水平位置
                pObject->y, // 垂直位置
                pObject->Width, // 宽度
                pObject->nHeigth, // 高度
                hWnd, // 父窗口
                pObject->hMenu, // 无菜单
                hInstance,
                NULL // 指针不需要。
            );
        }

        break;
    }

    case WM_DESTROY:
    {
        PostQuitMessage(0);
        break;
    }

    // 要绘制窗口,必须通过窗口接收“WM_PAINT”消息。它由程序本身或操作系统发送。
    case WM_PAINT:
    {
        // “PAINTSTRUCT”的“rcPaint”成员返回一个指定绘制请求的矩形的左上角和右下角。
        PAINTSTRUCT paintStruct = { };

        HDC hDeviceContext = BeginPaint(hWnd, &

<details>
<summary>英文:</summary>

Inside your `WM_CREATE` loop, you are assigning the new `HWND` to a *copy* of the `Object` from the vector. You are not updating the original `Object` in the vector.

Change this:

UiObjects::Object Object = MyObjects.Objects[i];


To this:

UiObjects::Object& Object = MyObjects.Objects[i];
^


Also, note that `Canvas`, `Output`, etc are separate `Object`s than the ones stored in the `Objects` vector. So, `Output.hObjectWnd` etc are likewise not being updated by your `WM_CREATE` loop, either. 
---
**UPDATE**: Indeed, you are filling `Canvas`, `Output`, etc with data, and then pushing *copies* of those objects into your `Objects` vector. So, even if your `WM_CREATE` code were assigning the new `HWND`s to the `Objects` elements correctly, `Canvas`, `Output`, etc would still not get the `HWND`s correctly.
To do what you are attempting, you need to change your `Objects` vector to hold `Object*` pointers instead of `Object` instances, eg:

#include <vector>
#include <windows.h>
#include <windowsx.h>

#using <System.dll>

const int CANVAS_HOR_OFFSET = 10;
const int CANVAS_VER_OFFSET = 10;
const int CANVAS_WIDTH = 640;
const int CANVAS_HEIGTH = 480;

const int OUTPUT_VER_OFFSET = 10;
const int OUTPUT_HEIGTH = 120;

static class UiObjects
{
public:
enum ObjectClass { Custom };

struct Object {
LPWSTR lpClassName;
LPWSTR lpWindowName;
DWORD dwStyle;
LONG x;
LONG y;
LONG nWidth;
LONG nHeigth;
HMENU hMenu;
HWND hObjectWnd;
ObjectClass objectType;
COLORREF color;
};
Object Canvas, Output;
std::vector&lt;Object*&gt; Objects;
UiObjects();

} MyObjects;

UiObjects::UiObjects() {
Canvas = {
L"myVirtualSandbox Canvas Class",
L"Canvas",
WS_VISIBLE | WS_CHILD | WS_BORDER,
CANVAS_HOR_OFFSET,
CANVAS_VER_OFFSET,
CANVAS_WIDTH,
CANVAS_HEIGTH,
NULL,
NULL,
ObjectClass::Custom
};
Objects.push_back(&Canvas);

Output = {
L&quot;EDIT&quot;,
L&quot;Output&quot;,
WS_VISIBLE | WS_CHILD | WS_BORDER,
CANVAS_HOR_OFFSET,
CANVAS_VER_OFFSET + CANVAS_HEIGTH + OUTPUT_VER_OFFSET,
CANVAS_WIDTH,
OUTPUT_HEIGTH,
NULL,
NULL,
ObjectClass::Custom
};
Objects.push_back(&amp;Output);

}

LRESULT CALLBACK wndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);

LRESULT CALLBACK canvasWndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);

int __stdcall wWinMain(
In HINSTANCE hInstance,
In_opt HINSTANCE hPrevInstance,
In LPWSTR lpCmdLine,
In int nShowCmd
) {
const wchar_t CLASS_NAME[] = L"myVirtualSandbox Window Class";

// A window class defines a set of behaviors that several windows 
// might have in common. Every window must be associated with a 
// window class. To register a window class, fill in a &quot;WNDCLASS&quot; 
// structure and call &quot;RegisterClass&quot; function afterwards.
WNDCLASS applicationWndClass = { };
applicationWndClass.lpfnWndProc = wndProc;
applicationWndClass.hInstance = hInstance;
applicationWndClass.lpszClassName = CLASS_NAME;
if (!RegisterClass(&amp;applicationWndClass))
{
return 0;
}
// Create the window.
HWND hApplicationWnd = CreateWindow(
CLASS_NAME,                     // Window class
L&quot;Learn to Program Windows&quot;,    // Window text
WS_OVERLAPPEDWINDOW,            // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL,       // Parent window    
NULL,       // Menu
hInstance,  // Instance handle
NULL        // Additional application data
);
if (hApplicationWnd == NULL)
{
return 0;
}
ShowWindow(hApplicationWnd, nShowCmd);
MSG msg = { };
while (GetMessage(&amp;msg, NULL, 0, 0) &gt; 0)
{
TranslateMessage(&amp;msg);
DispatchMessage(&amp;msg);
}
return 0;

}

LRESULT CALLBACK wndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
) {
switch (uMsg)
{
case WM_CREATE:
{
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);

    WNDCLASS canvasWndClass = { };
canvasWndClass.lpfnWndProc = canvasWndProc;
canvasWndClass.hInstance = hInstance;
canvasWndClass.lpszClassName = L&quot;myVirtualSandbox Canvas Class&quot;;
RegisterClass(&amp;canvasWndClass);
// The following loop iterates through the container of user 
// interface objects.
for (int i = 0; i &lt; MyObjects.Objects.size(); ++i) {
UiObjects::Object* pObject = MyObjects.Objects[i];
pObject-&gt;hObjectWnd = CreateWindow(
pObject-&gt;lpClassName, // Window class
pObject-&gt;lpWindowName,    // Window text
pObject-&gt;dwStyle, // Styles 
pObject-&gt;x, // Horizontal position 
pObject-&gt;y, // Vertical position 
pObject-&gt;Width, // Width
pObject-&gt;nHeigth, // Height
hWnd, // Parent window
pObject-&gt;hMenu, // No menu.
hInstance,
NULL // Pointer not needed.
);
}
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
// To paint the window the &quot;WM_PAINT&quot; message has to be 
// received by the window. It is sent by either the program 
// itself or the operating system.
case WM_PAINT:
{
// The &quot;rcPaint&quot; member of the &quot;PAINTSTRCUT&quot; structure 
// returns a &quot;RECT&quot; structure that specifies the upper 
// left and lower right corners of the rectangle in which 
// the painting is requested.
PAINTSTRUCT paintStruct = { };
HDC hDeviceContext = BeginPaint(hWnd, &amp;paintStruct);
HBRUSH hBrush = CreateSolidBrush(RGB(66,66,66));
// Paint application background.
FillRect(
hDeviceContext,
&amp;paintStruct.rcPaint,
hBrush
);
DeleteObject(hBrush);
EndPaint(hWnd, &amp;paintStruct);
break;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);

}

LRESULT CALLBACK canvasWndProc(
HWND hCanvasWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
) {
switch (uMsg)
{

case WM_LBUTTONDOWN:
{
POINT point = { };
point.x = GET_X_LPARAM(lParam);
point.y = GET_Y_LPARAM(lParam);
System::Diagnostics::Debug::WriteLine(&quot;Clicked in Canvas.&quot;);
HWND hOutputWnd = MyObjects.Output.hObjectWnd;
// Check if hObjectWnd is a valid handle
if (IsWindow(hOutputWnd))
{
SendMessage(hOutputWnd, WM_SETTEXT, NULL, (LPARAM)L&quot;Test.&quot;);
}
else {
System::Diagnostics::Debug::WriteLine(&quot;Is not..&quot;);
}
break;
}
}
return DefWindowProc(hCanvasWnd, uMsg, wParam, lParam);

}


</details>

huangapple
  • 本文由 发表于 2023年7月13日 21:40:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76680052.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定