After a while experimenting with WinAPI, I got a window with a button working. The code was put into my window procedure and looked like this:
std::vector<HWND> buttons;
HWND window;
LRESULT CALLBACK CSwindowProc(HWND window, unsigned int message, WPARAM shortParam, LPARAM longParam) {
switch (message) {
case WM_CREATE:
buttons.push_back(
CreateWindow(_T("BUTTON"), _T("OK"), WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
10, 10, 100, 100, window, NULL, (HINSTANCE)GetWindowLongPtr(window, GWL_HINSTANCE), NULL)
);
break;
case WM_DESTROY: PostQuitMessage(0); break;
default: return DefWindowProc(window, message, shortParam, longParam); break;
}
return 0;
}
Please note "buttons" is a HWND vector and "window" is just an HWND initialized with CreateWindow. Context to these snippets are identical to the code at the end of this question.
To simplify what I have, I decided to move the button creation code into a new function (CSnewButton). Then, I removed the button creation code from the window procedure and added a call to CSnewButton(). At this point the code looked like this:
std::vector<HWND> buttons;
HWND window;
void CSnewButton() {
buttons.push_back(
CreateWindow(_T("BUTTON"), _T("OK"), WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
10, 10, 100, 100, window, NULL, (HINSTANCE)GetWindowLongPtr(window, GWL_HINSTANCE), NULL)
);
}
LRESULT CALLBACK CSwindowProc(HWND window, unsigned int message, WPARAM shortParam, LPARAM longParam) {
switch (message) {
case WM_CREATE:
CSnewButton();
break;
case WM_DESTROY: PostQuitMessage(0); break;
default: return DefWindowProc(window, message, shortParam, longParam); break;
}
return 0;
}
Surprisingly, the button was no longer present on the window. By simply moving the code from the procedure to a function called by the procedure, the button disappeared.
I quickly did some debugging to rule out some problems. I added a message box to the function, and the message box did appear, so the function was being called. The C++ inline modifier didn't help my case either once added to the CSnewButton function. I see no errors or warnings on either compile time or run time either.
So I guess I either have a misunderstanding of C++ or aggressive C++ compilers (I wouldn't be surprised), a misunderstanding of WinAPI (probably the culprit) or something is so very incredibly obviously wrong with my code (I wouldn't be surprised either). Anyway, I would like to know why moving the button creation code into a function takes away the code's function, and a solution to this problem (if possible).
Full code showcasing the problem:
#include <windows.h>
#include <tchar.h>
#include <vector>
HINSTANCE instance;
HWND window;
std::vector<HWND> buttons;
//This function inexplicably does not create the button, however, if you were
//to take it's contents and paste them over a call to this function, it works
//fine.
void CSnewButton() {
buttons.push_back(
CreateWindow(_T("BUTTON"), _T("OK"), WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
10, 10, 100, 100, window, NULL, (HINSTANCE)GetWindowLongPtr(window, GWL_HINSTANCE), NULL)
);
}
LRESULT CALLBACK CSwindowProc(HWND window, unsigned int message, WPARAM shortParam, LPARAM longParam) {
switch (message) {
case WM_CREATE:
CSnewButton(); // Issue Here
break;
case WM_DESTROY: PostQuitMessage(0); break;
default: return DefWindowProc(window, message, shortParam, longParam); break;
}
return 0;
}
void CScreateWindow() {
WNDCLASSEX cclass = {
sizeof(WNDCLASSEX), CS_VREDRAW | CS_HREDRAW,
CSwindowProc, 0, 0, instance, NULL, LoadCursor(NULL, IDC_ARROW),
(HBRUSH)(COLOR_WINDOW + 1), NULL, _T("MyClass"), NULL
};
if (!RegisterClassEx(&cclass)) {
MessageBox(NULL, _T("CSControlApp failed to register window."), _T("CSControlApp failure"), MB_OK | MB_ICONERROR); return;
}
window = CreateWindow(cclass.lpszClassName, _T("My Window"), WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT, 600, 600, NULL, NULL, instance, NULL);
if (!window) {
MessageBox(NULL, _T("CSControlApp failed to create window."), _T("CSControlApp failure"), MB_OK | MB_ICONERROR); return;
}
}
int CALLBACK WinMain(HINSTANCE h, HINSTANCE p, LPSTR cmd, int show) {
instance = h;
CScreateWindow();
ShowWindow(window, show);
UpdateWindow(window);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
This is a C language issue. You have two separate variables with the same name, and you're confusing them.
HWND window; // This is a global variable
LRESULT CALLBACK CSwindowProc(HWND window, ...) // This is a parameter
Inside CSWindowProc when you say window you get the parameter. Outside CsWindowProc when you say window you get the global variable, which hasn't yet been initialized at the point at which you're trying to use it. (The call to CreateWindow in CScreateWindow hasn't returned yet.)
You can resolve the problem by giving CSnewButton an argument:
void CSnewButton(HWND window)
and calling it accordingly:
case WM_CREATE:
CSnewButton(window);
break;
To avoid any similar confusion in future, it would probably be best to remove the global variable altogether.
Related
I am creating a window with the following 2 aims both of which are requird:
when person clicks outside of the window, the window is destroyed.
when a person clicks a button inside the window, we process the WM_COMMAND message, and then the window is destroyed.
To achieve both these goals, I looked and found there is a WM_KILLFOCUS message which is sent when the window looses focus.
I wrote destroy window code in the WM_KILLFOCUS handler, but then the button click message box does not come. I searched and found the destroy event is not serialized and hence it could be that it is being sent before the window button click could be caught. I have hence changed the code like below, where it also sends a WM_COMMNAD to kill the window.
case WM_KILLFOCUS:
SendMessage(hwnd, WM_COMMAND, KILLTHEWIN, 0);// hope it serializes message
break;
case WM_COMMAND:
switch (wp)
{
case KILLTHEWIN:
DestroyWindow(hwnd);
break;
case BUTTON_CLICKED:
default:
MessageBox(NULL, L"Hurray", L"Hurray MSG reveid", MB_OK);
break;
}
I see that when I click the button inside the window, the window is destroyed and the MessageBox "Hurray" is not received.
How can I ensure that the messages are processed before destroying the window?
Update 1: Minimal code as asked. Now handling the WM_ACTIVATE message instead for handling WM_KILLFOCUS . Both the two aims are written above:
#include <windows.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <windowsx.h>
#include <map>
#include <commctrl.h>
#define KILLTHEWIN 10
#define BUTTON_PRESS 11
LRESULT CALLBACK windowprocedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow)
{
WNDCLASSW wc = { 0 };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hInstance = hInst;
wc.lpszClassName = L"mywindowsclass";
wc.lpfnWndProc = windowprocedure;
// before creating window for a class we need to register that class
if (!RegisterClassW(&wc))
{
return -1; // registration failed
}
HWND hWnd = CreateWindowW(L"mywindowsclass", L"My window", WS_POPUP | WS_VISIBLE/*| WS_EX_TOOLWINDOW*/ /* check for no task bar*/,
100, 100, 170, 500, NULL, NULL, NULL, NULL);
MSG msg{ 0 };
while (GetMessage(&msg, NULL, NULL, NULL))
{
// translate the messahe
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
// main window message handling
LRESULT CALLBACK windowprocedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
DWORD dwStyleOfStaticText = 0;
HWND hBmp2 = NULL;
WORD reason = 0;
switch (msg)
{
case WM_CREATE:
dwStyleOfStaticText = SS_NOTIFY | SS_LEFT | WS_CHILD | WS_VISIBLE | WS_TABSTOP /*| WS_BORDER */;
hBmp2 = CreateWindowW(L"Button", L"Button1", WS_VISIBLE | WS_CHILD,
5, 5 + 4 * 15, 100, 50,
hwnd, (HMENU)BUTTON_PRESS, NULL, NULL);
SetWindowLong(hwnd, GWL_STYLE, 0);
break;
case WM_ACTIVATE:
reason = LOWORD(wp);
if (reason == WA_ACTIVE)
{
//Activated by some method other than a mouse click (for example, by a call to the SetActiveWindow function or by use of the keyboard interface to select the window).
}
else if (reason == WA_CLICKACTIVE)
{
// activated by mouse click
}
else if (reason == WA_INACTIVE)
{
PostMessage(hwnd, WM_COMMAND, KILLTHEWIN, 0);
}
break;
case WM_COMMAND: // when ever a menu is clicked
// WM_COMMAND has been received for which control handle and then kill it
switch (wp)
{
case KILLTHEWIN:
DestroyWindow(hwnd);
break;
case BUTTON_PRESS:
MessageBoxW(hwnd, L"button pressed", L"button pressed caption ", MB_OK);
break;
}
PostMessage(hwnd, WM_COMMAND, KILLTHEWIN, 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wp, lp);// case other as default window procedure
}
return (LRESULT)0;
}
The WM_ACTIVATE was handled above instead of the kill focus .
The two aims written above are really important . Still on pressing the button the MessageBox does not appear and window is killed .
I put it in debugger
one time the breakpoint hit the button message box but it still did not came . The other time the breakpoint hit the WM_ACTIVATE deactivate reason first .
I started to learning winapi c++ today. And i wrote this simple program that have to change window name to the name that i write in text box:
#include <Windows.h>
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
void AddMenu(HWND);
void AddControl(HWND);
HWND hEdit;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrewInst, LPSTR args, int ncmdshow)
{
WNDCLASSW wc = { 0 };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hInstance = hInst;
wc.lpszClassName = L"class";
wc.lpfnWndProc = WindowProcedure;
if (!RegisterClassW(&wc))
return -1;
CreateWindowW(L"class", L"Window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 500, 500, NULL, NULL, NULL, NULL);
MSG msg = { 0 };
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
case WM_COMMAND:
switch (wp)
{
case 1:
MessageBeep(MB_SERVICE_NOTIFICATION); break;
case 2:
DestroyWindow(hWnd); break;
case 3:
wchar_t text[100];
GetWindowTextW(hEdit, text, 100);
SetWindowTextW(hWnd, text);
break;
}
case WM_CREATE:
AddMenu(hWnd);
AddControl(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hWnd, msg, wp, lp);
}
}
void AddMenu(HWND hWnd)
{
HMENU hMenu;
hMenu = CreateMenu();
HMENU file;
file = CreateMenu();
HMENU Sub;
Sub = CreateMenu();
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)file, "File");
AppendMenu(file, MF_STRING, 1, "Open");
AppendMenu(file, MF_POPUP, (UINT_PTR)Sub, "Sub");
AppendMenu(file, MF_STRING, 2, "Exit");
AppendMenu(Sub, MF_STRING, 3, "Change");
SetMenu(hWnd, hMenu);
}
void AddControl(HWND hWnd)
{
CreateWindowW(L"Static", L"Enter text: ", WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER, 200, 100, 100, 20, hWnd, NULL, NULL, NULL);
hEdit = CreateWindowW(L"Edit", L"...", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL, 200, 152, 100, 50, hWnd, NULL, NULL, NULL);
}
By default in text box i have .... So i run my program, starting to type hello world for expample but nothing changes. I highlited my 3 dots that had to change and boom, my text appeared. I go to the menu choose File -> Sub -> Change and my window name changes to 3 dots ....
BUT! If i delete these: | ES_MULTILINE | ES_AUTOVSCROLL then text box working correctly but menu doesnt show up in my window.
I found that i have to do smth with EN_CHANGE but idk what. I tried to change WM_COMMAND to EN_CHANGE and my textbox worked correctly, but other menu buttons didnt work at all...
Your failure is caused by handling all the edit control notifications the same way. You need to test the notification code (found in HIWORD(wParam)) to see whether it is EN_CHANGE, and let notifications you aren't handling pass through to DefWindowProc.
You also have an even bigger bug in that you reach the closing brace of a function with a non-void return type. You need to always return something; when you aren't calling DefWindowProc the system still expects a return value. The reason things start working when you change WM_COMMAND to EN_CHANGE is not because you are handling EN_CHANGE, it is because you no longer have incorrect handling of WM_COMMAND, and instead let the DefWindowProc take them.
The fall-through from WM_COMMAND into WM_CREATE isn't helping things either.
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
switch (HIWORD(wParam)) /* command kind */
{
case 0: // menus
switch (LOWORD(wParam)) /* child ID */
{
case 1:
MessageBeep(MB_SERVICE_NOTIFICATION);
return 0;
case 2:
DestroyWindow(hWnd);
return 0;
case 3:
wchar_t text[100];
GetWindowTextW(hEdit, text, 100);
SetWindowTextW(hWnd, text);
return 0;
}
break;
}
break;
case WM_CREATE:
AddMenu(hWnd);
AddControl(hWnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
// break actions also reach here
return DefWindowProcW(hWnd, msg, wParam, lParam);
}
I don't really get how to use HWND in c++.
I want to press a button and it should start a thread with a code running.
But I never receive the command for button click in an other callback.
So I did some debugging and it seems like that _wndInstance->GetWndHWND() returns something not valid. The method returns a private field which has it stored.
If you look a case WM_CREATE, the window content added will not show up with _wndInstance->GetWndHWND(). But if I just use hwnd from the parameters, it does work. But how is that possible if my first test-check validates that they are the same??
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (_wndInstance->GetWndHWND() == hwnd)
cout << "same" << endl; // Code is getting here!
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CREATE:
{
_wndInstance->CreateWndContent(_wndInstance->GetWndHWND()); // not working, but hwnd is!
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
EDIT:
_wndInstance is an instance of a mainwindow class I wrote.
Part of mainWindow header:
private:
HWND _wndHwnd;
public:
HWND GetWndHWND();
MainWindow cpp:
HWND MainWindow::GetWndHWND()
{
return _wndHwnd;
}
_wndHwnd is set in a private method which creates the window:
_wndHwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"\"Xbox controller on WINDOWS\" Manager",
WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX, // no maximize box
CW_USEDEFAULT, CW_USEDEFAULT, 450, 370,
NULL, NULL, hinstance, NULL);
if (_wndHwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return;
}
ShowWindow(_wndHwnd, nCmdShow);
UpdateWindow(_wndHwnd);
WM_CREATE is sent before CreateWindowEx() returns, and thus before your assignment happens. You will need to restructure your code so that the HWND is only used after it is assigned to _wndHwnd.
If you don't need to do anything between WM_CREATE and the beginning of the message loop, you can just drop your WM_CREATE handler. That depends on how your code will work in the future.
But a safer approach would be to assign _wndHwnd in your WM_CREATE handler (or even WM_NCCREATE), since you have it available as the hwnd parameter to your window procedure, and it would handle other messages sent between the window creation and the variable assignment. You can even pass _wndInstance as the last parameter to CreateWindowEx() and access it from WM_CREATE; see this for details.
I am very new to win32 gui applications.I am trying to create simple calculator application.
In that i have designed a GUI for calculator.Now i want to get text of textbox entered by user and also want to set text on click of any button i designed from 0 to 9.
For testing purpose i wrote a code to get text from textbox and tried to display it in a messagebox. But my messagebox is showing empty message. Following is my code in WinProc:
LRESULT CALLBACK WinProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
HWND B[16];// these are the saperate handles to each button in the main window.
HWND hEditA,hEditB,hEditC;//handle for text box.
char c[16][2]={"1","2","3","4","5","6","7","8","9","0","+","-","*","/",".","="};
int i=0,j,x=100,y=130;
int id[15]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int cnt;
//char val[20];
TCHAR val[30] = {0};
int len;
switch(msg)
{
case WM_PAINT:
hdc=BeginPaint(hWnd,&ps);
TextOut(hdc,10,12,"Number 1:",strlen("Number 1:")+1);
TextOut(hdc,10,47,"Number 2:",strlen("Number 2:")+1);
TextOut(hdc,10,87,"Result :",strlen("Result :")+1);
// TextOut(hdc,10,100,"hellow sachin",strlen("hello sachin")+1);
EndPaint(hWnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
case WM_CREATE:
hEditA=CreateWindow(TEXT("Edit"), TEXT(""), WS_CHILD | WS_VISIBLE | WS_BORDER|ES_NUMBER|ES_RIGHT, 100, 10, 150, 25, hWnd, NULL, NULL, NULL);
hEditB=CreateWindow(TEXT("Edit"), TEXT(""), WS_CHILD | WS_VISIBLE | WS_BORDER|ES_NUMBER|ES_RIGHT, 100, 45, 150, 25, hWnd, NULL, NULL, NULL);
hEditC=CreateWindow(TEXT("Edit"), TEXT(""), WS_CHILD | WS_VISIBLE | WS_BORDER|ES_NUMBER|ES_RIGHT, 100, 85, 150, 25, hWnd, NULL, NULL, NULL);
cnt=0;
for(i=0;i<16;i++)
{
if(cnt==4)
{
y=y+40;
x=100;
cnt=0;
}
if(cnt<4)
{
B[i] = CreateWindow(TEXT("button"), TEXT(c[i]),
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
x, y, 30, 30,
hWnd, (HMENU) id[i], NULL, NULL);
x=x+40;
//y=y+30;
cnt++;
}
}
case WM_COMMAND:
switch(LOWORD(wParam))
{
case 1:
len=GetWindowText( hEditA,val,10);
MessageBox(hWnd,(LPSTR)val, "Info", MB_OK);
SetWindowText( hEditB,val);
break;
}
}
}
B. I also want to display the caption of the button pressed in the textbox.
So please can anyone help me to find the way to correct it.
thanx in advance..
This should get the text for you:
SendMessage(hEditA, WM_GETTEXT, (WPARAM)10, (LPARAM)val);
Are those window handles defined in the scope of the window procedure, or global? If they are defined in the scope of the window procedure, they will be forgotten after WM_CREATE returns control to the Operating System.
If they are defined in the window procedure, try:
static HWND hEditA, ...
EDIT:
You should also break from your WM_CREATE handler, as it will fall through to your WM_COMMAND handler.
So, basically I'm using a code like this one. It's a little simple window where you can only change text inside of an edit box and press a button that will make a callback to a function (DoSomethingCallback(text)).
#include <windows.h>
#define ID_EDIT 1
#define ID_BUTTON 2
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND hwndEdit;
static HWND hwndButton;
static int len;
static TCHAR text[30];
switch(msg)
{
case WM_CREATE:
hwndEdit = CreateWindow(TEXT("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
50, 50, 150, 20, hwnd, (HMENU) ID_EDIT,
NULL, NULL);
hwndButton = CreateWindow(
TEXT("button"), TEXT("Set Title"),
WS_VISIBLE | WS_CHILD,
50, 100, 80, 25,
hwnd, (HMENU) ID_BUTTON, NULL, NULL);
break;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) {
SetWindowText(hwnd, "Working...");
GetWindowText(hwndEdit, text, len);
DoSomethingCallback(text);
SetWindowText(hwnd, "Finished");
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Edit Control" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0,IDC_ARROW);
RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Edit control"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
220, 220, 280, 200, 0, 0, hInstance, 0);
while( GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
The problem is that when running that Callback the window will appear as "Not responsive" and even laggy if you try to close or press the button. I understand the reason why this might be happening, the callback takes time and the receiving loop isn't there to check the input of the user. I've searched a way to 'fix' this and I couldn't find any. I'm pretty sure it's something dumb, but I have to try and ask.
An obvious way would be making a faster callback. But is there another one like checking the user input inside of a DoSomethingCallback() while, or I have to use multiple threads?
Sorry for the confusing question.
I would go for calling QueueUserWorkItem() to handle it. If your DoSomethingCallback() is too long, there is no way to make your window responsive while DoSomethingCallback() is working since there is only one thread to run the code. Good Luck!
If possible, you can process the message queue at intervals within DoSomethingCallback which will keep the UI responsive. Just run the original message loop i.e. while( GetMessage(... whenever you can but make sure you disable the button so the user does not click a second time... this will lead to recursion.
You can peek at the message queue to convince windows that you are still alive and kicking, by calling PeekMessage() from time to time. This assumes that you can do this in your DoSomethingCallback(), i.e. that the main thread doesn't hang completely. Regard the loop below as pseudo code and use your imagination to transform it to your needs.
void DoSomethingCallback()
{
// Loop that takes a long time
while (true) {
DoSomeStuff();
MSG msg;
PeekMessage(&msg, nil, 0, 0, PM_NOREMOVE);
if (ShouldBreak()) {
break;
}
}
}