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