开发 Windows API的Windows程序,需要编写两个函数。一个是Winmain()函数,程序的执行从这里开始,基本的初始化工作也在这里完成。另一个是WindowProc()函数,该函数由Windows调用,用来给应用程序传递消息。Winmain与WindowProc函数通过调用系统的API与Windows通信,如图所示:
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) 处理部分消息
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; //小图标的句柄,在任务栏显示的图标
};
将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值,它指出在屏幕显示窗口的方式。
在调用 ShowWindow()函数后,该窗口将出现在屏幕上,但仍然没有应用程序的内容。绘制工作区的最好方法是把绘制工作区的代码放入WindowProc()函数,并使Windows给程序发送请求重画工作区的消息。调用另一个Windows API函数UpdateWindow(),请求Windows给程序发送一条重画窗口工作区的消息。调用该函数的窗口如下:
UpdateWindow(hwnd);
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
}
使Windows以我们希望的方式运行的所有代码都在程序的消息处理部分——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位值
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:
//其他默认语句
}
#include
#include
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(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(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);
}