C++ -Win32 視窗程式設計 和小技巧
環境
windows 11 64bit
Visual Studio 2022
現在好像比較少有 Win32 視窗程式 這種需求了,不過我每次換電腦時,還是會試試看能不能用,對我來說算是視窗版的 Hello World 吧!
最簡單的 win32 視窗程式
main.cpp
#include <stdio.h>
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
WNDCLASSEX wndclass = { sizeof(wndclass),
CS_HREDRAW | CS_VREDRAW, //style
WndProc,
0, 0, hInstance,
LoadIcon(NULL, IDI_APPLICATION), //hIcon
LoadCursor(NULL,IDC_ARROW), //hCursor
(HBRUSH)(COLOR_WINDOW + 1), //hbrBackground
NULL, //hMenu
L"window",
LoadIcon(NULL, IDI_APPLICATION) }; //hIconSm
RegisterClassEx(&wndclass);
HWND hWnd = CreateWindowEx(
0, //dwExStyle
L"window", L"window", //視窗標題
WS_OVERLAPPEDWINDOW, //dwStyle
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
switch (msg)
{
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
小技巧 1:在 Win32 視窗後面出現 console
想起一開始學習視窗程式時,遇到非常多的困難,又很難 debug,因為沒有辦法使用 printf 或 cout 這種簡單的方式把值印出來看。後來找到這種方式,可以在 Win32 視窗後面出現 console,就可以使用 printf 或 cout 來 debug 了。
#pragma comment(linker, "/subsystem:console /entry:WinMainCRTStartup")
#include <stdio.h>
#include <windows.h>
//可以在視窗後面顯示一個 console 方便除錯
#pragma comment(linker, "/subsystem:console /entry:WinMainCRTStartup")
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
WNDCLASSEX wndclass = { sizeof(wndclass),
CS_HREDRAW | CS_VREDRAW, //style
WndProc,
0, 0, hInstance,
LoadIcon(NULL, IDI_APPLICATION), //hIcon
LoadCursor(NULL,IDC_ARROW), //hCursor
(HBRUSH)(COLOR_WINDOW + 1), //hbrBackground
NULL, //hMenu
L"window",
LoadIcon(NULL, IDI_APPLICATION) }; //hIconSm
RegisterClassEx(&wndclass);
HWND hWnd = CreateWindowEx(
0, //dwExStyle
L"window", L"window", //視窗標題
WS_OVERLAPPEDWINDOW, //dwStyle
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
printf("Hello World!\n");
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
switch (msg)
{
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
小技巧 2:使用命令列參數
沒有 int main() 了,那要怎麼拿 argc 和 argv 呢?沒關係,使用 __argc 和 __argv 就可以囉!
#include <stdio.h>
#include <windows.h>
//可以在視窗後面顯示一個 console 方便除錯
#pragma comment(linker, "/subsystem:console /entry:WinMainCRTStartup")
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
WNDCLASSEX wndclass = { sizeof(wndclass),
CS_HREDRAW | CS_VREDRAW, //style
WndProc,
0, 0, hInstance,
LoadIcon(NULL, IDI_APPLICATION), //hIcon
LoadCursor(NULL,IDC_ARROW), //hCursor
(HBRUSH)(COLOR_WINDOW + 1), //hbrBackground
NULL, //hMenu
L"window",
LoadIcon(NULL, IDI_APPLICATION) }; //hIconSm
RegisterClassEx(&wndclass);
HWND hWnd = CreateWindowEx(
0, //dwExStyle
L"window", L"window", //視窗標題
WS_OVERLAPPEDWINDOW, //dwStyle
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
printf("Hello World!\n");
printf("argc = %d\n", __argc);
if (__argc >= 3)
{
printf("argv1 = %s\n", __argv[1]);
printf("argv2 = %s\n", __argv[2]);
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
switch (msg)
{
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
PS.小問題:win32 的程式進入點 WinMain 函式會出現這個警告
警告 C28251 WinMain’ 的註釋不一致: 這個執行個體具有 沒有註釋。請參閱 c:\program files (x86)\windows kits\10\include\10.0.22000.0\um\winbase.h(1006)。 |
將原本的
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
改成
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)