【二】Windows API 零門檻編程指南——CreateWindow 窗口建立 「萬字長篇專業術語全解」

本系列博文幾乎沒有難啃的「專業術語」,儘可能讓讀者可以看明白文章所述內容,是本系列博文的核心宗旨之一。(因爲本人也是因爲項目須要,因此纔來查閱相關資料,文中出現的錯誤歡迎指出,共同進步!謝謝!windows

讀本系列博文的讀者必須具有如下的知識儲備:api

  • C/C++語言基礎語法及瞭解面向對象概念

窗口在 Windows 中指一個矩形區域,通常狀況下這個區域是用戶與應用程序交互的樞紐;上一小節使用 MessageBox 建立的簡單窗口也是與用戶交互的一個窗口,該窗口的功能有限,只可以簡單的展現一些想要表達的信息,想建立一個能表達更多信息的窗口,可使用 CreateWindow 函數建立。數據結構

開始建立

建立 Windows 桌面應用程序須要 windows.h,在頭部引入 windows.h 頭文件。app

#include <windows.h> 
複製代碼

WinMain

在C語言中,每一個C語言程序都有一個入口函數,在Windows桌面程序中,這個入口函數是 WinMain ,具體聲明以下:函數

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);
複製代碼

在程序中,緊接着在頭部文件後,咱們使用 WinMain做爲程序的入口函數:佈局

#include <windows.h> 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {}
複製代碼

寫好入口函數後,必需要使用 RegisterClassEx 註冊一個新的窗口類型,再使用 CreateWindow 進行建立。post

WNDCLASSEX

在註冊新窗口前,咱們可使用一個 WNDCLASSEX 結構用來描述建立的Windows,這是窗口類;微軟開發中心對WNDCLASSEXA的描述:「Contains window class information. It is used with the RegisterClassEx and GetClassInfoEx functions.」;WNDCLASSEXA 是包含窗口信息的結構。語法以下:學習

typedef struct tagWNDCLASSEXA {
  UINT      cbSize;
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCSTR    lpszMenuName;
  LPCSTR    lpszClassName;
  HICON     hIconSm;
} WNDCLASSEXA, *PWNDCLASSEXA, *NPWNDCLASSEXA, *LPWNDCLASSEXA;
複製代碼

結構成員:字體

  • cbSize 窗口的大小:爲 WNDCLASSEX 這個結構的字節數大小,賦值爲 sizeof(WNDCLASSEX)
  • style 窗口的風格:爲該窗口的樣式,取值爲 CS_HREDRAW | CS_VREDRAW
  • lpfnWndProc 窗口處理指針:爲指向窗體的的過程函數,爲指針,使用 WndProc 處理應用程序在發生事件時從 Windows 接收的消息,如下將會講解 WndProc
  • cbClsExtra 窗口類結構後的附加字節數,通常爲0
  • cbWndExtra 窗口事例後的附加字數,通常爲0
  • hInstance 當前實例句柄,直接把WinMain參數 hInstance(表示當前實例句柄) 賦值給 hInstance 便可
  • hIcon 圖標的句柄,暫時賦值爲NULL
  • hCursor 光標的句柄:使用 LoadCursor 加載光標,如下講解語法
  • lpszClassName: 類別名稱的指針賦值爲static TCHAR szWindowClass[] = _T("CSDN @1_bit");
  • hIconSm: 窗口類關聯的小圖標,使用 LoadIcon函數加載,不過在文檔中提示,這個函數已過期,可使用 LoadImage 函數加載,本篇使用的是 LoadIconLoadImage 後面再作補充;LoadIcon 函數語法將會在如下講解
  • hbrBackground 背景畫刷的句柄,將會在如下給出設置的值參考
  • lpszMenuName 指向菜單資源名的指針,爲NULL便可

代碼實現以下:ui

WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
	wcex.hCursor = LoadCursor(NULL, IDC_CROSS);
	wcex.hbrBackground = (HBRUSH)(COLOR_ACTIVECAPTION);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE(IDI_INFORMATION));
複製代碼

——————————————————————————————————

WNDCLASSEX hbrBackground

值參考:

在這裏插入圖片描述
——————————————————————————————————

LoadCursor

LoadCursor 返回類型爲 HCURSOR:的語法以下:

HCURSOR LoadCursorW( HINSTANCE hInstance, LPCWSTR lpCursorName );
複製代碼

參數說明:

  • hInstance :可賦值當前實例
  • lpCursorName:要加載的遊標資源的名稱

在微軟的參考文檔中說明,lpCursorName 的可設置爲如下值:

在這裏插入圖片描述
——————————————————————————————————

lpfnWndProc

lpfnWndProc 爲接收窗口處理的指針,使用 WndProc 處理應用程序在發生事件時從 Windows 接收的消息。在微軟的文檔中寫道:「WndProc 是每一個 Windows 桌面應用程序必須的窗口過程功能。 此函數一般命名爲WndProc,但您能夠爲所欲爲地命名它。 例如,若是用戶在應用程序中選擇"肯定"按鈕,Windows 會向您發送消息,您能夠在WndProc函數內編寫代碼,執行任何適當的操做。 這稱爲處理事件。 您只處理與應用程序相關的事件。WndProc 具備如下語法」;以下:。

LRESULT CALLBACK WndProc( _In_ HWND hWnd, _In_ UINT message, _In_ WPARAM wParam, _In_ LPARAM lParam );
複製代碼

那咱們在程序中聲明也如此聲明,那麼定義以下(使用微軟文檔示例):

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR greeting[] = _T("Hello, 我是CSDN 1_bit 博客主頁:https://me.csdn.net/A757291228 ");

	switch (message)
	{
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		TextOut(hdc,
			5, 5,
			greeting, _tcslen(greeting));
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
		break;
	}

	return 0;
}
複製代碼

在以上 WndProc 的實現中,使用了 switch 語句,在 switch 中判斷了 WM_PAINT 消息;WM_PAINT 消息爲繪製主窗體,在文檔中寫到:

要處理的一條重要信息是WM_PAINT消息。 當必須更新其顯示WM_PAINT窗口的一部分時,應用程序將接收消息。 當用戶在窗口前面移動窗口,而後再次將其移開時,可能會發生此事件。 您的應用程序不知道這些事件什麼時候發生。 只有 Windows 知道,所以它會經過消息WM_PAINT通知你的應用。 首次顯示窗口時,必須更新全部窗口。 要處理 WM_PAINT 消息,首先應調用 BeginPaint,而後處理全部的邏輯以在窗口中佈局文本、按鈕和其餘控件,而後調用 EndPaint。

——————————————————————————————————

BeginPaint

BeginPaint 的語法爲:

HDC BeginPaint( HWND hWnd, LPPAINTSTRUCT lpPaint );
複製代碼

參數說明:

  • HWND:處理要重繪的窗口
  • lpPaint:接收繪製的接收繪畫信息的 **PAINTSTRUCT**結構的指針

——————————————————————————————————

EndPaint

該調用EndPaint函數標記指定窗口畫的結束。每次調用BeginPaint函數都須要此函數,可是僅在繪製完成以後。

語法:

BOOL EndPaint( HWND hWnd, const PAINTSTRUCT *lpPaint );
複製代碼

參數說明:

  • hWnd:處理的窗口
  • lpPaint:指向PAINTSTRUCT結構的指針

——————————————————————————————————

PostQuitMessage

向系統指示線程已請求終止(退出)。一般用於響應WM_DESTROY消息。

語法:

void PostQuitMessage( int nExitCode );
複製代碼

參數說明:

  • nExitCode:應用程序退出代碼。此值用做WM_QUIT消息的wParam參數。

——————————————————————————————————

DefWindowProc

調用默認窗口過程覺得應用程序未處理的任何窗口消息提供默認處理。此功能確保處理全部消息。DefWindowProc用窗口過程接收到的相同參數調用。

語法:

LRESULT LRESULT DefWindowProcA( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
複製代碼

參數說明:

  • hWnd:窗口句柄
  • Msg:消息
  • wParam:附加消息
  • lParam:附加消息信息

——————————————————————————————————

TextOut

所述的TextOut函數在指定位置寫入的字符串,利用當前選擇的字體,背景顏色和文本顏色。

語法:

BOOL TextOutW( HDC hdc, int x, int y, LPCWSTR lpString, int c );
複製代碼

參數說明:

  • hdc:上下文句柄
  • x,y:對齊字符串的x,y座標
  • lpString:字符串指針,指向字符串
  • c:字符串長度

——————————————————————————————————

HDC

引用文檔解釋:

HDC代碼中是設備上下文的句柄,這是 Windows 用於使應用程序與圖形子系統通訊的數據結構。

WM_DESTROY

銷燬窗口時發送。從窗口中刪除窗口後,它將被髮送到銷燬窗口的窗口過程。 此消息首先發送到被銷燬的窗口,而後發送到被銷燬的子窗口(若是有)。在處理消息期間,能夠假定全部子窗口仍然存在。

WM_DESTROY 在 WndProc 函數中使用

——————————————————————————————————

補充

WM_CREATE

當應用程序經過調用CreateWindowEx或CreateWindow函數請求建立窗口時發送。(在函數返回以前發送消息。)在建立窗口以後,但在該窗口變爲可見以前,新窗口的窗口過程會收到此消息。

——————————————————————————————————

RegisterClassEx

以後註冊該窗口,使用 RegisterClassEx:

RegisterClassEx(&wcex);
複製代碼

註冊後使用 CreateWindow 進行註冊的窗口建立語法以下:

HWND CreateWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HANDLE hInstance, PVOID lpParam );
複製代碼

參數說明:

  • lpClassName:應用程序窗體名
  • lpWindowName:標題名
  • DWORD dwStyle:窗口類型風格
  • x,y:初始位置(x,y)
  • nWidth, nHeight:初始尺寸
  • hWndParent,:窗體父級,可爲NULL
  • hMenu,:菜單欄,可爲NULL
  • hInstance:當前實例
  • lpParam:應用程序使用,可爲NULL

建立窗體:

HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 1000, 1000, NULL, NULL, hInstance, NULL);
複製代碼

應用窗體名爲 szWindowClass

static TCHAR szWindowClass[] = _T("win32 Demo");
複製代碼

應用窗體名爲 szTitle

static TCHAR szTitle[] = _T("This Win32");
複製代碼

窗體風格類型爲:WS_OVERLAPPEDWINDOW 初始位置爲:CW_USEDEFAULT,默認左上角出現 尺寸爲:1000, 1000 父級及菜單欄都爲:NULL hInstance爲:當前實例 hInstance lpParam應用程序使用爲:NULL

代碼以下:

#include <windows.h> 
#include <tchar.h> 

static TCHAR szWindowClass[] = _T("CSDN @1_bit");
static TCHAR szTitle[] = _T("Win32 桌面應用程序");
HINSTANCE hInst;


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
	wcex.hCursor = LoadCursor(NULL, IDC_CROSS);
	wcex.hbrBackground = (HBRUSH)(COLOR_ACTIVECAPTION);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE(IDI_INFORMATION));

	RegisterClassEx(&wcex);

	hInst = hInstance;
	HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL);
	
	}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR greeting[] = _T("Hello, 我是CSDN 1_bit 博客主頁:https://me.csdn.net/A757291228 ");

	switch (message)
	{
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		TextOut(hdc,
			5, 5,
			greeting, _tcslen(greeting));
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
		break;
	}

	return 0;
}
複製代碼

