I'm building a real time graphics application and I've noticed that under certain conditions the operating system will post nonqueued messages that block my program. For example, this will happen during the entire time that a user is resizing the window.
To solve this problem, I would like to put the window on a different thread, but I read that this is a terrible idea because of the way windows messages work. Is it still a problem if I process all messages on the other thread, and never send or receive messages from the main thread? I'm thinking about setting up my own message queue with a mutex so I can pass the data I care about back and forth. If this won't work, are there any other ways to solve this problem?
Here's a simplified version of the code I'm running:
LRESULT CALLBACK WindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_SIZE) {
// This event gets called a lot
// Handle window resize
return 0;
} else {
return DefWindowProc(window, message, wParam, lParam);
}
}
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int numCmd) {
WNDCLASSA class = {};
class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
class.lpfnWndProc = WindowCallback;
class.lpszClassName = "Name";
class.hInstance = instance;
RegisterClassA(&class)l
HWND win = CreateWindowExA(0, class.lpszClassName, "Name",
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 600, 400,
0, 0, instance, 0);
while(true) {
// Handle Events
MSG msg = {};
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Do Updating of State and Graphics Rendering Here
}
}
Thanks in advance!
Related
I am creating a Win32 ComboBox for the first time. And I have a problem here.
When calling CreateWindow for the ComboBox, it calls the WndProc callback function again with the WM_CREATE message, so what happens is the ComboBox makes a child ComboBox, again and again like recursion.
Here is the code:
#include <stdio.h>
#include <conio.h>
#include <Windows.h>
#include <random>
#include <time.h>
#include <string>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = L"ComboBox";
const WCHAR *items[] = { L"Apple", L"Orange", L"Melon", L"Grape", L"Strawberry" };
HWND hwnd;
enum COMMAND_ID {
COMMAND_ID_CONTROL_COMBO_0
};
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
srand(time(NULL));
g_hInst = hInstance;
WNDCLASS wndClass;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hInstance = hInstance;
wndClass.lpfnWndProc = WndProc;
wndClass.lpszClassName = lpszClass;
wndClass.lpszMenuName = NULL;
wndClass.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndClass);
hwnd = CreateWindow(
lpszClass,
lpszClass,
WS_CAPTION | WS_SYSMENU | WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
(HMENU)NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (true)
{
GetMessage(&msg, NULL, 0, 0);
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
static HWND hCombo;
static WCHAR str[128];
switch (msg)
{
case WM_CREATE:
{
hCombo = CreateWindow(
L"combobox",
NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWN,
10, 10, 200, 200,
hWnd,
(HMENU)COMMAND_ID_CONTROL_COMBO_0,
g_hInst,
NULL);
for (int i = 0; i < 5; ++i)
{
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)items[i]);
}
SendMessage(hCombo, CB_SETCURSEL, 0, NULL);
}
break;
case WM_COMMAND:
{
switch (LOWORD(wparam))
{
case COMMAND_ID_CONTROL_COMBO_0:
switch (HIWORD(wparam))
{
case CBN_SELCHANGE:
{
int iCurSel = SendMessage(hCombo, CB_GETCURSEL, NULL, NULL);
SendMessage(hCombo, CB_GETLBTEXT, iCurSel, (LPARAM)str);
SetWindowText(hWnd, str);
}
break;
case CBN_EDITCHANGE:
GetWindowText(hCombo, str, 128);
SetWindowText(hWnd, str);
break;
}
break;
default:
break;
}
}
return 0;
}
return DefWindowProc(hWnd, msg, wparam, lparam);
}
And here is the result:
I tried to put some boolean flag to execute WM_CREATE only once, and it works, I mean only creating one ComboBox without any child in it.
But, it just looked like only a white window with a border mark, there's no arrow button or anything to dropdown page that the ComboBox is supposed to have.
This recursive case never happened when I was creating different controls like Buttons, CheckBoxes, ListBoxes, etc.
And the ComboBox created doesn't look like it has the proper shape, too.
Hope I am just missing something simple.
When calling CreateWindow for the ComboBox, it calls the WndProc
callback function again with the WM_CREATE message, so what happens
is the ComboBox makes a child ComboBox, again and again like
recursion.
WM_CREATE message is sent to the window procedure of the new window after the window is created. Your first WM_CREATE message is generated by this line hwnd = CreateWindow(). Then you create another window in first WM_CREATE message, so it will generate second WM_CREATE message. Because you use the same registered class ("ComboBox" / "combobox", it is not case sensitive) to create all these windows, all of them use the same one window procedure. So you receive WM_CREATE message again and again until CreateWindow fail to create a window and return NULL.
But, it just looked like only a white window with a border mark,
there's no arrow button or anything to dropdown page that the ComboBox
is supposed to have.
The root cause is you register a class with the same name as the existing system class: "ComboBox" / "combobox". This new registered class override the existing one. It is just a common window instead of a predefined Combobox control as #RemyLebeau pointed out.
An application can register an application local class having the same
name as a system class. This replaces the system class in the context
of the application but does not prevent other applications from using
the system class.
Refer to "How the System Locates a Window Class".
To make the Combobox display in expected shape, what you need to do is changing the lpszClass to a non-predefined one, for example, like "SimpleComboBoxExample".
It is suggested to use predefined macro of Combobox Class Name: WC_COMBOBOX instead of L"combobox".
More reference: "How to Create a Simple Combo Box".
You are not actually creating a Win32 ComboBox at all. You are registering your own class named "ComboBox", and then creating a window of that class, which creates a window of that class, which creates a window of that class, and so on recursively.
You need to change this line:
LPCTSTR lpszClass = L"ComboBox";
To a different unique name, such as "MyWindowClass".
On a side note, your message loop is structured wrong. It should look like this instead:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
And in your WndProc(), the return 0; statement underneath the WM_COMMAND handler is in the wrong place. It needs to be moved inside of the WM_COMMAND handler instead:
case WM_COMMAND:
{
switch (...)
{
...
}
return 0; // <-- moved here
}
//return 0; // <-- from here
I have been expanding my library (Physical library of these weird things called 'books'... I know... I know) and I'm reading Beginning DirectX 9.0 by Wendy Jones. As I have gone through some books in the past that are 'outdated' the logic behind them is actually the same, if not more important in the earlier versions (in my experience) of things like C++ books I have read. The issue I am having with this DirectX 9 book is, 10/10 practice codes, don't work, ever. Even the solutions found on here, and MSDN
didn't work for me. (Identical problem).
So I was hoping if you could tell me before I go and purchase a book on DX11, if it might be something to do with my compiler/vs or the fact that vs is updated 2015, and this DX9 is obselete/DX11 standards have been introduced.
//Include the Windows header file that's needed for all Windows applications
#include <Windows.h>
HINSTANCE hInst; // global handle to hold the application instance
HWND wndHandle; // global variable to hold the window handle
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);
//forward declerations
bool initWindow(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND, UINT WPARAM, LPARAM);
//This is winmain, the main etry point for Windows applications
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
//Initialize the window
if (!initWindow(hInstance))
return false;
//main message loop: (See page 13, "Adding the Windows Code" - Chapter 2
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT);
{
//Check the message queue
while (GetMessage(&msg, wndHandle, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return(int)msg.wParam;
}
/******************************************************************************
* bool initWindow( HINSTANCE hInstance )
* initWindow registers the window class for the application, creates the window
******************************************************************************/
bool initWindow(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
//Fill in the WNDCLASSEX structure. THis describes how the window will look to the system
wcex.cbSize = sizeof(WNDCLASSEX); // the size of the structure
wcex.style = CS_HREDRAW | CS_VREDRAW; // the class style
wcex.lpfnWndProc = (WNDPROC)WndProc; // the window procedure callback
wcex.cbClsExtra = 0; // extra bytes to allocate for this calss
wcex.cbWndExtra = 0; // extra bytes to allocate for this instance
wcex.hInstance = hInstance; // handle to the application
wcex.hIcon = 0; // icon to associate with the application
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); // the default cursor
wcex.lpszMenuName = NULL; // the resource name for the menu
wcex.lpszClassName = NULL; // the class name being created
wcex.hIconSm = 0;
RegisterClassEx(&wcex);
//Create the window
wndHandle = CreateWindow(
(LPCWSTR)"DirectXExample", // the window class to use
(LPCWSTR)"DirectXExample", // the title bar text
WS_OVERLAPPEDWINDOW, // the window style
CW_USEDEFAULT, // the starting x coordinate
CW_USEDEFAULT, // the starting y coordinate
640, //the pixel width of the window
480, //the pixel height of the window
NULL, // the parent window; NULL for desktop
NULL, // the menu for the application; NULL for none
hInstance, // the handle to the apllication instance
NULL); // no values passed to the window
//make sure that the window handle that is created is valid
if (!wndHandle)
return false;
//Display the window on the screen
ShowWindow(wndHandle, SW_SHOW);
UpdateWindow(wndHandle);
return true;
}
It's perfectly fine to keep using DirectX 9. But your implementation for getting a minimal host window up on the screen has some simple bugs. It also is doing some bad casts between ANSI and wide strings. Let's get you fixed:
Remove the forward declaration of WinMain. This line, just remove it.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);
In the actual function body for WinMain, change the type for lpCmdLine from LPTSTR parameter to be just LPSTR.
//This is winmain, the main etry point for Windows applications
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
//Initialize the window
if (!initWindow(hInstance))
Your declaration of WndProc is also incorrect. WndProc should be declared as follows:
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam);
Once you fix the above declaration of WndProc, you can take out that bad cast operation in the WNDCLASS initialization. Change this:
wcex.lpfnWndProc = (WNDPROC)WndProc; // the window procedure callback
To this:
wcex.lpfnWndProc = WndProc; // the window procedure callback
You are missing a definition of WndProc. You need to implement that function yourself. Here's a minimal implementation:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
The above will get your code to compile, but it will still have some bugs and won't actually run like it should. Let's fix those.
First, your message pump has an extra ; that is preventing it from actually running and keeping your code in an infinite loop. This line:
while (msg.message != WM_QUIT);
Should be (without the semicolon):
while (msg.message != WM_QUIT)
And while I'm here, your message pump implementation is kind of weird. GetMessage only returns FALSE when msg.message==WM_QUIT So the outer loop is not needed. Change this:
while (msg.message != WM_QUIT)
{
//Check the message queue
while (GetMessage(&msg, wndHandle, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
to be this:
//Check the message queue until WM_QUIT is received
while (GetMessage(&msg, wndHandle, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
When you actually implement your graphics loop for your DX app, you can change the GetMessage call to PeekMessage and then explicitly check for WM_QUIT then.
Your initWindow is failing for many reasons.
You are leaving some garbage values in the WNDCLASSEX variable. Change this line:
WNDCLASSEX wcex;
To be this:
WNDCLASSEX wcex = {};
You are forgetting to set wcex.lpszClassName. Make it this:
wcex.lpszClassName = L"DirectXExample";
And then your casting of ANSI strings to (LPCWSTR) is incorrect. To make it easier, here's a fixed version of your initWindow function.
bool initWindow(HINSTANCE hInstance)
{
WNDCLASSEX wcex = {};
//Fill in the WNDCLASSEX structure. THis describes how the window will look to the system
wcex.cbSize = sizeof(WNDCLASSEX); // the size of the structure
wcex.style = CS_HREDRAW | CS_VREDRAW; // the class style
wcex.lpfnWndProc = (WNDPROC)WndProc; // the window procedure callback
wcex.cbClsExtra = 0; // extra bytes to allocate for this calss
wcex.cbWndExtra = 0; // extra bytes to allocate for this instance
wcex.hInstance = hInstance; // handle to the application
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION); // icon to associate with the application
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); // the default cursor
wcex.lpszMenuName = NULL; // the resource name for the menu
wcex.lpszClassName = L"DirectXExample"; // the class name being created
wcex.hIconSm = 0;
RegisterClassEx(&wcex);
//Create the window
wndHandle = CreateWindow(
L"DirectXExample", // the window class to use
L"DirectXExample", // the title bar text
WS_OVERLAPPEDWINDOW, // the window style
CW_USEDEFAULT, // the starting x coordinate
CW_USEDEFAULT, // the starting y coordinate
640, //the pixel width of the window
480, //the pixel height of the window
NULL, // the parent window; NULL for desktop
NULL, // the menu for the application; NULL for none
hInstance, // the handle to the apllication instance
NULL); // no values passed to the window
//make sure that the window handle that is created is valid
if (!wndHandle)
return false;
//Display the window on the screen
ShowWindow(wndHandle, SW_SHOW);
UpdateWindow(wndHandle);
return true;
}
And that should do it.
I've starting watching the handmade hero videos and I'm trying to make a win32 window but the CreateWindowEx() function keeps failing.
I checked the error code and I get 1407.
Code is below.
Thanks in advance.
#include <Windows.h>
LRESULT CALLBACK WindowProcedure(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT result;
switch (uMsg)
{
case WM_ACTIVATEAPP:
{
OutputDebugStringA("The window is now active");
break;
}
case WM_SIZE:
{
OutputDebugStringA("The window is now being resized");
break;
}
case WM_CREATE:
{
OutputDebugStringA("The window has been created");
break;
}
default:
{
result = DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
}
}
return result;
};
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
WNDCLASS GameWindow;
GameWindow.style = CS_OWNDC|CS_HREDRAW|CS_VREDRAW;
GameWindow.lpfnWndProc = WindowProcedure;
GameWindow.hInstance = hInstance;
// HICON hIcon;
GameWindow.lpszClassName = "HandmadeHeroWindowClass";
RegisterClass(&GameWindow);
if (HWND GameWindowHandle = CreateWindowEx(
0,
GameWindow.lpszClassName,
"Handmade Hero",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
hInstance,
0
))
{
for (;;)
{
MSG message;
BOOL messageResult = GetMessage(&message, GameWindowHandle, 0, 0);
if (messageResult != 0)
{
DispatchMessage(&message);
}
else if (messageResult == 0)
{
break;
}
else
{
// ERROR
}
}
}
else
{
OutputDebugStringA("Couldn't create window");
}
DWORD error = GetLastError();
return 0;
};
Your window procedure returns an uninitialized variable in every path except for default:, this is undefined behavior and failure of window creation is entirely possible.
For WM_CREATE, the documentation says:
If an application processes this message, it should return zero to continue creation of the window.
As Michael noted in the comments, RegisterClass is failing. Same category of mistake, you're passing a WNDCLASS structure leaving most members uninitialized.
Thanks to Remy Lebeau for the answer, the problem was that my WNDCLASS had uninitialized values for all fields except those I changed, this caused the RegisterClass() to fail and consequently the CreateWindowEx() to fail.
I changed WNDCLASS declaration to this:
WNDCLASS GameWindow = {0};
Thanks to everyone who helped.
I am having trouble correctly running a message loop from within a message handler. In effect replicating how DialogBox() processes messages, minus all of the windowing.
Simply invoking GetMessage() from within a message handler nearly works except when the WM_SYSKEYDOWN event opening the system menu also triggers the entry into a sub-loop. After this weird things happen, with keys presses being swallowed and WM_MOUSEMOVE messages relative to the system menu getting sent to the main window.
For the record this happens both in Windows 8 and XP.
To give some context I am attempting a threading model where a (windowless) worker thread communicates through blocking SendMessage calls back to the main window acting as a server. These actions may require further input or depend on other I/O and thus regular messages need to be processed until the reply is ready.
I'm fairly certain that this is a basic mistake or misunderstanding on my part, just like last time I posted here, but I can't quite seem to work out what I'm doing wrong on my own.
Here is my repro case. Try navigating after pressing ALT+SPACE to open the system menu,
#include <windows.h>
BOOL update;
LRESULT WINAPI WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
MSG msg;
char text[256];
switch(uMsg) {
case WM_DESTROY:
ExitProcess(0);
// Trigger an update on input
case WM_SYSKEYDOWN:
update = TRUE;
break;
// Display the update from the worker thread, returning once it is time to
// ask for the next one
case WM_USER:
wsprintf(text, TEXT("%u"), (unsigned int) lParam);
SetWindowText(hwnd, text);
while(!update && GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
update = FALSE;
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
DWORD WINAPI ThreadProc(void *hwnd) {
// Submit updates as quickly as possible
LONG sequence = 1;
for(;;)
SendMessage(hwnd, WM_USER, 0, sequence++);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCommandLine, int nCmdShow) {
HWND hwnd;
MSG msg;
// Create our window
WNDCLASS windowClass = { 0 };
windowClass.lpfnWndProc = WindowProc;
windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
windowClass.hCursor = NULL;
windowClass.lpszClassName = TEXT("Repro");
RegisterClass(&windowClass);
hwnd = CreateWindow(TEXT("Repro"), TEXT("Repro"),
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
hInstance, 0);
// Launch the worker thread
CreateThread(NULL, 0, ThreadProc, hwnd, 0, NULL);
// And run the primary message loop
while(GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Modal message loops are perfectly fine. Raymond Chen has a series of articles on writing modal message loops properly.
One thing I notice: Your thread should post the message, not send it; SendMessage calls directly into the window proc. Don't use PostThreadMessage, either; that's designed for threads without visible UI (and the nested DispatchMessage won't know how to dispatch the thread message, resulting in dropped messages).
This is excerpt from my code based on win32 api:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
void __fastcall TMyThread::Execute(void)
{
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = class_name.c_str();
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK);
return;
}
hwnd = CreateWindowEx(0, class_name.c_str(), NULL, 0, 0, 0, 100, 100, HWND_MESSAGE, NULL, wc.hInstance, NULL);
if (hwnd == NULL)
{
MessageBox(NULL, L"Window Creation Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK);
return;
}
MSG msg;
BOOL ret;
while ((ret = GetMessage(&msg, 0, 0, 0)) != 0)
{
if (ret != -1)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
My questions:
Is it required to do some resource cleanup after quitting message loop (like CloseHandle for example)? Quite often i am seeing code samples without any such things. Is it correct?
Can newly created window receive messages into thread queue before first call of GetMessage function (we suppose that window was successfully created i.e. function CreateWindowEx returned without errors)?
Please take in mind that TMyThread is not main thread of application. So it can be created and destroyed many times in one application run. Please don't pay attention to quite simplified window creation. This particular window is not meant to be visible on screen. It is created solely for purpose of receiving messages from another application. This is highlighted by using HWND_MESSAGE value of hWndParent parameter when calling CreateWindowEx function.
By creating, running and destroying thread in the above example again and again I have found out that two methods needs to be called after quitting from message loop. First is DestroyWindow and second is UnregisterClass. In regular application DestroyWindow function should be called in WM_CLOSE handler after the user confirms that he really wants to close the application. DestroyWindow function then sends WM_DESTROY and WM_NCDESTROY messages to window. In response to WM_DESTROY message application should call PostQuitMessage(0) function which results to immediately quitting message loop. So this part of code is not neccessary in all scenarios. I needed to call DestroyWindow function explicitly because I exit message loop by simply sending WM_QUIT message. When not doing it sometimes I received error 1412 (ERROR_CLASS_HAS_WINDOWS) when trying to unregister window class.
if (hwnd != NULL)
{
ret = DestroyWindow(hwnd);
if (ret == 0)
{
str.printf(L"Window destroying failed (GetLastError = %d)!", GetLastError());
ShowError(str);
}
hwnd = NULL;
}
ret = UnregisterClass(class_name.c_str(), wc.hInstance);
if (ret == 0)
{
str.printf(L"Window class unregistration failed (GetLastError = %d)!", GetLastError());
ShowError(str);
}
You must be very careful with resources in Win32. Make sure you've looked at the documentation carefully to determine what Windows itself will unload for you and what you have to unload yourself.
For examples, HWNDs will get destroyed as parent HWNDs get destroyed.
The best trick is to attempt to unload everything you've personally created. If you get an error returned from a particular function on unload, you likely should not unload it as it's been unloaded by Windows or some related resource.
It is important not to unload things when it is not necessary, as that could cause a crash.
So for example, an icon created with a window that's directly from a resource likely shouldn't be unloaded. But an HBITMAP you create that you draw to other windows, should most definitely be unloaded.
Your section question can be determined by a quick test with breakpoints. I do not know off the top of my head.