Window 使用 WINAPI 创建的不是绘图对象。有什么问题?
Window created with WINAPI is not drawing objects. What's the problem?
我有我的 window 文件 (Window.h
):
LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
class Window
{
private:
HWND hWnd;
HINSTANCE hInstance;
bool running = true;
const char* ID = "WINAPI_JVM64";
public:
Window()
{
init();
}
virtual void draw(Gdiplus::Graphics*) = 0;
void init()
{
hInstance = (HINSTANCE)GetModuleHandle(NULL);
WNDCLASS wc;
wc = {};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MessageHandler;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_HAND);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = ID;
assert(RegisterClass(&wc));
hWnd = CreateWindow(ID, "Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
200, 200, 400, 400, NULL, NULL, hInstance, NULL);
ShowCursor(true);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
}
void run()
{
MSG msg;
PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE);
while(running)
{
if(PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
running = false;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Here, the draw function is called.
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics* g = Gdiplus::Graphics::FromHDC(hdc);
draw(g);
EndPaint(hWnd, &ps);
}
}
UnregisterClass(ID, hInstance);
}
};
和主文件(main.cpp
):
#include "Window.h"
LRESULT CALLBACK MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
class AppWindow : public Window
{
public:
void draw(Gdiplus::Graphics* g) override
{
Gdiplus::SolidBrush brown_brush(Gdiplus::Color(255, 128, 57, 0));
g->FillRectangle(&brown_brush, 0, 0, 200, 200);
}
};
int main()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
AppWindow w;
w.run();
Gdiplus::GdiplusShutdown(gdiplusToken);
return 0;
}
我有一个问题就是它不会绘制!
它处理每条消息,一切都很好,但它不绘制。甚至发送了 WM_PAINT
类型的消息,但没有任何反应。
你能找出问题所在吗?
我只想要一个 window class,它有一个可覆盖的 draw()
函数和一个处理所有事件的 run()
函数,例如 WM_LBUTTONDOWN
。一切正常,屏幕只是空白。
另外,我无法关闭window,当按下右上角的X
按钮时,window只是停留;只有在调整大小并快速按下 X
后,它才会关闭。
如您所见,我有一些非常奇怪的行为,我不知道问题出在哪里。
你的绘图逻辑放错地方了。处理 WM_PAINT
消息时,它需要位于 MessageHandler
内。如果需要绘制 window 并且没有其他消息待处理,PeekMessage()
将生成一条 WM_PAINT
消息。您不能从 WM_PAINT
处理程序外部使用 window。
此外,您为 init()
中的 wc.hbrBackground
分配了错误的值。如果您使用像 COLOR_WINDOW
这样的颜色常量,则需要向其添加 1
。 WNDCLASS
documentation.
中也说明了这一点
此外,在 run()
中,您创建消息队列的第一个 PeekMessage()
正在丢弃一条初始消息(如果有待处理消息),该消息不会被您的调度循环处理。第一次调用应该使用 PM_NOREMOVE
标志。
此外,请注意消息循环中的 the dangers of filtering window messages。
话虽如此,试试这个:
LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
class Window
{
private:
HWND hWnd;
HINSTANCE hInstance;
const char* ID = "WINAPI_JVM64";
public:
Window()
{
init();
}
~Window()
{
cleanup();
}
virtual void draw(Gdiplus::Graphics*) = 0;
void init()
{
hInstance = (HINSTANCE)GetModuleHandle(NULL);
WNDCLASS wc{};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = &MessageHandler;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_HAND);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = ID;
assert(RegisterClass(&wc));
hWnd = CreateWindow(ID, "Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
200, 200, 400, 400, NULL, NULL, hInstance, this);
assert(hWnd != NULL);
ShowCursor(true);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
}
void cleanup()
{
UnregisterClass(ID, hInstance);
}
void run()
{
MSG msg;
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
};
#include "Window.h"
LRESULT CALLBACK MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCCREATE:
{
Window *pThis = static_cast<Window*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
break;
}
// DefWindowProc(WM_CLOSE) calls DestroyWindow(),
// WM_CLOSE is not the right place to call PostQuitMessage()...
//case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
{
Window *pThis = reinterpret_cast<Window*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
if (pThis)
{
Gdiplus::Graphics* g = Gdiplus::Graphics::FromHDC(hdc);
pThis->draw(g);
delete g;
}
EndPaint(hWnd, &ps);
return 0;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
class AppWindow : public Window
{
public:
void draw(Gdiplus::Graphics* g) override
{
Gdiplus::SolidBrush brown_brush(Gdiplus::Color(255, 128, 57, 0));
g->FillRectangle(&brown_brush, 0, 0, 200, 200);
}
};
int main()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
AppWindow w;
w.run();
Gdiplus::GdiplusShutdown(gdiplusToken);
return 0;
}
我有我的 window 文件 (Window.h
):
LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
class Window
{
private:
HWND hWnd;
HINSTANCE hInstance;
bool running = true;
const char* ID = "WINAPI_JVM64";
public:
Window()
{
init();
}
virtual void draw(Gdiplus::Graphics*) = 0;
void init()
{
hInstance = (HINSTANCE)GetModuleHandle(NULL);
WNDCLASS wc;
wc = {};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MessageHandler;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_HAND);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = ID;
assert(RegisterClass(&wc));
hWnd = CreateWindow(ID, "Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
200, 200, 400, 400, NULL, NULL, hInstance, NULL);
ShowCursor(true);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
}
void run()
{
MSG msg;
PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE);
while(running)
{
if(PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
running = false;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Here, the draw function is called.
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics* g = Gdiplus::Graphics::FromHDC(hdc);
draw(g);
EndPaint(hWnd, &ps);
}
}
UnregisterClass(ID, hInstance);
}
};
和主文件(main.cpp
):
#include "Window.h"
LRESULT CALLBACK MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
class AppWindow : public Window
{
public:
void draw(Gdiplus::Graphics* g) override
{
Gdiplus::SolidBrush brown_brush(Gdiplus::Color(255, 128, 57, 0));
g->FillRectangle(&brown_brush, 0, 0, 200, 200);
}
};
int main()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
AppWindow w;
w.run();
Gdiplus::GdiplusShutdown(gdiplusToken);
return 0;
}
我有一个问题就是它不会绘制!
它处理每条消息,一切都很好,但它不绘制。甚至发送了 WM_PAINT
类型的消息,但没有任何反应。
你能找出问题所在吗?
我只想要一个 window class,它有一个可覆盖的 draw()
函数和一个处理所有事件的 run()
函数,例如 WM_LBUTTONDOWN
。一切正常,屏幕只是空白。
另外,我无法关闭window,当按下右上角的X
按钮时,window只是停留;只有在调整大小并快速按下 X
后,它才会关闭。
如您所见,我有一些非常奇怪的行为,我不知道问题出在哪里。
你的绘图逻辑放错地方了。处理 WM_PAINT
消息时,它需要位于 MessageHandler
内。如果需要绘制 window 并且没有其他消息待处理,PeekMessage()
将生成一条 WM_PAINT
消息。您不能从 WM_PAINT
处理程序外部使用 window。
此外,您为 init()
中的 wc.hbrBackground
分配了错误的值。如果您使用像 COLOR_WINDOW
这样的颜色常量,则需要向其添加 1
。 WNDCLASS
documentation.
此外,在 run()
中,您创建消息队列的第一个 PeekMessage()
正在丢弃一条初始消息(如果有待处理消息),该消息不会被您的调度循环处理。第一次调用应该使用 PM_NOREMOVE
标志。
此外,请注意消息循环中的 the dangers of filtering window messages。
话虽如此,试试这个:
LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
class Window
{
private:
HWND hWnd;
HINSTANCE hInstance;
const char* ID = "WINAPI_JVM64";
public:
Window()
{
init();
}
~Window()
{
cleanup();
}
virtual void draw(Gdiplus::Graphics*) = 0;
void init()
{
hInstance = (HINSTANCE)GetModuleHandle(NULL);
WNDCLASS wc{};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = &MessageHandler;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_HAND);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = ID;
assert(RegisterClass(&wc));
hWnd = CreateWindow(ID, "Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
200, 200, 400, 400, NULL, NULL, hInstance, this);
assert(hWnd != NULL);
ShowCursor(true);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
}
void cleanup()
{
UnregisterClass(ID, hInstance);
}
void run()
{
MSG msg;
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
};
#include "Window.h"
LRESULT CALLBACK MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCCREATE:
{
Window *pThis = static_cast<Window*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
break;
}
// DefWindowProc(WM_CLOSE) calls DestroyWindow(),
// WM_CLOSE is not the right place to call PostQuitMessage()...
//case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
{
Window *pThis = reinterpret_cast<Window*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
if (pThis)
{
Gdiplus::Graphics* g = Gdiplus::Graphics::FromHDC(hdc);
pThis->draw(g);
delete g;
}
EndPaint(hWnd, &ps);
return 0;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
class AppWindow : public Window
{
public:
void draw(Gdiplus::Graphics* g) override
{
Gdiplus::SolidBrush brown_brush(Gdiplus::Color(255, 128, 57, 0));
g->FillRectangle(&brown_brush, 0, 0, 200, 200);
}
};
int main()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
AppWindow w;
w.run();
Gdiplus::GdiplusShutdown(gdiplusToken);
return 0;
}