【转】C++ win32窗口创建详解
轉自:https://my.oschina.net/u/4328928/blog/3315324
本篇所講解的內容僅限于 Windows 操作系統且限于 win32程序設計
現在我們在Windows系統上用的軟件, 早已不是控制臺界面, 而是窗體應用程序
窗體與控制臺的區別就是:?有了窗口的概念
由于C++的語法復雜, 使得很多人錯誤地認為C++并不能編寫Windows平臺的應用程序, 而只能編寫由 main 函數為入口的控制臺應用程序,這顯然是錯誤。
雖然C++ 編寫窗體程序復雜 (這使得很多人放棄學習C++窗體) ,但它具有更強的靈活性, 因為C++代碼能夠在窗體的任何地方添加想要的操作
C++ 在函數入口上下了很大的功夫,?普遍使用的是 main 函數, 但是如果不是 main 函數為入口的呢?
函數入口的不同導致了生成的結果不同, 下表列出了函數入口所對應生成的結果:
| 函數入口 | 生成情況 |
| main | 控制臺應用程序(.exe) |
| WinMain | 窗體應用程序(.exe): 以notepad.exe為例 ? |
| DLLMain | 鏈接庫(.lib或.dll) |
?
這里我們要講的是以WinMain函數為入口的win32應用程序
win32 應用程序有以下創建步驟:
1.?向系統注冊, 并規定該應用程序的基本信息并綁定事件處理函數
2.?創建窗口(HWND), 并規定該窗口的樣式
以上任何一步驟發生錯誤, 窗口都無法形成
3. 成功后進入消息循環, 將所接收到的消息發送給事件處理函數處理
?
事件 (消息) 處理函數有以下基本內容:
1.?收到消息后進行判斷, 并作出相應的數據處理
2. 一旦收到退出的消息(WM_QUIT),?摧毀窗口并結束程序
?
要用到的頭文件:
#include <windows.h>首先, 先認識一下WinMain函數:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, HINSTANCE, LPWSTR lpCmdLine, int nCmdShow);關于WinMain的解釋 Microsoft 較為詳細, 以下內容摘自?Microsoft Docs 的翻譯
WinMain是程序入口點。程序啟動時,它會注冊一些有關應用程序窗口行為的信息
hInstance:?被稱為“實例的句柄”或“模塊的句柄”。當可執行文件(EXE)加載到內存中時,操作系統使用該值來標識該可執行文件(EXE)。某些Windows功能需要實例句柄,例如,加載圖標或位圖
hPrevInstance:沒有任何意義。它曾在16位Windows中使用,但現在始終為零
lpCmdLine:?包含命令行參數作為Unicode字符串 (初學者可以先跳過)
nCmdShow:?是一個標志,指示是否將主應用程序窗口最小化,最大化或正常顯示
?
進入步驟1, 向系統注冊
注冊要用到 WNDCLASSEX 類型, 其用來存儲該程序的基本信息
基本信息:
1. 所給的緩存空間?(包括事件處理函數),?一般為0
wnd.cbClsExtra=0; wnd.cbWndExtra=0; wnd.cbSize=sizeof(WNDCLASSEX);2.?綁定模塊句柄(hInstance)與事件處理函數,?菜單欄資源
wnd.hInstance=hInstance;//模塊句柄 wnd.lpfnWndProc=WndProc;//事件處理函數(假設為WndProc) wnd.lpszMenuName=nullptr;//本篇未涉及到菜單欄的講解, 沒有指向任何菜單欄資源, 故為空3. 設定該程序的繪制風格與窗口類名
wnd.style=CS_HREDRAW|CS_VREDRAW;//繪制風格 wnd.lpszClassName="WindowClass";//窗口類名(任意, 但是確定了不能更改),可以理解為該應用程序窗口的"代表"名字CS_HREDRAW: 窗口發生移動或變化時使寬度變化, 重新繪制窗口
CS_VREDRAW:?窗口發生移動或變化時使高度變化, 重新繪制窗口
沒有其中之一可能會使文字的輸出在調整窗口后發生錯誤
4. 設定該窗口的背景色, 圖標, 以及鼠標挪到窗口上時的顯示樣式
wnd.hbrBackground=(HBRUSH)COLOR_WINDOW;//有很多顏色, 這里選用淺灰色, 可以自己到頭文件里選 wnd.hIcon=LoadIcon(hInstance,IDI_APPLICATION);//默認的圖標 wnd.hIconSm=LoadIcon(hInstance,IDI_APPLICATION);//指向圖標資源的指針, 這里采用默認圖標 wnd.hCursor=LoadCursor(hInstance,IDC_ARROW);//標準的鼠標樣式有多種樣式, 自己查, 不做過多介紹
合寫為:
WNDCLASSEX wnd; wnd.hbrBackground=(HBRUSH)COLOR_WINDOW; wnd.lpfnWndProc=WndProc; wnd.style=CS_HREDRAW|CS_VREDRAW; wnd.lpszClassName="WindowClass"; wnd.hInstance=hInstance; wnd.hIcon=LoadIcon(hInstance,IDI_APPLICATION); wnd.hIconSm=LoadIcon(hInstance,IDI_APPLICATION); wnd.hCursor=LoadCursor(hInstance,IDC_ARROW); wnd.cbSize=sizeof(WNDCLASSEX); wnd.lpszMenuName=nullptr; wnd.cbClsExtra=0; wnd.cbWndExtra=0;現在注冊并檢查是否成功, 用到函數?RegisterClassEx?,向系統提交注冊信息, 失敗返回0
if(!RegisterClassEx(&wnd)) //注冊并檢查是否失敗 {MessageBox(nullptr,"Error!","Error",MB_ICONERROR|MB_OK);return 0; //失敗后結束程序 }?
?
?
執行步驟2, 創建窗口并規定樣式:
認識創建窗口的函數(參考自百度百科?):
HWND CreateWindowEx( DWORD DdwExStyle, //窗口的擴展風格 LPCTSTR lpClassName, //指向注冊類名的指針 LPCTSTR lpWindowName, //指向窗口名稱的指針 DWORD dwStyle, //窗口基本樣式 int x, //窗口的一開始出現在電腦屏幕上的x坐標 int y, //窗口的一開始出現在電腦屏幕上的y坐標int nWidth, //窗口的寬度 int nHeight, //窗口的高度 HWND hWndParent, //父窗口 HMENU hMenu, //菜單欄 HINSTANCE hInstance, //該應用程序的模塊句柄 LPVOID lpParam //指向窗口的創建數據 );這里窗口的擴展, 基本風格有很多, 不多介紹, 以標準的窗口風格為例
這里選擇幾個難懂的參數做解釋:
DdwExStyle: 這里采用標準擴展風格?WS_EX_CLIENTEDGE
lpClassName: 這里可理解為窗口所"代表的名字", 上面注冊時已編寫, 即"WindowClass"
lpWindowName: 這里可以理解為窗口的標題
dwStyle: 這里采用標準窗口風格?WS_OVERLAPPEDWINDOW
hMenu: 該窗口所包含的菜單欄, 這里沒有創建菜單欄, 即為?nullptr
hInstance: 直接將WinMain中的?hInstance?填入
lpParam: 不常用, 直接寫?nullptr
?
創建完了,?不代表能夠顯示出來, 所以還要顯示窗口
BOOL ShowWindow(HWND hWnd, int nCmdShow); //顯示窗口的函數hWnd: 要顯示的窗口
nCmdShow:?窗口的顯示方式, WinMain 參數中已有該窗口的顯示方式, 直接將參數?nCmdShow?寫入
?
上代碼:
HWND hwnd=CreateWindow("WindowClass","Title",WS_OVERLAPPEDWINDOW,0,0,800,800,nullptr,hMenu,hInstance,nullptr); ShowWindow(hwnd,nCmdShow);這是一個出現在(0,0)左上角, 800px?* 800px, 標題欄顯示?Title?的標準父窗口
?
?
?
現在執行步驟3, 進入消息循環
消息循環是這樣的:
? ? ? ?在窗體程序創建并顯示成功后, 需要進行各種各樣的消息的處理?(如,?鼠標按下了窗口中的按鈕, 鍵盤按下了一個按鍵等), 這些信息就是消息(MSG), 這些消息要被程序所捕獲,用不斷循環來檢查是否存在消息, 將消息轉換為可以被事件處理函數所接受的內容并發送給事件處理函數.
?
而如何捕獲消息呢? 每個應用程序創建后, 系統都會為其開辟一個"消息隊列", 將所接收到的各種消息入隊:
?
?
?而程序要做的就是不斷從隊首元素中獲取消息,?直到隊首元素為空(即收到了WM_QUIT的退出消息)為止, 結束程序
消息的表示:?WM_+ 消息內容
如:
退出消息:?WM_QUIT
創建窗口消息:?WM_CREATE
調整窗口大小的消息:?WM_SIZE
?
上代碼 (實現簡單):?
MSG msg; while(GetMessage(&msg,0,0,0)) //不斷獲取隊首元素直到隊首為空 {TranslateMessage(&msg); //將收到的消息轉換為"WM_+消息內容"的形式DispatchMessage(&msg); //將收到的消息發送給事件處理函數 }?
?
?
WinMain 函數的部分已完成, 上完整代碼:
BOOL RegisterWindow(HINSTANCE hInstance) {WNDCLASSEX wnd;wnd.hbrBackground=(HBRUSH)COLOR_WINDOW;wnd.lpfnWndProc=WndProc;wnd.style=CS_HREDRAW|CS_VREDRAW;wnd.lpszClassName="WindowClass";wnd.hInstance=hInstance;wnd.hIcon=LoadIcon(hInstance,IDI_APPLICATION);wnd.hIconSm=LoadIcon(hInstance,IDI_APPLICATION);wnd.hCursor=LoadCursor(hInstance,IDC_ARROW);wnd.cbSize=sizeof(WNDCLASSEX);wnd.lpszMenuName=nullptr;wnd.cbClsExtra=0;wnd.cbWndExtra=0;return RegisterClassEx(&wnd); } BOOL DisplayWindow(HWND& hwnd,HINSTANCE hInstance,HMENU hMenu,int nCmdShow) {hwnd=CreateWindow("WindowClass","TweeChaice",WS_OVERLAPPEDWINDOW,0,0,800,800,nullptr,nullptr,hInstance,nullptr);ShowWindow(hwnd,nCmdShow);return hwnd?TRUE:FALSE; }HWND hwnd; int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) {if(!RegisterWindow(hInstance)){MessageBox(nullptr,"Error register!","Error",MB_ICONERROR|MB_OK);return -1;}DisplayMenu();if(!DisplayWindow(hwnd,hInstance,nullptr,nCmdShow)){MessageBox(nullptr,"Error creating window!","Error",MB_ICONERROR|MB_OK);return -1;}MSG msg;while(GetMessage(&msg,0,0,0)){TranslateMessage(&msg);DispatchMessage(&msg);}return 0; }?
?
?
現在就差事件處理了, 什么是事件處理函數?
對一系列的 "WM_消息" 進行對應的處理?(如,?點擊按鈕后進行處理) 的函數, 它的編寫風格很隨意, 憑自己選擇
?
事件處理函數的返回值必須為:?LRESULT CALLBACK
所包含的參數類型必須為如下, 但變量名隨意:
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)解釋:
hwnd:?父窗口
message:?收到的 "WM" 消息
wParam與lParam: MSG中附加消息, 與窗口的信息, 事件處理有關
?
編寫隨意(帶必須擁有WM_QUIT, 否則窗體的關閉將無法進行):
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) {if(message==WM_DESTROY) //當窗口被摧毀時(有時是強制摧毀), 發送WM_QUIT消息, 可以省略PostQuitMessage(0);else if(message==WM_QUIT) //接收到退出消息, 摧毀窗口(并停止接受任何消息, 消息隊列中的隊首元素清除為空), 然后消息循環將發現隊首為空, 實現退出程序的效果DestroyWindow(hwnd);return DefWindowProc(hwnd,message,wParam,lParam); //正常返回 }?
上完整代碼:
#include <windows.h> LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) {if(message==WM_DESTROY)PostQuitMessage(0);else if(message==WM_QUIT)DestroyWindow(hwnd);return DefWindowProc(hwnd,message,wParam,lParam); } BOOL RegisterWindow(HINSTANCE hInstance) {WNDCLASSEX wnd;wnd.hbrBackground=(HBRUSH)COLOR_WINDOW;wnd.lpfnWndProc=WndProc;wnd.style=CS_HREDRAW|CS_VREDRAW;wnd.lpszClassName="WindowClass";wnd.hInstance=hInstance;wnd.hIcon=LoadIcon(hInstance,IDI_APPLICATION);wnd.hIconSm=LoadIcon(hInstance,IDI_APPLICATION);wnd.hCursor=LoadCursor(hInstance,IDC_ARROW);wnd.cbSize=sizeof(WNDCLASSEX);wnd.lpszMenuName=nullptr;wnd.cbClsExtra=0;wnd.cbWndExtra=0;return RegisterClassEx(&wnd); } BOOL DisplayWindow(HWND& hwnd,HINSTANCE hInstance,HMENU hMenu,int nCmdShow) {hwnd=CreateWindow("WindowClass","TweeChaice",WS_OVERLAPPEDWINDOW,0,0,800,800,nullptr,nullptr,hInstance,nullptr);ShowWindow(hwnd,nCmdShow);return hwnd?TRUE:FALSE; }HWND hwnd; int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) {if(!RegisterWindow(hInstance)){MessageBox(nullptr,"Error register!","Error",MB_ICONERROR|MB_OK);return -1;}if(!DisplayWindow(hwnd,hInstance,nullptr,nCmdShow)){MessageBox(nullptr,"Error creating window!","Error",MB_ICONERROR|MB_OK);return -1;}MSG msg;while(GetMessage(&msg,0,0,0)){TranslateMessage(&msg);DispatchMessage(&msg);}return 0; }執行效果:
?
?win32 編程復雜但不難, 需要好好領悟其原理, 才能走得更遠
另: 窗體應用程序不能在單個文件中或在生成結果沒被設定為GUI 應用程序(窗體應用程序或WIN32)中正確編寫, 需創建窗體程序(WIN32)項目, 否則生成的程序還帶有控制臺.
? ? ? 窗體應用程序中無法使用任何控制臺的輸入輸出
總結
以上是生活随笔為你收集整理的【转】C++ win32窗口创建详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国寿安鑫利365怎么样?风险和历史收益
- 下一篇: s3c2440移植MQTT