英文:
Why does the window handle not get stored properly?
问题
首先,您获得了一个MCWE(可能是一种自定义窗口编辑器)。您可能比我更了解如何使用它,但我建议从头开始构建一个C++项目,然后将其添加进去。它使用Unicode字符集,请确保将子系统设置为“windows”。
在WM_CREATE
消息的父窗口中,通过迭代Objects
来创建窗口。
还注册了一个自定义窗口类,以便能够在该对象上进行自定义事件处理。在这个“子”过程的WM_LBUTTONDOWN
中,您希望通过向其发送消息来修改另一个子窗口的文本。
输出是“Is not..”是因为窗口句柄没有正确传递,但我做错了什么?
我尝试将变量作为指针传递,但没有任何区别。
您遇到的问题可能与窗口句柄的作用域和生存期有关。在WM_CREATE
消息处理程序中创建的窗口句柄只在该函数内部有效。要在其他消息处理程序中使用这些窗口句柄,您需要确保它们的生存期足够长。
一种解决方法是将窗口句柄存储在全局或静态变量中,以便在整个应用程序中访问它们。例如,在UiObjects
类中,您可以将 Canvas
和 Output
对象的窗口句柄存储为静态变量:
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];
^
另外,请注意,Canvas
、Output
等都是与存储在Objects
向量中的Object
不同的独立Object
。因此,你的WM_CREATE
循环也没有正确更新Output.hObjectWnd
等。
更新:事实上,你正在填充Canvas
、Output
等数据,然后将那些对象的副本推送到Objects
向量中。因此,即使你的WM_CREATE
代码正确地将新的HWND
分配给了Objects
元素,Canvas
、Output
等也不会正确获取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<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";
// 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;
if (!RegisterClass(&applicationWndClass))
{
return 0;
}
// Create the window.
HWND 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);
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);
// The following loop iterates through the container of user
// interface objects.
for (int i = 0; i < MyObjects.Objects.size(); ++i) {
UiObjects::Object* pObject = MyObjects.Objects[i];
pObject->hObjectWnd = CreateWindow(
pObject->lpClassName, // Window class
pObject->lpWindowName, // Window text
pObject->dwStyle, // Styles
pObject->x, // Horizontal position
pObject->y, // Vertical position
pObject->Width, // Width
pObject->nHeigth, // Height
hWnd, // Parent window
pObject->hMenu, // No menu.
hInstance,
NULL // Pointer not needed.
);
}
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 = BeginPaint(hWnd, &paintStruct);
HBRUSH 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(hOutputWnd))
{
SendMessage(hOutputWnd, WM_SETTEXT, NULL, (LPARAM)L"Test.");
}
else {
System::Diagnostics::Debug::WriteLine("Is not..");
}
break;
}
}
return DefWindowProc(hCanvasWnd, uMsg, wParam, lParam);
}
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论