C++ -Win32 視窗程式設計

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)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *