WinMain函數
Windows應用程序的唯一程序入口。
函數原型
1 int WINAPI WinMain 2 { 3 HINSTANCE hInstancem 4 HINSTANCE hPreInstance, 5 LPSTR lpCmdLine, 6 int nCmdShow 7 }
WINAPI定義如下
#define WINAPI _stdcall
_stdcall是一個函數調用約定,除此之外,還有__cdecl,fastcall,thiscall,naked call等函數調用約定。
_stdcall調用約定又稱Pascal調用約定,也是Pascal語言的調用約定。它使用的方式為:
1 int __stdcall sum(int a,int b);
__stdcall:函數的多個參數由調用者按從右到左的順序壓入堆棧,被調用函數獲得參數的序列是從左到右的的;清理堆棧的工作由被調用函數負責。
在Visual C++中,常用宏WINAPI或CALLBACK來表示__stdcall調用約定。
更詳細的說明可以查看
https://docs.microsoft.com/en-us/cpp/cpp/stdcall?view=vs-2019
__cdecl(也可寫成_cdecl)調用約定又稱C調用約定,是C函數默認的調用約定,也是C++全局函數的默認調用約定,通常省略。
如
1 int sum(int a,int b); 2 int __cdecl sum(int a,int b);
__cdecl:函數的多個參數由調用者按從右向左的順序壓入堆棧,被調函數獲得參數的序列是從左到右的;清理堆棧的工作由調用者負責
。
更詳細的說明可以查看
https://docs.microsoft.com/en-us/cpp/cpp/cdecl?view=vs-2019
WinMain函數的各參數說明
hInstance
應用程序當前運行的實例的句柄,該句柄由Windows系統生成。
hPrevInstance
當前實例的前一個實例的句柄,在Win32環境下,該參數總是NULL,不再起作用
lpCmdLine
一個以空終止的字符串,代表傳遞給程序的命令行參數。
nCmdShow
指定窗口的顯示狀態
常用值如下
nCmdShow = 0;不顯示
nCmdShow = 1;正常顯示(默認)
nCmdShow = 2;最小化顯示
nCmdShow = 3;最大化顯示
使用代碼創建Windows程序的步驟
1、設計一個Windows類
2、在Windows系統中注冊Windows類
3、用該Windows類創建一個窗口
4、創建一個消息循環
5、創建一個窗口過程函數WndProc
一、設計Windows類
在創建一個窗口前,必須對窗口進行設計,指定窗口的屬性。系統已經定義了WNDCLASS結構用於描述待創建窗口的參數。
WNDCLASS聲明如下
1 typedef struct tagWNDCLASSA { 2 UINT style; 3 WNDPROC lpfnWndProc; 4 int cbClsExtra; 5 int cbWndExtra; 6 HINSTANCE hInstance; 7 HICON hIcon; 8 HCURSOR hCursor; 9 HBRUSH hbrBackground; 10 LPCSTR lpszMenuName; 11 LPCSTR lpszClassName; 12 } WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;
下面介紹各參數
style
窗口樣式,可用值如下
CS_VREDRAW:垂直重繪,當窗口垂直方向上的高度發生變化時,將重新繪制整個窗口。如果沒有指定這一樣式,在垂直方向上調整窗口高度時,將不會重繪窗口。
CS_HREDRAW:水平重繪,當窗口水平方向上的寬度發生變化時,將重新繪制整個窗口。如果沒有指定這一樣式,在水平方向上調整窗口高度時,將不會重繪窗口。
CS_OWNDC:獨占設備描述表,為該類中的每個窗口分配一個單值的設備描述表。
CS_SAVEBITS:在一個窗口中保存用戶圖像,以便於在該窗口被遮住、移動時不必每次刷新屏幕。但是,這樣會占用更多的內存,並且比人工進行同樣操作時要慢得多。
CS_DBLCLKS:使窗口可以檢測到鼠標雙擊事件,當用戶在窗口中雙擊鼠標時,向窗口過程發送鼠標雙擊消息
CS_BYTEALLGNCLIENT:鼠標用戶區域按字節對齊顯示。
CS_BYTEALLGNWINDOW:鼠標用戶窗口按字節對齊顯示。
CS_PARENTDC:在父窗口中設定一個子窗口的剪切區,以便於子窗口能夠畫在父窗口中。
CS_NOCLOSE:系統菜單中沒有CLOSE菜單項,窗口沒有關閉按鈕。
lpfnWndProc
指向窗口過程函數的函數指針。窗口過程函數是一個回調函數,針對Windows的消息處理機制,窗口過程函數被調用的過程如下:
1、在設計窗口類的時候,將窗口過程函數的地址賦給lpfnWndProc成員變量
2、調用RegisterClass(&wndclass)注冊窗口類,系統就有了用戶編寫的窗口過程函數的地址
3、當應用程序接收到某一窗口的信息時,調用DispatchMessage(&msg)將消息回傳給系統。系統則利用先前注冊窗口類時得到的函數指針,調用窗口過程函數對消息進行處理
cbClsExtra
Windows系統為窗口類結構分配追加的額外字節數。一般為0
cbWndExtra
Windows系統為窗口實例分配或追加的額外字節數,一般為0。如果應用程序使用資源文件里的CLASS指令創建對話框,並用WNDCLASS結構注冊對話框框時,cbWndExtra必須設置成DLGWINDOWEXTRA
hInstance
包含窗口過程程序的實例句柄。一般直接賦WinMain()的hInstance即可
hIcon
窗口類的圖標資源。這個成員變量必須是一個圖標資源的句柄。可以使用LoadIcon()函數加載圖標,如果hIcon為NULL,窗口將使用系統提供的默認圖標
hCursor
窗口類的光標句柄。這個成員變量必須是一個光標資源的句柄。可以使用LoadCursor()函數加載光標。如果hCursor為NULL,應用程序必須在鼠標進入應用程序窗口時,明確設置光標的形狀
hbrBackground
窗口類的背景畫刷句柄。當窗口發生重繪時,系統使用這里指定的畫刷來填充窗口的背景。該成員可以指定為用於繪制背景的物理畫刷的句柄,也可以指定為標准的系統顏色值。如下:
BLACK_BRUSH 黑色
DKGRAY_BRUSH 深灰
GRAY_BRUSH 灰色
HOLLOW_BRUSH 空
LTGRAY_BRUSH 淺灰
NULL_BRUSH 等同於HOLLOW_BRUSH
WHITE_BRUSH 白色
BLACK_BRUSH 黑色
lpszMenuName
指向一個以空終止的字符串,該字符串描述菜單的資源名。若使用整數來標識菜單,需要用MAKEINTRESOURCE宏來進行轉換。如果lpszMenuName設置為NULL,那么基於窗口類創建的窗口將沒有默認菜單
lpszClassName
指向一個以空終止的字符串,該字符串描述窗口類的名字。這個類名可以是由RegisterClass或者RegisterClassEx注冊的名字,或者是任何預定義的控件類名
WNDCLASS使用實例如下
1 WNDCLASS wc; 2 3 wc.style = CS_HREDRAW | CS_VREDRAW; 4 wc.lpfnWndProc = WndProc; 5 wc.cbClsExtra = 0; 6 wc.cbWndExtra = 0; 7 wc.hInstance = hInstance; 8 wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1)); 9 wc.hCursor = LoadCursor(nullptr, IDC_ARROW); 10 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 11 wc.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1); 12 wc.lpszClassName = szWindowClass;
二、注冊Windows類
Windows類設計完成時,需要調用RegisterClass()函數去注冊這個類,才可以創建該類型的窗口
1 ATOM RegisterClass( 2 const WNDCLASSA *lpWndClass 3 );
注冊代碼如下
if(!RegisterClass(&wc)) { return 0; }
三、創建窗口
使用CreateWindow函數創建窗口,如果函數調用成功,返回值為新窗口的句柄;如果調用失敗,返回值為NULL。可以使用GetLastError()函數獲取錯誤信息
1 HWND CreateWindow( 2 LPCTSTR lpClassName, 3 LPCTSTR lpWindowName, 4 DWORD dwStyle, 5 int x, 6 int y, 7 int nWidth, 8 int nHeight, 9 HWND hWndParent, 10 HMENU hMenu, 11 HANDLE hInstance, 12 PVOID lpParam 13 );
lpClassName
指定窗口類的名稱,這個名稱就是WNDCLASSA中的lpszClassName。如果在調用CreateWindow函數之前,沒有調用RegisterClass函數注冊這個類,系統無法得知窗口的相關信息,窗口創建就會失敗。
lpWindowName
指定窗口名稱,如果指定了標題欄,那么這里指向的字符串就會顯示在標題欄上。
dwStyle
指定創建窗口的樣式,可以組合不同的窗口樣式
常量 | 說明 |
WS_CAPTION(0x00C00000L) | 創建一個有標題欄的窗口 |
WS_SYSMENU(0x00080000L) | 創建一個在標題欄上帶有系統菜單的窗口(需要和WS_CAPTION一起使用) |
WS_MINIMIZEBOX(0x00020000L) | 創建一個具有最小化按鈕的窗口(需要和WS_SYSMENU一起使用) |
WS_MAXIMIZEBOX(0x00010000L) | 創建一個具有最大化按鈕的窗口(需要和WS_SYSMENU一起使用) |
WS_TILED(0x00000000L) | 創建一個層疊的窗口,層疊的窗口有一個標題欄和一個邊框 |
WS_TILEDWINDOW | 創建一個使用(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)樣式的層疊的窗口 |
WS_CHILD(0x40000000L) | 創建窗口為子窗口,不能應用於彈出式窗口樣式 |
WS_OVERLAPPED | 與WS_TILED樣式相同 |
WS_OVERLAPPEDWINDOW | 創建一個使用(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)樣式的層疊的窗口 |
WS_EX_TOPMOST | 創建一個始終置頂的窗口(不管窗口是否已經激活) |
WS_POPUP(0x80000000L) | 創建一個彈出式窗口(不能與WS_CHILD一起使用) |
WS_VISIBLE(0x10000000L) | 創建一個初始狀態為可見的窗口(可以使用ShowWindow函數來控制顯示或隱藏窗口) |
完整窗口樣式可以訪問:https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles |
x
指定窗口左上角的x坐標
y
指定窗口左上角的y坐標
nWidth
以設備單元指定窗口的寬度
nHeight
以設備單元指定窗口的高度
hWndParent
指定被創建窗口的父窗口的句柄。如果要創建一個子窗口,這里就需要提供父窗口的句柄。
hMenu
菜單句柄,指向附屬於該窗口的菜單
hInstance
WinMain函數中傳入的應用程序實例的句柄
lpParam
作為WM_CREATE消息的附加參數lParam傳入的數據指針。在創建多文檔界面的客戶窗口時,lpParam必須指向CLIENTCREATESTRUCT結構體。多數窗口將這個參數設置為NULL
CreateWindow示例代碼如下
HWND hwnd; hwnd = CreateWindow( "MainWClass", "Test Window", WS_OVERLAPPEDWINDOW, 0, 0, CW_USEDEFAULT, // 默認寬度 CW_USEDEFAULT, // 默認高度 NULL, // 沒有父窗體 NULL, // 沒有菜單 hinstance, NULL); //沒有附加數據
四、顯示窗口
執行CreateWindow函數,窗體創建成功之后,需要調用ShowWindow函數把窗口顯示在桌面上
BOOL ShowWindow(HWND hWnd,int nCmdShow);
hWnd
CreateWindow創建窗口成功后返回的窗口句柄
nCmdShow
指示窗口顯示的狀態
常用的窗口顯示狀態如下
SW_HIDE: 隱藏窗口並激活其它窗口
SW_SHOW: 在窗口原來的位置以原來的尺寸激活並顯示窗口
SW_SHOWMAXIMIZED: 激活並以最大化顯示窗口
SW_SHOWMINIMIZED: 激活並最小化顯示窗口
SW_SHOWNORMAL 激活並顯示窗口。如果窗口是最大化或最小化的狀態,系統將其恢復到原來的尺寸和大小。應用程序在第一次顯示窗口時,應該使用這種狀態
未完