開發 Windows API的Windows程序,需要編寫兩個函數。一個是Winmain()函數,程序的執行從這裏開始,基本的初始化工作也在這裏完成。另一個是WindowProc()函數,該函數由Windows調用,用來給應用程序傳遞消息。Winmain與WindowProc函數通過調用系統的API與Windows通信,如圖所示:
1 Winmain函數
Winmain()函數等價於控制檯的程序中的main()函數,Winmain()函數的原型如下:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow);
(1) WINAPI是一個Windows定義的宏,將使系統以特定於Windows API函數的特種方式處理函數名和實參。
(2) hInstance是指向某個實例的的句柄。
(3) hPrevInstance是16位操作系統繼承下來的,現在的操作系統可以將這位始終設爲空。
(4) lpCmdLine是指向某個字符串的指針,該字符串包括啓動程序的命令行字符。
(5) nCmdShow決定着被創建窗口的外觀。
程序中的Winmain()函數需要做以下四件事情:
(1) 指定程序窗口種類
(2) 創建窗口
(3) 初始化窗口
(4) 處理部分消息
1.1 指定程序窗口
Windows使用WNDCLASSEX來包含用來指定窗口的數據,WNDCLASSEX 結構用於註冊窗口類,WNDCLASSEX的結構定義如下:
struct WNDCLASSEX {
UINT cbSize; // WNDCLASSEX 的大小
UINT style; //窗口類的樣式,它的值可以是窗口樣式值的任意組合
WNDPROC lpfnWndProc; //指向窗口處理消息的函數的指針
int cbClsExtra; //允許請求Windows在內部爲特別用途提供額外空間,常用初始化爲0
int cbWndExtra; //允許請求Windows在內部爲特別用途提供額外空間,常用初始化爲0
HINSTANCE hInstance; //當前應用程序的實例句柄
HICON hIcon; //最小化時的應用程序
HCURSOR hCursor; //窗口使用的光標
HBRUSH hbrBackground; //窗口客戶區的背景色
LPCTSTR lpszMenuName; //定義窗口菜單的資源名稱;如果窗口沒有菜單,則定義爲NULL
LPCTSTR lpszClassName; //指向窗口類的指針,LPSTR類型
HICON hIconSm; //小圖標的句柄,在任務欄顯示的圖標
};
1.2 創建程序窗口
將WNDCLASSEX結構的所有成員都設置爲所需的值後,下一步是把相關情況告訴Windows。使用RegisterClassEx()來完成這件事。假定WNDCLASSEX的對象是WindowsClass,則相應的語句如下所示:
RegisterClassEx(&WindowsClass);//註冊窗口類
接下來是創建窗口,由createWindow()函數完成。
HWND hWnd; //窗體句柄
hWnd = CreateWindow(
szAppName, // the window class name
_T("A Basic Window the Hard Way"), //標題欄文本
WS_OVERLAPPEDWINDOW, // 創建後的窗體樣式
CW_USEDEFAULT, // 窗體位置
CW_USEDEFAULT, // 左上角位置座標
CW_USEDEFAULT, // 窗體長度
CW_USEDEFAULT, // 窗體高度
nullptr, // 如果不是父窗體設置爲空
nullptr, // 沒有菜單設置爲空
hInstance, // Program Instance handle
nullptr // No window creation data
);
在調用CreateWindow()函數後,被創建的窗口現在已經存在,但是還沒有顯示在屏幕上。需要調用另一個Windows API函數將該窗口顯示出來:
ShowWindow(hWnd, nCmdShow);
第一個參數是CreateWindow()函數返回的句柄。第二個參數是給Winmain()傳遞的nCmdShow值,它指出在屏幕顯示窗口的方式。
1.3 初始化程序窗口
在調用 ShowWindow()函數後,該窗口將出現在屏幕上,但仍然沒有應用程序的內容。繪製工作區的最好方法是把繪製工作區的代碼放入WindowProc()函數,並使Windows給程序發送請求重畫工作區的消息。調用另一個Windows API函數UpdateWindow(),請求Windows給程序發送一條重畫窗口工作區的消息。調用該函數的窗口如下:
UpdateWindow(hwnd);
1.4 處理Windows消息
Windows有兩種消息:一種是排隊消息,Winmain()從隊列中提取這些消息進行處理,稱爲消息循環;另一種是致使Windows直接調用WindowsProc()函數的非排隊消息。我們在Winmain()函數的消息循環中所作的事情是從Windows爲應用程序排好的消息隊列中提取一條消息,然後請求Windows調用WindowsProc()函數來處理該消息。
while (GetMessage(&msg, nullptr, 0, 0) == TRUE) // Get any messages
{
TranslateMessage(&msg); // Translate the message
DispatchMessage(&msg); // Dispatch the message
}
- GetMessage()——從隊列中檢索一條消息
- TranslateMessage()——對檢索的消息執行必要的轉換
- DispatchMessage()——使Windows調用應用程序的WindowProc()函數來處理消息
2 處理Windows消息
使Windows以我們希望的方式運行的所有代碼都在程序的消息處理部分——WindowProc()函數
2.1 WindowProc()函數
WindowProc()函數的原型如下:
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message,WPARAM wParam, LPARAM lParam);
可以使用WINAPI替代CALLBACK,但後者更好地表達出這個函數的作用。每個參數的意義如下:
HWND hWnd : 一個句柄,事件引起消息發生的窗口
UINT message: 消息的ID,指出消息類型的32爲整數值
WPARAM wParam : 32位值,包含附加信息,決定於消息的種類
LPARAM lParam: 32位值
2.2 解碼Windows消息
switch (message) // Process selected messages
{
case WM_PAINT:
//繪製窗口工作區的代碼
break;
case WM_LBUTTONDOWN:
//處理鼠標左鍵按下時的事件
break;
case WM_LBUTTONUP:
//處理鼠標釋放時的事件
break;
......
case WM_DESTROY: // Window is being destroyed
//退出窗口
break;
default:
//其他默認語句
}
3 完整程序代碼
#include <windows.h>
#include <tchar.h>
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WindowClass; // Structure to hold our window's attributes
static LPCTSTR szAppName { _T("OFWin") }; // Define window class name
HWND hWnd; // Window handle
MSG msg; // Windows message structure
WindowClass.cbSize = sizeof(WNDCLASSEX); // Set structure size
// Redraw the window if the size changes
WindowClass.style = CS_HREDRAW | CS_VREDRAW;
// Define the message handling function
WindowClass.lpfnWndProc = WindowProc;
WindowClass.cbClsExtra = 0; // No extra bytes after the window class
WindowClass.cbWndExtra = 0; // structure or the window instance
WindowClass.hInstance = hInstance; // Application instance handle
// Set default application icon
WindowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
// Set window cursor to be the standard arrow
WindowClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
// Set gray brush for background color
WindowClass.hbrBackground = static_cast<HBRUSH>(GetStockObject(GRAY_BRUSH));
WindowClass.lpszMenuName = nullptr; // No menu
WindowClass.lpszClassName = szAppName; // Set class name
WindowClass.hIconSm = nullptr; // Default small icon
// Now register our window class
RegisterClassEx(&WindowClass);
// Now we can create the window
hWnd = CreateWindow(
szAppName, // the window class name
_T("A Basic Window the Hard Way"), // The window title
WS_OVERLAPPEDWINDOW, // Window style as overlapped
CW_USEDEFAULT, // Default screen position of upper left
CW_USEDEFAULT, // corner of our window as x,y.
CW_USEDEFAULT, // Default window size width ...
CW_USEDEFAULT, // ... and height
nullptr, // No parent window
nullptr, // No menu
hInstance, // Program Instance handle
nullptr // No window creation data
);
ShowWindow(hWnd, nCmdShow); // Display the window
UpdateWindow(hWnd); // Redraw window client area
// The message loop
while (GetMessage(&msg, nullptr, 0, 0) == TRUE) // Get any messages
{
TranslateMessage(&msg); // Translate the message
DispatchMessage(&msg); // Dispatch the message
}
return static_cast<int>(msg.wParam); // End, so return to Windows
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch (message) // Process selected messages
{
case WM_PAINT: // Message is to redraw the window
HDC hDC;
PAINTSTRUCT PaintSt; // Structure defining area to be drawn
hDC = BeginPaint(hWnd, &PaintSt) ; // Prepare to draw the window
// Get upper left and lower right of client area
RECT aRect; // A working rectangle
GetClientRect(hWnd, &aRect);
SetBkMode(hDC, TRANSPARENT); // Set text background mode
// Now draw the text in the window client area
DrawText(
hDC, // Device context handle
_T("But, soft! What light through yonder window breaks?"),
-1, // Indicate null terminated string
&aRect, // Rectangle in which text is to be drawn
DT_SINGLELINE | // Text format - single line
DT_CENTER | // - centered in the line
DT_VCENTER); // - line centered in aRect
EndPaint(hWnd, &PaintSt); // Terminate window redraw operation
return 0;
case WM_DESTROY: // Window is being destroyed
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}