[X]
or Alt+F4
), the application does not terminate completely—it remains in the Task Manager's process list.WndProc
function and simply return DefWindowProc()
, the issue persists. The program lingers in the background, and I have to manually kill it from the Task Manager.#include <Windows.h>
#include <stdio.h>
#include <gl/GL.h>
#include <iostream>
const char *GREEN = "\x1b[38;2;45;200;120m";
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void GeneratePixelFormat(HDC &dc);
void SettingUpConsole();
void SetupRC(HDC hDC, GLuint nFontList);
void RenderScene(GLuint nFontList);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdArg, int nCmd)
{
// SettingUpConsole();
WNDCLASS wc = {0};
wc.hInstance = hInstance;
wc.lpszClassName = "Window Class";
// ? CS_OWNDC is required in OpenGL because some old drivers are not very stable without this (check page 656 to 660 in OpenGL SuperBbile)
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(248, 248, 255));
wc.cbWndExtra = 0;
wc.cbClsExtra = 0;
if (!RegisterClass(&wc))
return 0;
HWND WindowHandle = CreateWindowExA(
0, wc.lpszClassName, "Custom Window",
// ! When making window for OpenGL it is required to set clipchildren and siblings
// ? If not used then opengl may draw onto sibling or children window also
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1000, 460,
NULL, NULL, 0, NULL);
if (!WindowHandle)
{
return 0;
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// printf("Running message loop.\n");
}
printf("\x1b[38;2;120mExiting Loop.");
return 0;
}
LRESULT CALLBACK WndProc(HWND WindowHandle, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HDC dc = NULL;
static HGLRC rc = NULL;
static GLuint nFontList;
switch (uMsg)
{
case WM_CREATE:
{
printf("%sGetting window's handle\n", GREEN);
dc = GetDC(WindowHandle);
printf("%sSetting pixel format\n", GREEN);
GeneratePixelFormat(dc);
printf("%sCreating rendering context...\n", GREEN);
rc = wglCreateContext(dc);
printf("%sSetting current context.\n", GREEN);
wglMakeCurrent(dc, rc);
printf("%sSending message 30 times per second (30fps)\n", GREEN);
SetTimer(WindowHandle, 101, 33, NULL);
SetupRC(dc, nFontList);
printf("\x1b[38;2;150;150;150m\n");
return 0;
}
case WM_TIMER:
{
InvalidateRect(WindowHandle, NULL, FALSE);
printf("MESSAGE SENT\n");
return 0;
}
case WM_CLOSE:
{
printf("WM_CLOSE received. Destroying window...\n");
ReleaseDC(WindowHandle, dc);
wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
DestroyWindow(WindowHandle);
return 0;
}
case WM_DESTROY:
{
printf("WM_DESTROY received. Posting quit message...\n");
PostQuitMessage(0);
KillTimer(WindowHandle, 101);
// ! Do not remove ExitProcess T_T U_U
// ? Don't know why but closing the window don't close the process in the task manager
// ? Therefore using ExitProcess(0) to force process to stop when wm_destory message received.
// ExitProcess(0);
return 0;
}
case WM_SIZE:
{
RenderScene(nFontList);
SwapBuffers(dc);
ValidateRect(WindowHandle, NULL);
return 0;
}
case WM_PAINT:
{
RenderScene(nFontList);
SwapBuffers(dc); // Ensure the rendered scene is displayed
ValidateRect(WindowHandle, NULL);
return 0;
}
default:
return DefWindowProc(WindowHandle, uMsg, wParam, lParam);
}
}
void SettingUpConsole()
{
AllocConsole();
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
// Set console to ASCII mode
SetConsoleOutputCP(CP_ACP);
// Enable virtual terminal processing
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD consoleMode;
GetConsoleMode(hConsole, &consoleMode);
consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hConsole, consoleMode);
}
void GeneratePixelFormat(HDC &dc)
{
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure
1, // Version of this structure
PFD_DRAW_TO_WINDOW | // Draw to window (not to bitmap)
PFD_SUPPORT_OPENGL | // Support OpenGL calls in window
PFD_DOUBLEBUFFER, // Double buffered mode
PFD_TYPE_RGBA, // RGBA color mode
32, // Want 32-bit color
0, 0, 0, 0, 0, 0, // Not used to select mode
0, 0, // Not used to select mode
0, 0, 0, 0, 0, // Not used to select mode
16, // Size of depth buffer
0, // No stencil
0, // No auxiliary buffers
0, // Obsolete or reserved
0, // No overlay and underlay planes
0, // Obsolete or reserved layer mask
0, // No transparent color for underlay plane
0}; // Obsolete
int nPixelFormat = ChoosePixelFormat(dc, &pfd);
SetPixelFormat(dc, nPixelFormat, &pfd);
}
void SetupRC(HDC hDC, GLuint nFontList)
{
// Set up the font characteristics
HFONT hFont;
GLYPHMETRICSFLOAT agmf[128]; // Throw away
LOGFONT logfont;
logfont.lfHeight = -10;
logfont.lfWidth = 0;
logfont.lfEscapement = 0;
logfont.lfOrientation = 0;
logfont.lfWeight = FW_BOLD;
logfont.lfItalic = FALSE;
logfont.lfUnderline = FALSE;
logfont.lfStrikeOut = FALSE;
logfont.lfCharSet = ANSI_CHARSET;
logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logfont.lfQuality = DEFAULT_QUALITY;
logfont.lfPitchAndFamily = DEFAULT_PITCH;
strcpy(logfont.lfFaceName, "Arial");
// Create the font and display list
hFont = CreateFontIndirect(&logfont);
SelectObject(hDC, hFont);
// Create display lists for glyphs 0 through 128 with 0.1 extrusion
// and default deviation. The display list numbering starts at 1000
// (it could be any number).
nFontList = glGenLists(128);
wglUseFontOutlines(hDC, 0, 128, nFontList, 0.0f, 0.5f,
WGL_FONT_POLYGONS, agmf);
DeleteObject(hFont);
}
void RenderScene(GLuint nFontList)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Blue 3D text
glColor3ub(0, 0, 255);
glPushMatrix();
glListBase(nFontList);
glCallLists(6, GL_UNSIGNED_BYTE, "SDFSDF");
glPopMatrix();
}
# My Folders
include_folder = ./include
lib_folder = ./lib
# Libraries to include
my_libraries = User32.lib Gdi32.lib ucrt.lib kernel32.lib vcruntime.lib msvcrt.lib opengl32.lib
# INFO(Include Folders)
# Windows Kit Include Folders
kit_include_1 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/winrt"
kit_include_2 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/ucrt"
kit_include_3 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/um"
kit_include_4 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared"
kit_include_5 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/cppwinrt"
# Windows Kit Lib Folders
kit_lib_1 = "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64"
kit_lib_2 = "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/ucrt/x64"
# MSVC Include Folders
msvc_include = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/include"
msvc_atlmfc_include = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/atlmfc/include"
# MSVC Lib Folders
msvc_lib = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/lib/x64"
msvc_atlmfc_lib = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/atlmfc/lib/x64"
# INFO(Combined Include Folders)
include_folders = /I$(msvc_include) /I$(msvc_atlmfc_include) /I$(kit_include_1) /I$(kit_include_2) /I$(kit_include_3) /I$(kit_include_4) /I$(kit_include_5) /I$(include_folder) /I"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared"
#INFO(Combined Lib Folders)
lib_folders = /LIBPATH:$(msvc_lib) /LIBPATH:$(msvc_atlmfc_lib) /LIBPATH:$(kit_lib_1) /LIBPATH:$(kit_lib_2) /LIBPATH:$(lib_folder) $(my_libraries)
# When using windows kit 10.0.19041.0 i.e. Win32
build:
cl main.cpp ${include_folders} /EHsc /link ${lib_folders} /OUT:./bin/program.exe /SUBSYSTEM:WINDOWS /NODEFAULTLIB /ENTRY:WinMain
del *.obj
# ? Testing if I can use my custom made Logger.dll if I use g++ rather than msvc, because I was not able to use Logger.dll with msvc
# building with g++ without -mwindows flag
# -mwindows flag is used to hide the console window
# mark the entry point as WinMain
buildGPP:
g++ main.cpp -o ./bin/program.exe -I$(include_folder) -L$(lib_folder) -L./bin -lUser32 -lGdi32 -lucrt -lkernel32 -lLogger
DefWindowProc()
in WndProc, but the process still remains.WM_CLOSE
calls DestroyWindow()
, and WM_DESTROY
calls PostQuitMessage(0)
.printf("Exiting main loop."))
, which does print, meaning the loop terminates but the process does not.WinMain
the program is not able to close properly, but not specifying the flag does it. So is the problem related to compiler, I am using MSVC 2022 version. Answer from @tommybee and hints from @Mippy, @Red.Wave, @Mantee.Pink suggested to use console as starting point, and that also does help, but then what is problem with WinMain?[X]
or Alt+F4
), the application does not terminate completely—it remains in the Task Manager's process list.WndProc
function and simply return DefWindowProc()
, the issue persists. The program lingers in the background, and I have to manually kill it from the Task Manager.#include <Windows.h>
#include <stdio.h>
#include <gl/GL.h>
#include <iostream>
const char *GREEN = "\x1b[38;2;45;200;120m";
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void GeneratePixelFormat(HDC &dc);
void SettingUpConsole();
void SetupRC(HDC hDC, GLuint nFontList);
void RenderScene(GLuint nFontList);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdArg, int nCmd)
{
// SettingUpConsole();
WNDCLASS wc = {0};
wc.hInstance = hInstance;
wc.lpszClassName = "Window Class";
// ? CS_OWNDC is required in OpenGL because some old drivers are not very stable without this (check page 656 to 660 in OpenGL SuperBbile)
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(248, 248, 255));
wc.cbWndExtra = 0;
wc.cbClsExtra = 0;
if (!RegisterClass(&wc))
return 0;
HWND WindowHandle = CreateWindowExA(
0, wc.lpszClassName, "Custom Window",
// ! When making window for OpenGL it is required to set clipchildren and siblings
// ? If not used then opengl may draw onto sibling or children window also
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1000, 460,
NULL, NULL, 0, NULL);
if (!WindowHandle)
{
return 0;
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// printf("Running message loop.\n");
}
printf("\x1b[38;2;120mExiting Loop.");
return 0;
}
LRESULT CALLBACK WndProc(HWND WindowHandle, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HDC dc = NULL;
static HGLRC rc = NULL;
static GLuint nFontList;
switch (uMsg)
{
case WM_CREATE:
{
printf("%sGetting window's handle\n", GREEN);
dc = GetDC(WindowHandle);
printf("%sSetting pixel format\n", GREEN);
GeneratePixelFormat(dc);
printf("%sCreating rendering context...\n", GREEN);
rc = wglCreateContext(dc);
printf("%sSetting current context.\n", GREEN);
wglMakeCurrent(dc, rc);
printf("%sSending message 30 times per second (30fps)\n", GREEN);
SetTimer(WindowHandle, 101, 33, NULL);
SetupRC(dc, nFontList);
printf("\x1b[38;2;150;150;150m\n");
return 0;
}
case WM_TIMER:
{
InvalidateRect(WindowHandle, NULL, FALSE);
printf("MESSAGE SENT\n");
return 0;
}
case WM_CLOSE:
{
printf("WM_CLOSE received. Destroying window...\n");
ReleaseDC(WindowHandle, dc);
wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
DestroyWindow(WindowHandle);
return 0;
}
case WM_DESTROY:
{
printf("WM_DESTROY received. Posting quit message...\n");
PostQuitMessage(0);
KillTimer(WindowHandle, 101);
// ! Do not remove ExitProcess T_T U_U
// ? Don't know why but closing the window don't close the process in the task manager
// ? Therefore using ExitProcess(0) to force process to stop when wm_destory message received.
// ExitProcess(0);
return 0;
}
case WM_SIZE:
{
RenderScene(nFontList);
SwapBuffers(dc);
ValidateRect(WindowHandle, NULL);
return 0;
}
case WM_PAINT:
{
RenderScene(nFontList);
SwapBuffers(dc); // Ensure the rendered scene is displayed
ValidateRect(WindowHandle, NULL);
return 0;
}
default:
return DefWindowProc(WindowHandle, uMsg, wParam, lParam);
}
}
void SettingUpConsole()
{
AllocConsole();
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
// Set console to ASCII mode
SetConsoleOutputCP(CP_ACP);
// Enable virtual terminal processing
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD consoleMode;
GetConsoleMode(hConsole, &consoleMode);
consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hConsole, consoleMode);
}
void GeneratePixelFormat(HDC &dc)
{
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure
1, // Version of this structure
PFD_DRAW_TO_WINDOW | // Draw to window (not to bitmap)
PFD_SUPPORT_OPENGL | // Support OpenGL calls in window
PFD_DOUBLEBUFFER, // Double buffered mode
PFD_TYPE_RGBA, // RGBA color mode
32, // Want 32-bit color
0, 0, 0, 0, 0, 0, // Not used to select mode
0, 0, // Not used to select mode
0, 0, 0, 0, 0, // Not used to select mode
16, // Size of depth buffer
0, // No stencil
0, // No auxiliary buffers
0, // Obsolete or reserved
0, // No overlay and underlay planes
0, // Obsolete or reserved layer mask
0, // No transparent color for underlay plane
0}; // Obsolete
int nPixelFormat = ChoosePixelFormat(dc, &pfd);
SetPixelFormat(dc, nPixelFormat, &pfd);
}
void SetupRC(HDC hDC, GLuint nFontList)
{
// Set up the font characteristics
HFONT hFont;
GLYPHMETRICSFLOAT agmf[128]; // Throw away
LOGFONT logfont;
logfont.lfHeight = -10;
logfont.lfWidth = 0;
logfont.lfEscapement = 0;
logfont.lfOrientation = 0;
logfont.lfWeight = FW_BOLD;
logfont.lfItalic = FALSE;
logfont.lfUnderline = FALSE;
logfont.lfStrikeOut = FALSE;
logfont.lfCharSet = ANSI_CHARSET;
logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logfont.lfQuality = DEFAULT_QUALITY;
logfont.lfPitchAndFamily = DEFAULT_PITCH;
strcpy(logfont.lfFaceName, "Arial");
// Create the font and display list
hFont = CreateFontIndirect(&logfont);
SelectObject(hDC, hFont);
// Create display lists for glyphs 0 through 128 with 0.1 extrusion
// and default deviation. The display list numbering starts at 1000
// (it could be any number).
nFontList = glGenLists(128);
wglUseFontOutlines(hDC, 0, 128, nFontList, 0.0f, 0.5f,
WGL_FONT_POLYGONS, agmf);
DeleteObject(hFont);
}
void RenderScene(GLuint nFontList)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Blue 3D text
glColor3ub(0, 0, 255);
glPushMatrix();
glListBase(nFontList);
glCallLists(6, GL_UNSIGNED_BYTE, "SDFSDF");
glPopMatrix();
}
# My Folders
include_folder = ./include
lib_folder = ./lib
# Libraries to include
my_libraries = User32.lib Gdi32.lib ucrt.lib kernel32.lib vcruntime.lib msvcrt.lib opengl32.lib
# INFO(Include Folders)
# Windows Kit Include Folders
kit_include_1 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/winrt"
kit_include_2 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/ucrt"
kit_include_3 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/um"
kit_include_4 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared"
kit_include_5 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/cppwinrt"
# Windows Kit Lib Folders
kit_lib_1 = "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64"
kit_lib_2 = "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/ucrt/x64"
# MSVC Include Folders
msvc_include = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/include"
msvc_atlmfc_include = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/atlmfc/include"
# MSVC Lib Folders
msvc_lib = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/lib/x64"
msvc_atlmfc_lib = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/atlmfc/lib/x64"
# INFO(Combined Include Folders)
include_folders = /I$(msvc_include) /I$(msvc_atlmfc_include) /I$(kit_include_1) /I$(kit_include_2) /I$(kit_include_3) /I$(kit_include_4) /I$(kit_include_5) /I$(include_folder) /I"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared"
#INFO(Combined Lib Folders)
lib_folders = /LIBPATH:$(msvc_lib) /LIBPATH:$(msvc_atlmfc_lib) /LIBPATH:$(kit_lib_1) /LIBPATH:$(kit_lib_2) /LIBPATH:$(lib_folder) $(my_libraries)
# When using windows kit 10.0.19041.0 i.e. Win32
build:
cl main.cpp ${include_folders} /EHsc /link ${lib_folders} /OUT:./bin/program.exe /SUBSYSTEM:WINDOWS /NODEFAULTLIB /ENTRY:WinMain
del *.obj
# ? Testing if I can use my custom made Logger.dll if I use g++ rather than msvc, because I was not able to use Logger.dll with msvc
# building with g++ without -mwindows flag
# -mwindows flag is used to hide the console window
# mark the entry point as WinMain
buildGPP:
g++ main.cpp -o ./bin/program.exe -I$(include_folder) -L$(lib_folder) -L./bin -lUser32 -lGdi32 -lucrt -lkernel32 -lLogger
DefWindowProc()
in WndProc, but the process still remains.WM_CLOSE
calls DestroyWindow()
, and WM_DESTROY
calls PostQuitMessage(0)
.printf("Exiting main loop."))
, which does print, meaning the loop terminates but the process does not.WinMain
the program is not able to close properly, but not specifying the flag does it. So is the problem related to compiler, I am using MSVC 2022 version. Answer from @tommybee and hints from @Mippy, @Red.Wave, @Mantee.Pink suggested to use console as starting point, and that also does help, but then what is problem with WinMain?The issue isn't in the code. It is a valid C++ Windows program that terminates when control leaves the WinMain
function. The problem is that the linker command line opts out of C++ language rules by supplying a custom entry point.
The problem can be reproduced with the following program:
#include <Windows.h>
DWORD WINAPI ThreadProc(LPVOID)
{
::Sleep(INFINITE);
return 0;
}
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
::CreateThread(nullptr, 0, ThreadProc, nullptr, 0, nullptr);
return 0;
}
The primary thread creates a thread that sleeps indefinitely and returns immediately. Per C and C++ language rules returning from the entry point has the effect of terminating the program.
This happens when compiling the program using the following command line:
cl main.cpp /EHsc /link kernel32.lib /OUT:./bin/program.exe /SUBSYSTEM:WINDOWS
If we instead set a custom entry point via the /ENTRY
linker option, the program will not terminate when the primary thread returns from WinMain
:
cl main.cpp /EHsc /link kernel32.lib /OUT:./bin/program.exe /SUBSYSTEM:WINDOWS /NODEFAULTLIB /ENTRY:WinMain
Launching program.exe now keeps the process alive indefinitely. If you attach a debugger, you will see that the process has a single thread that is sleeping (ThreadProc
).
When setting a custom entry point rather than letting the linker choose the default WinMainCRTStartup
the rules for process termination change: A process terminates when a thread calls ExitProcess
explicitly or when all threads have terminated. Raymond Chen explains this in his blog entry If you return from the main thread, does the process exit? in more detail.
While calling ExitProcess
from the custom WinMain
entry point would address the immediate issue, it is not a practical solution. The language support libraries don't just terminate a process for us. They are responsible for a lot of things (such as initializing objects with static storage duration). The real solution is to drop the /ENTRY
linker option and let the linker choose the default entry point.
I am trying to think about this problem from a different perspective. If I want the entry point to be 'WinMain' in the linker command, defined as:
cl main.cpp $(include_folders) /EHsc /link $(lib_folders) /OUT:program_$(my_arch).exe /SUBSYSTEM:WINDOWS /NODEFAULTLIB /ENTRY:WinMain
This means I want a custom WinMain function without CRT initialization. So, I write the code with WinMain as the entry point, like this:
extern "C" {
void __cdecl _CRT_INIT(void);
}
void __stdcall WinMain() {
_CRT_INIT();
int exitCode = WinMain(GetModuleHandle(NULL), NULL, GetCommandLineA(), SW_SHOWDEFAULT);
exit(exitCode);
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdArg, int nCmd)
{
... whatever code is ...
}
However, I should encounter a compilation issue like this:
enter code here`error C2731: 'WinMain': You can not overload
So, adjust the code with a few changes:
extern "C" {
void __cdecl _CRT_INIT(void);
}
void __stdcall WinMainEntry() {
_CRT_INIT();
int exitCode = WinMain(GetModuleHandle(NULL), NULL, GetCommandLineA(), SW_SHOWDEFAULT);
exit(exitCode);
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdArg, int nCmd)
{
... whatever code is ...
}
With link command:
cl main.cpp $(include_folders) /EHsc /link $(lib_folders) /OUT:program_$(my_arch).exe /SUBSYSTEM:WINDOWS /NODEFAULTLIB /ENTRY:WinMainEntry
This program runs without any issues. I made a few changes to the code:
void __stdcall WinMainEntry() {
//_CRT_INIT();
int exitCode = WinMain(GetModuleHandle(NULL), NULL, GetCommandLineA(), SW_SHOWDEFAULT);
//exit(exitCode);
}
Then, the output is very similar to yours.
So, I think the problem is that you overloaded the WinMain function. I don't encounter any issues when there's no Entry. The linker command is as follows:
cl main.cpp $(include_folders) /link $(lib_folders) /OUT:program_$(my_arch).exe /SUBSYSTEM:WINDOWS /NODEFAULTLIB /ENTRY:WinMainEntry
And the full source is :
#include <Windows.h>
#include <stdio.h>
#include <gl/GL.h>
#include <iostream>
const char *GREEN = "\x1b[38;2;45;200;120m";
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void GeneratePixelFormat(HDC &dc);
void SettingUpConsole();
void SetupRC(HDC hDC, GLuint nFontList);
void RenderScene(GLuint nFontList);
//extern "C" {
// void __cdecl _CRT_INIT(void);
//}
//void __stdcall WinMainEntry() {
//_CRT_INIT();
// int exitCode = WinMain(GetModuleHandle(NULL), NULL, GetCommandLineA(), SW_SHOWDEFAULT);
//exit(exitCode);
//}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdArg, int nCmd)
{
SettingUpConsole();
WNDCLASS wc = {0};
wc.hInstance = hInstance;
wc.lpszClassName = "Window Class";
// ? CS_OWNDC is required in OpenGL because some old drivers are not very stable without this (check page 656 to 660 in OpenGL SuperBbile)
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(248, 248, 255));
wc.cbWndExtra = 0;
wc.cbClsExtra = 0;
if (!RegisterClass(&wc))
return 0;
HWND WindowHandle = CreateWindowExA(
0, wc.lpszClassName, "Custom Window",
// ! When making window for OpenGL it is required to set clipchildren and siblings
// ? If not used then opengl may draw onto sibling or children window also
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1000, 460,
NULL, NULL, 0, NULL);
if (!WindowHandle)
{
return 0;
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// printf("Running message loop.\n");
}
printf("\x1b[38;2;120mExiting Loop.");
return 0;
}
LRESULT CALLBACK WndProc(HWND WindowHandle, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HDC dc = NULL;
static HGLRC rc = NULL;
static GLuint nFontList;
switch (uMsg)
{
case WM_CREATE:
{
printf("%sGetting window's handle\n", GREEN);
dc = GetDC(WindowHandle);
printf("%sSetting pixel format\n", GREEN);
GeneratePixelFormat(dc);
printf("%sCreating rendering context...\n", GREEN);
rc = wglCreateContext(dc);
printf("%sSetting current context.\n", GREEN);
wglMakeCurrent(dc, rc);
printf("%sSending message 30 times per second (30fps)\n", GREEN);
SetTimer(WindowHandle, 101, 33, NULL);
SetupRC(dc, nFontList);
printf("\x1b[38;2;150;150;150m\n");
return 0;
}
case WM_TIMER:
{
InvalidateRect(WindowHandle, NULL, FALSE);
//printf("MESSAGE SENT\n");
return 0;
}
case WM_CLOSE:
{
printf("WM_CLOSE received. Destroying window...\n");
ReleaseDC(WindowHandle, dc);
wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
DestroyWindow(WindowHandle);
return 0;
}
case WM_DESTROY:
{
printf("WM_DESTROY received. Posting quit message...\n");
PostQuitMessage(0);
KillTimer(WindowHandle, 101);
// ! Do not remove ExitProcess T_T U_U
// ? Don't know why but closing the window don't close the process in the task manager
// ? Therefore using ExitProcess(0) to force process to stop when wm_destory message received.
// ExitProcess(0);
return 0;
}
case WM_SIZE:
{
RenderScene(nFontList);
SwapBuffers(dc);
ValidateRect(WindowHandle, NULL);
return 0;
}
case WM_PAINT:
{
RenderScene(nFontList);
SwapBuffers(dc); // Ensure the rendered scene is displayed
ValidateRect(WindowHandle, NULL);
return 0;
}
default:
return DefWindowProc(WindowHandle, uMsg, wParam, lParam);
}
}
void SettingUpConsole()
{
AllocConsole();
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
// Set console to ASCII mode
SetConsoleOutputCP(CP_ACP);
// Enable virtual terminal processing
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD consoleMode;
GetConsoleMode(hConsole, &consoleMode);
consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hConsole, consoleMode);
}
void GeneratePixelFormat(HDC &dc)
{
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure
1, // Version of this structure
PFD_DRAW_TO_WINDOW | // Draw to window (not to bitmap)
PFD_SUPPORT_OPENGL | // Support OpenGL calls in window
PFD_DOUBLEBUFFER, // Double buffered mode
PFD_TYPE_RGBA, // RGBA color mode
32, // Want 32-bit color
0, 0, 0, 0, 0, 0, // Not used to select mode
0, 0, // Not used to select mode
0, 0, 0, 0, 0, // Not used to select mode
16, // Size of depth buffer
0, // No stencil
0, // No auxiliary buffers
0, // Obsolete or reserved
0, // No overlay and underlay planes
0, // Obsolete or reserved layer mask
0, // No transparent color for underlay plane
0}; // Obsolete
int nPixelFormat = ChoosePixelFormat(dc, &pfd);
SetPixelFormat(dc, nPixelFormat, &pfd);
}
void SetupRC(HDC hDC, GLuint nFontList)
{
// Set up the font characteristics
HFONT hFont;
GLYPHMETRICSFLOAT agmf[128]; // Throw away
LOGFONT logfont;
logfont.lfHeight = -10;
logfont.lfWidth = 0;
logfont.lfEscapement = 0;
logfont.lfOrientation = 0;
logfont.lfWeight = FW_BOLD;
logfont.lfItalic = FALSE;
logfont.lfUnderline = FALSE;
logfont.lfStrikeOut = FALSE;
logfont.lfCharSet = ANSI_CHARSET;
logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logfont.lfQuality = DEFAULT_QUALITY;
logfont.lfPitchAndFamily = DEFAULT_PITCH;
strcpy(logfont.lfFaceName, "Arial");
// Create the font and display list
hFont = CreateFontIndirect(&logfont);
SelectObject(hDC, hFont);
// Create display lists for glyphs 0 through 128 with 0.1 extrusion
// and default deviation. The display list numbering starts at 1000
// (it could be any number).
nFontList = glGenLists(128);
wglUseFontOutlines(hDC, 0, 128, nFontList, 0.0f, 0.5f,
WGL_FONT_POLYGONS, agmf);
DeleteObject(hFont);
}
void RenderScene(GLuint nFontList)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Blue 3D text
glColor3ub(0, 0, 255);
glPushMatrix();
glListBase(nFontList);
glCallLists(6, GL_UNSIGNED_BYTE, "SDFSDF");
glPopMatrix();
}
Here’s the code for console mode with a slight modification:
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <iostream>
const char *GREEN = "\x1b[38;2;45;200;120m";
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void GeneratePixelFormat(HDC &dc);
void SettingUpConsole();
void SetupRC(HDC hDC, GLuint nFontList);
void RenderScene(GLuint nFontList);
int main() {
SettingUpConsole();
WNDCLASS wc = {0};
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = "Window Class";
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(248, 248, 255));
if (!RegisterClass(&wc))
return 0;
HWND WindowHandle = CreateWindowExA(
0, wc.lpszClassName, "Custom Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, 1000, 460, NULL, NULL, GetModuleHandle(NULL), NULL);
if (!WindowHandle)
return 0;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
std::cout << "Exiting Loop." << std::endl;
//for pause
getchar();
return 0;
}
LRESULT CALLBACK WndProc(HWND WindowHandle, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HDC dc = NULL;
static HGLRC rc = NULL;
static GLuint nFontList;
switch (uMsg)
{
case WM_CREATE:
{
printf("%sGetting window's handle\n", GREEN);
dc = GetDC(WindowHandle);
printf("%sSetting pixel format\n", GREEN);
GeneratePixelFormat(dc);
printf("%sCreating rendering context...\n", GREEN);
rc = wglCreateContext(dc);
printf("%sSetting current context.\n", GREEN);
wglMakeCurrent(dc, rc);
printf("%sSending message 30 times per second (30fps)\n", GREEN);
SetTimer(WindowHandle, 101, 33, NULL);
SetupRC(dc, nFontList);
printf("\x1b[38;2;150;150;150m\n");
return 0;
}
case WM_TIMER:
{
InvalidateRect(WindowHandle, NULL, FALSE);
// printf("MESSAGE SENT\n");
return 0;
}
case WM_CLOSE:
{
printf("WM_CLOSE received. Destroying window...\n");
ReleaseDC(WindowHandle, dc);
wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
DestroyWindow(WindowHandle);
return 0;
}
case WM_DESTROY:
{
printf("WM_DESTROY received. Posting quit message...\n");
PostQuitMessage(0);
KillTimer(WindowHandle, 101);
// ! Do not remove ExitProcess T_T U_U
// ? Don't know why but closing the window don't close the process in the task manager
// ? Therefore using ExitProcess(0) to force process to stop when wm_destory message received.
// ExitProcess(0);
return 0;
}
case WM_SIZE:
{
RenderScene(nFontList);
SwapBuffers(dc);
ValidateRect(WindowHandle, NULL);
return 0;
}
case WM_PAINT:
{
RenderScene(nFontList);
SwapBuffers(dc); // Ensure the rendered scene is displayed
ValidateRect(WindowHandle, NULL);
return 0;
}
default:
return DefWindowProc(WindowHandle, uMsg, wParam, lParam);
}
}
void SettingUpConsole()
{
AllocConsole();
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
// Set console to ASCII mode
SetConsoleOutputCP(CP_ACP);
// Enable virtual terminal processing
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD consoleMode;
GetConsoleMode(hConsole, &consoleMode);
consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hConsole, consoleMode);
}
void GeneratePixelFormat(HDC &dc)
{
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure
1, // Version of this structure
PFD_DRAW_TO_WINDOW | // Draw to window (not to bitmap)
PFD_SUPPORT_OPENGL | // Support OpenGL calls in window
PFD_DOUBLEBUFFER, // Double buffered mode
PFD_TYPE_RGBA, // RGBA color mode
32, // Want 32-bit color
0, 0, 0, 0, 0, 0, // Not used to select mode
0, 0, // Not used to select mode
0, 0, 0, 0, 0, // Not used to select mode
16, // Size of depth buffer
0, // No stencil
0, // No auxiliary buffers
0, // Obsolete or reserved
0, // No overlay and underlay planes
0, // Obsolete or reserved layer mask
0, // No transparent color for underlay plane
0}; // Obsolete
int nPixelFormat = ChoosePixelFormat(dc, &pfd);
SetPixelFormat(dc, nPixelFormat, &pfd);
}
void SetupRC(HDC hDC, GLuint nFontList)
{
// Set up the font characteristics
HFONT hFont;
GLYPHMETRICSFLOAT agmf[128]; // Throw away
LOGFONT logfont;
logfont.lfHeight = -10;
logfont.lfWidth = 0;
logfont.lfEscapement = 0;
logfont.lfOrientation = 0;
logfont.lfWeight = FW_BOLD;
logfont.lfItalic = FALSE;
logfont.lfUnderline = FALSE;
logfont.lfStrikeOut = FALSE;
logfont.lfCharSet = ANSI_CHARSET;
logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logfont.lfQuality = DEFAULT_QUALITY;
logfont.lfPitchAndFamily = DEFAULT_PITCH;
strcpy(logfont.lfFaceName, "Arial");
// Create the font and display list
hFont = CreateFontIndirect(&logfont);
SelectObject(hDC, hFont);
// Create display lists for glyphs 0 through 128 with 0.1 extrusion
// and default deviation. The display list numbering starts at 1000
// (it could be any number).
nFontList = glGenLists(128);
wglUseFontOutlines(hDC, 0, 128, nFontList, 0.0f, 0.5f,
WGL_FONT_POLYGONS, agmf);
DeleteObject(hFont);
}
void RenderScene(GLuint nFontList)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Blue 3D text
glColor3ub(0, 0, 255);
glPushMatrix();
glListBase(nFontList);
glCallLists(6, GL_UNSIGNED_BYTE, "SDFSDF");
glPopMatrix();
}
Then, the Makefile will also need to be changed:
# My Folders
include_folder = ./include
lib_folder = ./lib
# Libraries to include
my_libraries = User32.lib Gdi32.lib ucrt.lib kernel32.lib vcruntime.lib msvcrt.lib opengl32.lib
!IF "$(my_arch)" == ""
my_arch = x86
!ENDIF
# Windows Kits Include Folders
kit_include_1 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/winrt"
kit_include_2 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/ucrt"
kit_include_3 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/um"
kit_include_4 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared"
kit_include_5 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/cppwinrt"
# Windows Kits Lib Folders
kit_lib_1 = "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/$(my_arch)"
kit_lib_2 = "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/ucrt/$(my_arch)"
# Combined Include Folders
include_folders = /I$(kit_include_1) /I$(kit_include_2) /I$(kit_include_3) /I$(kit_include_4) /I$(kit_include_5) /I$(include_folder)
# Combined Lib Folders
lib_folders = /LIBPATH:$(kit_lib_1) /LIBPATH:$(kit_lib_2) /LIBPATH:$(lib_folder) $(my_libraries)
# Build target
build:
cl main.cpp $(include_folders) /EHsc /link $(lib_folders) /OUT:program_$(my_arch).exe /SUBSYSTEM:CONSOLE /NODEFAULTLIB:LIBCMT
# Clean target
clean:
del *.obj
del program_$(my_arch).exe
To set up the development environment in MSVC 2022, follow these commands:
call cmd /K "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
nmake clean & nmake my_arch=x64
program_x64.exe
Now, I have an OpenGL test program with no issues. The results are as follows:
It is the WinMain version :
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
const char *GREEN = "\x1b[38;2;45;200;120m";
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void GeneratePixelFormat(HDC &dc);
void SetupRC(HDC hDC, GLuint nFontList);
void RenderScene(GLuint nFontList);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASS wc = {0};
wc.hInstance = hInstance;
wc.lpszClassName = "Window Class";
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(248, 248, 255));
if (!RegisterClass(&wc))
return 0;
HWND WindowHandle = CreateWindowExA(
0, wc.lpszClassName, "Custom Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, 1000, 460, NULL, NULL, hInstance, NULL);
if (!WindowHandle)
return 0;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
OutputDebugString("Exiting Loop.\n");
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND WindowHandle, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HDC dc = NULL;
static HGLRC rc = NULL;
static GLuint nFontList;
switch (uMsg)
{
case WM_CREATE:
{
OutputDebugString("Getting window's handle\n");
dc = GetDC(WindowHandle);
OutputDebugString("Setting pixel format\n");
GeneratePixelFormat(dc);
OutputDebugString("Creating rendering context...\n");
rc = wglCreateContext(dc);
OutputDebugString("Setting current context.\n");
wglMakeCurrent(dc, rc);
OutputDebugString("Sending message 30 times per second (30fps)\n");
SetTimer(WindowHandle, 101, 33, NULL);
SetupRC(dc, nFontList);
return 0;
}
case WM_TIMER:
{
InvalidateRect(WindowHandle, NULL, FALSE);
return 0;
}
case WM_CLOSE:
{
OutputDebugString("WM_CLOSE received. Destroying window...\n");
ReleaseDC(WindowHandle, dc);
wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
DestroyWindow(WindowHandle);
return 0;
}
case WM_DESTROY:
{
OutputDebugString("WM_DESTROY received. Posting quit message...\n");
PostQuitMessage(0);
KillTimer(WindowHandle, 101);
return 0;
}
case WM_SIZE:
{
RenderScene(nFontList);
SwapBuffers(dc);
ValidateRect(WindowHandle, NULL);
return 0;
}
case WM_PAINT:
{
RenderScene(nFontList);
SwapBuffers(dc);
ValidateRect(WindowHandle, NULL);
return 0;
}
default:
return DefWindowProc(WindowHandle, uMsg, wParam, lParam);
}
}
void GeneratePixelFormat(HDC &dc)
{
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32,
0, 0, 0, 0, 0, 0,
0, 0,
0, 0, 0, 0, 0,
16,
0,
0,
0,
0,
0,
0,
0
};
int nPixelFormat = ChoosePixelFormat(dc, &pfd);
SetPixelFormat(dc, nPixelFormat, &pfd);
}
void SetupRC(HDC hDC, GLuint nFontList)
{
HFONT hFont;
GLYPHMETRICSFLOAT agmf[128];
LOGFONT logfont;
logfont.lfHeight = -10;
logfont.lfWidth = 0;
logfont.lfEscapement = 0;
logfont.lfOrientation = 0;
logfont.lfWeight = FW_BOLD;
logfont.lfItalic = FALSE;
logfont.lfUnderline = FALSE;
logfont.lfStrikeOut = FALSE;
logfont.lfCharSet = ANSI_CHARSET;
logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logfont.lfQuality = DEFAULT_QUALITY;
logfont.lfPitchAndFamily = DEFAULT_PITCH;
strcpy(logfont.lfFaceName, "Arial");
hFont = CreateFontIndirect(&logfont);
SelectObject(hDC, hFont);
nFontList = glGenLists(128);
wglUseFontOutlines(hDC, 0, 128, nFontList, 0.0f, 0.5f,
WGL_FONT_POLYGONS, agmf);
DeleteObject(hFont);
}
void RenderScene(GLuint nFontList)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3ub(0, 0, 255);
glPushMatrix();
glListBase(nFontList);
glCallLists(6, GL_UNSIGNED_BYTE, "SDFSDF");
glPopMatrix();
}
The result is :
This is Makefile for me :
# My Folders
include_folder = ./include
lib_folder = ./lib
# Libraries to include
my_libraries = User32.lib Gdi32.lib ucrt.lib kernel32.lib vcruntime.lib msvcrt.lib opengl32.lib
!IF "$(my_arch)" == ""
my_arch = x86
!ENDIF
# Windows Kits Include Folders
kit_include_1 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/winrt"
kit_include_2 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/ucrt"
kit_include_3 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/um"
kit_include_4 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared"
kit_include_5 = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/cppwinrt"
# Windows Kits Lib Folders
kit_lib_1 = "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/$(my_arch)"
kit_lib_2 = "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/ucrt/$(my_arch)"
# Combined Include Folders
include_folders = /I$(kit_include_1) /I$(kit_include_2) /I$(kit_include_3) /I$(kit_include_4) /I$(kit_include_5) /I$(include_folder)
# Combined Lib Folders
lib_folders = /LIBPATH:$(kit_lib_1) /LIBPATH:$(kit_lib_2) /LIBPATH:$(lib_folder) $(my_libraries)
# Build target (exe file name: program_x86.exe or program_x64.exe)
build:
cl main_window_mode.cpp $(include_folders) /EHsc /link $(lib_folders) /OUT:program_$(my_arch).exe /SUBSYSTEM:WINDOWS /NODEFAULTLIB:LIBCMT
# Clean target
clean:
del *.obj
del program_$(my_arch).exe
Message Loop Should Handle WM_QUIT Properly,
Your message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GetMessage
returns 0 when WM_QUIT
is received, which exits the loop. This should normally cause WinMain to exit, terminating the process.
However, if another thread is keeping the process alive, it may never fully terminate.
Try modifying the message loop to log when WM_QUIT
is received:
while (GetMessage(&msg, NULL, 0, 0) > 0) // Ensure GetMessage doesn't return -1 (error)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
printf("Main loop exited. Process should terminate.\n");
If Main loop exited
. is printed but the process remains, something else is keeping it alive.
Also you can forcing Process Termination (ExitProcess)
As a last resort, you can call ExitProcess(0); in WM_DESTROY:
case WM_DESTROY:
{
printf("WM_DESTROY received. Posting quit message...\n");
KillTimer(WindowHandle, 101);
PostQuitMessage(0);
// Last-resort cleanup if the process is still alive
ExitProcess(0);
return 0;
}
This forces termination of all threads and resources.