——————————————————————————————————

ShowWindow

完成以上代碼後,還需使用 ShowWindow 讓Windows窗體指定如何顯示,代碼以下:

ShowWindow(hWnd, nCmdShow);
複製代碼

語法:

BOOL ShowWindow( HWND hWnd, int nCmdShow );
複製代碼

參數說明:

  • hWnd:窗口句柄
  • nCmdShow:窗口的顯示方式

nCmdShow 參考:

在這裏插入圖片描述
——————————————————————————————————

UpdateWindow

使用 UpdateWindow 發送 WM_PAINT 消息,更新指定窗口。 語法:

BOOL UpdateWindow( HWND hWnd );
複製代碼

參數:

  • hWnd:窗口句柄

總體代碼以下:

#include <windows.h> 
#include <tchar.h> 

static TCHAR szWindowClass[] = _T("CSDN @1_bit");
static TCHAR szTitle[] = _T("Win32 桌面應用程序");
HINSTANCE hInst;


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
	wcex.hCursor = LoadCursor(NULL, IDC_CROSS);
	wcex.hbrBackground = (HBRUSH)(COLOR_ACTIVECAPTION);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE(IDI_INFORMATION));

	RegisterClassEx(&wcex);

	hInst = hInstance;
	HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL);

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	return 0;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR greeting[] = _T("Hello, 我是CSDN 1_bit 博客主頁:https://me.csdn.net/A757291228 ");

	switch (message)
	{
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		TextOut(hdc,
			5, 5,
			greeting, _tcslen(greeting));
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
		break;
	}

	return 0;
}
複製代碼

運行程序,發現出現了一閃而過的窗口,這個很像剛學習C語言的時候,沒有加上中止;那咱們就循環偵聽 Windows 發送的消息便可:

MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
   TranslateMessage(&msg);
   DispatchMessage(&msg);
}

return (int) msg.wParam;
複製代碼

——————————————————————————————————

GetMessage

GetMessage

從調用線程的消息隊列中檢索消息。該函數分派傳入的已發送消息,直到已發佈的消息可供檢索爲止。

語法:

BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax );
複製代碼

參數說明:

  • lpMsg:指向 MSG 結構的指針,該結構從線程的消息隊列接收消息信息。
  • hWnd:獲取消息的的窗口句柄,文檔中解釋到:「若是hWnd爲NULL,則GetMessage檢索屬於當前線程的任何窗口的消息,以及當前線程的消息隊列中hwnd值爲NULL的消息(請參閱MSG結構)。所以,若是hWnd爲NULL,則將同時處理窗口消息和線程消息。
  • wMsgFilterMin,wMsgFilterMax:要檢索的最低、最高消息值的整數值「**
  • wMsgFilterMin 和 wMsgFilterMax 都爲零,則 GetMessage 返回全部可用消息**」

——————————————————————————————————

完整代碼

#include <windows.h> 
#include <tchar.h> 

static TCHAR szWindowClass[] = _T("CSDN @1_bit");
static TCHAR szTitle[] = _T("Win32 桌面應用程序");
HINSTANCE hInst;


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
	wcex.hCursor = LoadCursor(NULL, IDC_CROSS);
	wcex.hbrBackground = (HBRUSH)(COLOR_ACTIVECAPTION);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE(IDI_INFORMATION));

	RegisterClassEx(&wcex);

	hInst = hInstance;
	HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, 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 message, WPARAM wParam, LPARAM lParam) {
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR greeting[] = _T("Hello, 我是CSDN 1_bit 博客主頁:https://me.csdn.net/A757291228 ");

	switch (message)
	{
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		TextOut(hdc,
			5, 5,
			greeting, _tcslen(greeting));
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
		break;
	}

	return 0;
}
複製代碼

運行結果以下:

在這裏插入圖片描述
相關文章
相關標籤/搜索