要能够轻松掌握MFC(希望它还没有过时)并不是件容易的事,这方面候捷的《深入浅出MFC》是本不错的书。但这书对读者也有一定的要求,既要熟悉C+SDK方式写程序,又要对C++继承和多态特性有所了解。为了能够更好的引导读者阅读,候捷先生在书中特别开辟了第一章和第二章来做基础知识的介绍。通过阅读它,我们也能很快地记住Win32程序的特点:
1. 程序进入点为WinMain函数
2. 要注册窗口类和产生窗口
3. 要有消息循环
4. 要有窗口过程函数(WndProc)
这四点用代码来表示,就会是下面这个样子:
// 1. 程序进入点为WinMain函数,相当于c中main函数
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev,
LPSTR lpCmdLine,int nCmdShow)
{
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
// 2. 要注册窗口类
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
// 2. 要产生窗口
hwnd = CreateWindow (szAppName, // window class name
TEXT ("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
// 3. 要有消息循环,通过while实现
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
// 4. 要有窗口过程函数(WndProc)
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
RECT rect ;
switch (message)
{
case WM_CREATE:
PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
GetClientRect (hwnd, &rect) ;
DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
而在《深入浅出MFC》第一章中,候捷用InitApplication和InitInstance这两个函数把注册窗口类和创建窗口的代码再次进行了包装,代码如下:
BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
// 2. 要注册窗口类
return RegisterClass (&wndclass);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hwnd = CreateWindow (szAppName, // window class name
TEXT ("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters
if (!hwnd)
return FALSE;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
return TRUE;
}
使用包装后的函数,入口函数WinMain就可以简化成下面这个样子:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev,
LPSTR lpCmdLine,int nCmdShow)
{
if (!InitApplication(hInst))
return FALSE;
if (!InitInstance(hInst, nCmdShow))
return FALSE;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
候捷为什么要多次一举,用InitApplication和InitInstance把这些代码行包装起来呢?原因是MFC就是这么做的。那MFC为什么要这样做?那是因为InitApplication里的代码(注册窗口类)一个应用程序(application)实例只需要执行一次,而InitInstance中的代码则属于窗口的范畴(见第六章)。
而且书中还有这样的一段话,
“几乎可以说CWinApp用来取代WinMain在SDK程序中的地位。这并不是说MFC程序没有WinMain(稍后我会解释),而是说传统上SDK程序的WinMain所完成的工作现在由WinApp的三个函数完成:
virtual BOOL InitApplication();
virtual BOOL InitInstance();
virtual int Run();
WinMain知识扮演役使它们的角色。”
这样一来,MFC程序中WinMain的内容大概可以用以下的伪代码来表示:
int WINAPI WinMain(...)
{
// Call WinApp::InitApplication() 2. 注册窗口类
// Call WinApp::InitInstance(); 2. 创建窗口
// Call WinApp::Run(); 3. 消息循环
}
确实够简单,但是文章开头提到的第4点,窗口过程(WndProc)哪里去了?答案在CFrameWnd这样的窗口类中找。
注:以上理解与MFC实际的行为有出入,但对于理解MFC程序的运行有些帮助。更多的信息请阅读《深入浅出MFC》第六章。