#c #winapi #gdi
#c #winapi #gdi
Вопрос:
У меня есть мой файл окна ( 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(amp;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(amp;msg, hWnd, 0, 0, PM_REMOVE);
while(running)
{
if(PeekMessage(amp;msg, hWnd, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
running = false;
TranslateMessage(amp;msg);
DispatchMessage(amp;msg);
}
else
{
// Here, the draw function is called.
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, amp;ps);
Gdiplus::Graphics* g = Gdiplus::Graphics::FromHDC(hdc);
draw(g);
EndPaint(hWnd, amp;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(amp;brown_brush, 0, 0, 200, 200);
}
};
int main()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(amp;gdiplusToken, amp;gdiplusStartupInput, nullptr);
AppWindow w;
w.run();
Gdiplus::GdiplusShutdown(gdiplusToken);
return 0;
}
У меня проблема в том, что оно просто не рисуется!
Он обрабатывает каждое сообщение, все хорошо, но он не рисует. Отправляются даже сообщения типа WM_PAINT
, но ничего не происходит.
Можете ли вы определить проблему?
Мне просто нужен класс window, который имеет переопределяемую draw()
функцию с run()
функцией, которая обрабатывает все события, такие как WM_LBUTTONDOWN
. Все это работает нормально, экран просто остается пустым.
Кроме того, я не могу закрыть окно, при нажатии X
кнопки в правом верхнем углу окно просто остается; только после изменения размера и быстрого нажатия X
оно закрывается.
Как вы можете видеть, у меня довольно странное поведение, и я не знаю, в чем проблема.
Комментарии:
1. Вы вызываете
BeginPaint()
извнеWM_PAINT
обработчика. Это недопустимо.2. @rodrigo Ах, это действительно была проблема. Теперь это работает.
Ответ №1:
Ваша логика рисования находится не в том месте. Оно должно быть внутри MessageHandler
при обработке WM_PAINT
сообщения. PeekMessage()
сгенерирует WM_PAINT
сообщение, если окно необходимо нарисовать, и никакие другие сообщения не ожидаются. Вы не можете рисовать в окне снаружи WM_PAINT
обработчика.
Кроме того, вы присваиваете неправильное значение wc.hbrBackground
in init()
. Если вы используете константу цвета like COLOR_WINDOW
, вам нужно добавить 1
к ней. Это указано в WNDCLASS
документации.
Кроме того run()
, ваш первый PeekMessage()
способ создания очереди сообщений отбрасывает начальное сообщение, если оно находится в ожидании, это сообщение не обрабатывается вашим циклом отправки. Этот 1-й вызов должен использовать PM_NOREMOVE
флаг вместо этого.
Кроме того, помните об опасностях фильтрации оконных сообщений в вашем цикле сообщений.
С учетом сказанного, попробуйте это вместо:
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 = amp;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(amp;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(amp;msg, NULL, 0, 0, PM_NOREMOVE);
while (GetMessage(amp;msg, NULL, 0, 0))
{
TranslateMessage(amp;msg);
DispatchMessage(amp;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, amp;ps);
if (pThis)
{
Gdiplus::Graphics* g = Gdiplus::Graphics::FromHDC(hdc);
pThis->draw(g);
delete g;
}
EndPaint(hWnd, amp;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(amp;brown_brush, 0, 0, 200, 200);
}
};
int main()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(amp;gdiplusToken, amp;gdiplusStartupInput, nullptr);
AppWindow w;
w.run();
Gdiplus::GdiplusShutdown(gdiplusToken);
return 0;
}
Комментарии:
1. Спасибо за этот ответ. Но я действительно не знаю, что происходит в MessageHandler(): «case WM_NCCREATE:{…}». Не возражаете, объяснив это? И является ли ~Window() чем-то вроде «антиконструктора»?
2. Обратите внимание, что я добавил
this
указатель объекта класса кlpParam
параметруCreateWindow()
. Таким образом,this
указатель может быть передан вMessageHandler()
(который не является членом класса) вWM_NCCREATE
сообщении (иWM_CREATE
). Затем я сохраняюthis
указатель в самомHWND
себе для использования с более поздними сообщениями, в данном случаеWM_PAINT
, чтобы он мог вызыватьdraw()
правильныйWindow
объект, окно которого рисуется.3.
~Window()
формально известен как деструктор . Я удивлен, что вы узнали о конструкторах, но не о деструкторах, поскольку они идут рука об руку вместе. Компилятор вызывает конструктор при создании объекта и вызывает деструктор при уничтожении объекта.4. Это необычный способ сделать это, но разве я не могу просто создать экземпляр window снаружи
main()
, а затем получить к нему доступMessageHandler()
? Кстати: я начал с C несколько дней назад, а до этого занимался Java. Но я провел некоторое исследование и понял, что происходит внутриcase WM_NCCREATE:{...}
.5. @Lost » разве я не могу просто создать экземпляр окна снаружи
main()
, а затем получить к нему доступMessageHandler()
? » — вы могли бы, если у вас одновременно есть только 1Window
объект в памяти. Если вам когда-нибудь понадобится иметь более 1Window
одновременно, этот подход больше не будет работать, поскольку они будут использовать одно и то жеMessageHandler()
, поэтому он должен иметь возможность различать их. Именно здесьhWnd
вступает в игру его параметр, который создает удобное место для храненияthis
указателя каждого объекта для легкого доступа.