如何創(chuàng)建一個(gè)最簡(jiǎn)單的Windows桌面應(yīng)用程序 (C++)
最近剛開始學(xué)習(xí)C/C++開發(fā)Windows應(yīng)用程序,這里將會(huì)以零基礎(chǔ)的視角把學(xué)習(xí)過(guò)程完全記錄下來(lái)。如果你也剛剛起步,那本文一定非常適合你。
進(jìn)入正題,本文討論如何使用Visual Studio生成一個(gè)最簡(jiǎn)單的C窗體應(yīng)用程序,并向用戶顯示Hello~
下面我們一步步來(lái)介紹,對(duì)于涉及代碼的地方,我們只介紹大體的框架,完整的代碼會(huì)在文章最后給出。
創(chuàng)建基于 Win32 的項(xiàng)目
1.在文件菜單上,單擊新建,然后單擊項(xiàng)目。
2.在“新建項(xiàng)目”對(duì)話框的左窗格中,依次單擊“已安裝模板”和“Visual C++”,然后選擇“Win32”。在中間窗格中,選擇“Win32 項(xiàng)目”。在“名稱”框中,鍵入項(xiàng)目名稱,例如HelloApp。單擊“確定”。
3.在“Win32 應(yīng)用程序向?qū)А钡臍g迎頁(yè)面中,單擊“下一步”。在“應(yīng)用程序設(shè)置”頁(yè)的“應(yīng)用程序類型”下,選擇“Windows 應(yīng)用程序”。 在“附加選項(xiàng)”下,選擇“空項(xiàng)目”。 單擊“完成”以創(chuàng)建項(xiàng)目。
4.在“解決方案資源管理器”中,右鍵單擊 HelloApp項(xiàng)目,然后依次單擊“添加”和“新建項(xiàng)”。 在“添加新項(xiàng)”對(duì)話框中選擇“C++ 文件(.cpp)”。 在“名稱”框中,鍵入文件名,例如GT_HelloWorldWin32.cpp。單擊“添加”。
添加引用
我們的應(yīng)用程序需要使用許多現(xiàn)有定義才能完成所需功能,針對(duì)我們的需求,添加引用如下(其中前兩個(gè)是必須的):
#include <windows.h> #include <stdlib.h> #include <string.h> #include <tchar.h>WinMain函數(shù)
正如每個(gè) C/C++控制臺(tái)應(yīng)用程序在起始點(diǎn)必須具有 main 函數(shù),每個(gè)基于 Win32 的應(yīng)用程序的函數(shù)也必須具有 WinMain 函數(shù)。WinMain就相當(dāng)于是入口函數(shù),并且具有固定的語(yǔ)法:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPRevInstance, LPSTR lpCmdLine, int nCmdShow);實(shí)現(xiàn)WinMain函數(shù)時(shí)內(nèi)部邏輯大體相同,主要有以下幾部分:
1.創(chuàng)建描述窗體信息的窗口類結(jié)構(gòu)WNDCLASSEX
如何創(chuàng)建一個(gè) WNDCLASSEX 類型的窗口類結(jié)構(gòu)?下面的代碼演示了一個(gè)典型的窗口類結(jié)構(gòu)WNDCLASSEX 的定義:
//創(chuàng)建 WNDCLASSEX 類型的窗口類結(jié)構(gòu)。 此結(jié)構(gòu)包含關(guān)于窗口的信息 //例如應(yīng)用程序圖標(biāo)、窗口背景色、標(biāo)題欄中顯示的名稱、窗口過(guò)程函數(shù)的名稱等。 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_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));對(duì)于初學(xué)者,我們不用過(guò)分糾結(jié)代碼的細(xì)節(jié),暫時(shí)從宏觀上把控。我們需要知道,此結(jié)構(gòu)包含關(guān)于窗口的信息,例如應(yīng)用程序圖標(biāo)、窗口背景色、標(biāo)題欄中顯示的名稱、窗口過(guò)程函數(shù)的名稱等。 2.對(duì)窗口類進(jìn)行注冊(cè)
現(xiàn)在已創(chuàng)建了窗口類,必須進(jìn)行注冊(cè)。
使用 RegisterClassEx 函數(shù),并將窗口類結(jié)構(gòu)作為參數(shù)傳遞。
RegisterClassEx(&wcex);3.創(chuàng)建并顯示窗口
現(xiàn)在需要使用CreateWindow函數(shù)創(chuàng)建窗口
使用ShowWindow函數(shù)顯示窗口
這部分也很好理解,詳見文末的代碼
4.偵聽消息
添加用于偵聽操作系統(tǒng)所發(fā)送消息的消息循環(huán)。
當(dāng)應(yīng)用程序收到一條消息時(shí),此循環(huán)將該消息調(diào)度到 WndProc 函數(shù)。WndProc 函數(shù)用于對(duì)接收的消息進(jìn)行處理,我們下面會(huì)介紹到。
該消息循環(huán)類似于以下代碼:
// Main message loop: // 添加用于偵聽操作系統(tǒng)所發(fā)送消息的消息循環(huán)。 // 當(dāng)應(yīng)用程序收到一條消息時(shí),此循環(huán)將該消息調(diào)度到 WndProc 函數(shù)以進(jìn)行處理。 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); //翻譯消息 DispatchMessage(&msg); //派遣消息 }WndProc 函數(shù)
WndProc 函數(shù)用以處理應(yīng)用程序收到的消息。
首先需要判斷收到的消息類型進(jìn)而做出不同的處理,這需要使用 switch 語(yǔ)句。 系統(tǒng)提供了眾多的消息命令,例如 WM_PAINT 代表收到了繪圖消息,而收到鼠標(biāo)點(diǎn)擊消息的標(biāo)識(shí)是WM_LBUTTONDOWN...
這里以處理 WM_PAINT 消息為例進(jìn)行說(shuō)明。
要處理 WM_PAINT 消息,首先應(yīng)調(diào)用 BeginPaint,然后處理所有的繪圖邏輯,例如在窗口中布局文本、按鈕和其他控件,然后調(diào)用 EndPaint。
對(duì)于此應(yīng)用程序,開始調(diào)用和結(jié)束調(diào)用之間的邏輯是在窗口中顯示字符串 “Hello,World!”。 在以下代碼中,TextOut 函數(shù)用于顯示字符串。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; TCHAR greeting[] = _T("Hello, World!"); switch (message) { case WM_PAINT: //要處理 WM_PAINT 消息,首先應(yīng)調(diào)用 BeginPaint //然后處理所有的邏輯以及在窗口中布局文本、按鈕和其他控件等 //然后調(diào)用 EndPaint。 hdc = BeginPaint(hWnd, &ps); // -----------------在這段代碼對(duì)應(yīng)用程序進(jìn)行布局------------------------ // 對(duì)于此應(yīng)用程序,開始調(diào)用和結(jié)束調(diào)用之間的邏輯是在窗口中顯示字符串 “Hello,World!”。 // 請(qǐng)注意 TextOut 函數(shù)用于顯示字符串。 TextOut(hdc, 50, 5, greeting, _tcslen(greeting)); // -----------------------布局模塊結(jié)束---------------------------------- EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: //DefWindowProc缺省窗口處理函數(shù) //這個(gè)函數(shù)是默認(rèn)的窗口處理函數(shù),我們可以把不關(guān)心的消息都丟給它來(lái)處理 return DefWindowProc(hWnd, message, wParam, lParam); break; } return 0; }程序運(yùn)行的結(jié)果為:
完整代碼
到此我已經(jīng)理清了寫一個(gè)Windows應(yīng)用程序的主要邏輯
我是按照Microsoft官方文檔進(jìn)行的學(xué)習(xí),詳見創(chuàng)建Windows桌面應(yīng)用程序
對(duì)于更多的細(xì)節(jié),代碼中給出了詳細(xì)注釋。
// GT_HelloWorldWin32.cpp // compile with: /D_UNICODE /DUNICODE /DWIN32 /D_WINDOWS /c // 這是一個(gè)最簡(jiǎn)單的Win32程序,亦可作為開發(fā)桌面應(yīng)用程序的模板#include <windows.h> #include <stdlib.h> #include <string.h> #include <tchar.h> // ------------------------Global variables---------------------------------// 主窗體類名static TCHAR szWindowClass[] = _T("win32app"); // 應(yīng)用程序標(biāo)題欄處出現(xiàn)的字符串static TCHAR szTitle[] = _T("Win32 Guided Tour Application"); //HINSTANCE 是Windows里的一中數(shù)據(jù)類型,是用于標(biāo)示(記錄)一個(gè)程序的實(shí)例。//它與HMODULE是一樣的(通用的,這兩種類型最終就是32位無(wú)符號(hào)長(zhǎng)整形)。//HINSTANCE,分開看就是 HANDLE(句柄) + INSTANCE(實(shí)例)//實(shí)例就是一個(gè)程序。用QQ來(lái)舉例:你可以開同時(shí)開2個(gè)qq號(hào),但是你電腦里的qq軟件只有一份。//這2個(gè)qq號(hào)就是qq的2個(gè)實(shí)例HINSTANCE hInst; // -------------------------需要預(yù)定義的函數(shù)放置在此代碼塊種:---------------------------- //每個(gè) Windows 桌面應(yīng)用程序必須具有一個(gè)窗口過(guò)程函數(shù)//此函數(shù)處理應(yīng)用程序從操作系統(tǒng)中接收的大量消息。 //例如,如果應(yīng)用程序的對(duì)話框中有“確定”按鈕,那么用戶單擊該按鈕時(shí),//操作系統(tǒng)會(huì)向應(yīng)用程序發(fā)送一條消息,通知按鈕已被單擊。WndProc 負(fù)責(zé)對(duì)該事件作出響應(yīng)。//在本例中,相應(yīng)的響應(yīng)可能是關(guān)閉對(duì)話框。LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //-------------------------------------正式內(nèi)容-----------------------------------------//主窗體函數(shù)(入口過(guò)程)//每個(gè)基于 Win32 的應(yīng)用程序的函數(shù)必須具有 WinMain 函數(shù)//正如每個(gè) C 應(yīng)用程序和 C++ 控制臺(tái)應(yīng)用程序在起始點(diǎn)必須具有 main 函數(shù)int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { //創(chuàng)建 WNDCLASSEX 類型的窗口類結(jié)構(gòu)。 此結(jié)構(gòu)包含關(guān)于窗口的信息 //例如應(yīng)用程序圖標(biāo)、窗口背景色、標(biāo)題欄中顯示的名稱、窗口過(guò)程函數(shù)的名稱等。 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_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); //對(duì)已創(chuàng)建的窗口類進(jìn)行注冊(cè)。 使用 RegisterClassEx 函數(shù),并將窗口類結(jié)構(gòu)作為參數(shù)傳遞。 if (!RegisterClassEx(&wcex)) { MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Win32 Guided Tour"), NULL); return 1; } // Store instance handle in our global variable // 將句柄實(shí)例存儲(chǔ)于全局變量中 hInst = hInstance; // CreateWindow 函數(shù)的參數(shù)解釋: // szWindowClass: the name of the application // szTitle: the text that appears in the title bar // WS_OVERLAPPEDWINDOW: the type of window to create // CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y) // 500, 100: initial size (width, length) // NULL: the parent of this window // NULL: this application does not have a menu bar // hInstance: the first parameter from WinMain // NULL: not used in this application // 返回的HWND是一個(gè)窗口的句柄 HWND hWnd = CreateWindow( szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL ); if (!hWnd) { MessageBox(NULL, _T("Call to CreateWindow failed!"), _T("Win32 Guided Tour"), NULL); return 1; } // ShowWindow 函數(shù)的參數(shù)解釋: // hWnd: CreateWindow函數(shù)返回的窗口句柄 // nCmdShow: the fourth parameter from WinMain ShowWindow(hWnd, nCmdShow); // UpdateWindow函數(shù)用于更新窗口指定的區(qū)域 // 如果窗口更新的區(qū)域不為空,UpdateWindow函數(shù)就發(fā)送一個(gè)WM_PAINT消息來(lái)更新指定窗口的客戶區(qū)。 // 函數(shù)繞過(guò)應(yīng)用程序的消息隊(duì)列,直接發(fā)送WM_PAINT消息給指定窗口的窗口過(guò)程 // 如果更新區(qū)域?yàn)榭眨瑒t不發(fā)送消息。 UpdateWindow(hWnd); // Main message loop: // 添加用于偵聽操作系統(tǒng)所發(fā)送消息的消息循環(huán)。 // 當(dāng)應(yīng)用程序收到一條消息時(shí),此循環(huán)將該消息調(diào)度到 WndProc 函數(shù)以進(jìn)行處理。 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); //翻譯消息 DispatchMessage(&msg); //派遣消息 } return (int) msg.wParam; } // // 函數(shù): WndProc(HWND, UINT, WPARAM, LPARAM) // // 目標(biāo): 處理主窗體產(chǎn)生的信息// // WM_PAINT消息代表繪制主窗體 // // WM_DESTROY消息代表投遞一個(gè)退出消息并返回 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; TCHAR greeting[] = _T("Hello, World!"); switch (message) { case WM_PAINT: //要處理 WM_PAINT 消息,首先應(yīng)調(diào)用 BeginPaint //然后處理所有的邏輯以及在窗口中布局文本、按鈕和其他控件等 //然后調(diào)用 EndPaint。 hdc = BeginPaint(hWnd, &ps); // -----------------在這段代碼對(duì)應(yīng)用程序進(jìn)行布局------------------------ // 對(duì)于此應(yīng)用程序,開始調(diào)用和結(jié)束調(diào)用之間的邏輯是在窗口中顯示字符串 “Hello,World!”。 // 請(qǐng)注意 TextOut 函數(shù)用于顯示字符串。 TextOut(hdc, 50, 5, greeting, _tcslen(greeting)); // -----------------------布局模塊結(jié)束---------------------------------- EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: //DefWindowProc缺省窗口處理函數(shù) //這個(gè)函數(shù)是默認(rèn)的窗口處理函數(shù),我們可以把不關(guān)心的消息都丟給它來(lái)處理 return DefWindowProc(hWnd, message, wParam, lParam); break; } return 0; }
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注