您的位置:首页 > 编程语言

VS2010/MFC编程入门之三(MFC应用程序框架分析)

2014-04-23 13:22 731 查看
上一讲鸡啄米讲的是VS2010应用程序工程中文件的组成结构,可能大家对工程的运行原理还是很模糊,理不出头绪,毕竟跟C++编程入门系列中的例程差别太大。这一节鸡啄米就为大家分析下MFC应用程序框架的运行流程。

一.SDK应用程序与MFC应用程序运行过程的对比

程序运行都要有入口函数,在之前的C++教程中都是main函数,而Windows应用程序的入口函数是WinMain函数,MFC程序也是从WinMain函数开始的。下面鸡啄米就给出用Windows
SDK写的“HelloWorld”程序,与应用程序框架进行对比,这样能更好的了解框架是怎样运行的。Windows SDK开发程序就是不使用MFC类库,直接用WindowsAPI函数进行软件开发。鸡啄米不是要讲解SDK开发,只是为了对比而简单介绍,至于SDK开发可以在大家学完MFC以后选择是否要研究,一般来说有简单了解就可以了。

SDK应用程序

首先,给出WindowsSDK应用程序“HelloWorld”的源码:

C++代码

1. #include <windows.h>

2.

3. LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);

4.

5. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)

6. {

7. const static TCHAR appName[] = TEXT("Hello world");

8. WNDCLASSEX myWin;

9. myWin.cbSize = sizeof(myWin);

10. myWin.style = CS_HREDRAW | CS_VREDRAW;

11. myWin.lpfnWndProc = myWndProc;

12. myWin.cbClsExtra = 0;

13. myWin.cbWndExtra = 0;

14. myWin.hInstance = hInstance;

15. myWin.hIcon = 0;

16. myWin.hIconSm = 0;

17. myWin.hCursor = 0;

18. myWin.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

19. myWin.lpszMenuName = 0;

20. myWin.lpszClassName = appName;

21. //Register

22. if (!RegisterClassEx(&myWin)) return 0;

23. const HWND hWindow = CreateWindow(

24. appName,

25. appName,

26. WS_OVERLAPPEDWINDOW,

27. CW_USEDEFAULT,

28. CW_USEDEFAULT,

29. CW_USEDEFAULT,

30. CW_USEDEFAULT,

31. 0,

32. 0,

33. hInstance,

34. 0);

35. ShowWindow(hWindow,iCmdShow);

36. UpdateWindow(hWindow);

37. {

38. MSG msg;

39. while(GetMessage(&msg,0,0,0))

40. {

41. TranslateMessage(&msg);

42. DispatchMessage(&msg);

43. }

44. return (int)msg.wParam;

45. }

46. }

47.

48. LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)

49. {

50. if (msg==WM_PAINT)

51. {

52. PAINTSTRUCT ps;

53. const HDC hDC = BeginPaint(hWindow,&ps);

54. RECT rect;

55. GetClientRect(hWindow,&rect);

56. DrawText(hDC,TEXT("HELLO WORLD"),-1,&rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

57. EndPaint(hWindow,&ps);

58. return 0;

59. }

60. else if (msg==WM_DESTROY)

61. {

62. PostQuitMessage(0);

63. return 0;

64. }

65. return DefWindowProc(hWindow,msg,wParam,lParam);

66. }

上面的程序运行的流程是:进入WinMain函数->初始化WNDCLASSEX,调用RegisterClassEx函数注册窗口类->调用ShowWindow和UpdateWindow函数显示并更新窗口->进入消息循环。关于消息循环再简单说下,Windows应用程序是消息驱动的,系统或用户让应用程序进行某项操作或完成某个任务时会发送消息,进入程序的消息队列,然后消息循环会将消息队列中的消息取出,交予相应的窗口过程处理,此程序的窗口过程函数就是myWndProc函数,窗口过程函数处理完消息就完成了某项操作或任务。本例是要显示“HELLOWORLD”字符串,UpdateWindow函数会发送WM_PAINT消息,但是此消息不经过消息队列而是直接送到窗口过程处理,在窗口过程函数中最终绘制了“HELLO
WORLD”字符串。

MFC应用程序

下面是MFC应用程序的运行流程,通过MFC库中代码进行分析:

首先在HelloWorld.cpp中定义全局对象theApp:CHelloWorldApp
theApp;。调用CWinApp和CHelloWorldApp的构造函数后,进入WinMain函数(位于appmodul.cpp中)。

C++代码

1. extern "C" int WINAPI

2. _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

3. _In_ LPTSTR lpCmdLine, int nCmdShow)

4. #pragma warning(suppress: 4985)

5. {

6. // call shared/exported WinMain

7. return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

8. }

在TCHAR.h中,有此定义:#define_tWinMain WinMain,所以这里的_tWinMain就是WinMain函数。它调用了AfxWinMain函数(位于WinMain.cpp中)。

C++代码

1. int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)

2. {

3. .............略

4. // App global initializations (rare)

5. if (pApp != NULL && !pApp->InitApplication())

6. goto InitFailure;

7.

8. if (!pThread->InitInstance())

9. {

10. .........略

11. }

12.

13. // Run函数位于THRDCORE.cpp中,由此函数进入消息循环

14. nReturnCode = pThread->Run();

15.

16. ..............略

17.

18. return nReturnCode;

19. }

上面InitInstance函数的代码如下:

C++代码

1. BOOL CTestApp::InitInstance()

2. {

3. .............略

4. CSingleDocTemplate* pDocTemplate;

5. pDocTemplate = new CSingleDocTemplate(

6. IDR_MAINFRAME,

7. RUNTIME_CLASS(CTestDoc),

8. RUNTIME_CLASS(CMainFrame), // main SDI frame window

9. RUNTIME_CLASS(CTestView));

10. if (!pDocTemplate)

11. return FALSE;

12. AddDocTemplate(pDocTemplate);

13. // Parse command line for standard shell commands, DDE, file open

14.

15. CCommandLineInfo cmdInfo;

16. ParseCommandLine(cmdInfo);

17.

18. //ProcessShellCommand位于AppUI2.cpp中,注册并创建窗口

19. if (!ProcessShellCommand(cmdInfo))

20. return FALSE;

21.

22. m_pMainWnd->ShowWindow(SW_SHOW);

23. m_pMainWnd->UpdateWindow();

24.

25. return TRUE;

26. }

InitInstance中的ProcessShellCommand函数又调用了CMainFrame的LoadFrame函数注册并创建了窗口,执行完ProcessShellCommand函数以后,调用了m_pMainWnd的ShowWindow和UpdateWindow函数显示并更新框架窗口。这些是不是与上面的SDK程序十分类似?

接下来该是消息循环了,上面的AfxWinMain函数中调用了pThread的Run函数(位于THRDCORE.cpp中),在Run中包含了消息循环。Run函数的代码如下:

C++代码

1. int CWinThread::Run()

2. {

3. .............略

4. // phase2: pump messages while available

5. do

6. {

7. // pump message, but quit on WM_QUIT

8. if (!PumpMessage())

9. return ExitInstance();

10.

11. // reset "no idle" state after pumping "normal" message

12. if (IsIdleMessage(&m_msgCur))

13. {

14. bIdle = TRUE;

15.

16. lIdleCount = 0;

17.

18. }

19. } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));

20. ..............略

21. }

22.

23. BOOL CWinThread::PumpMessage()

24. {

25. return AfxInternalPumpMessage();

26. }

27.

28. BOOL AFXAPI AfxInternalPumpMessage()

29. {

30. _AFX_THREAD_STATE *pState = AfxGetThreadState();

31.

32. if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))

33. {

34. .............略

35. }

36. ...............略

37. if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))

38. {

39. ::TranslateMessage(&(pState->m_msgCur));

40. ::DispatchMessage(&(pState->m_msgCur));

41. }

42.

43. return TRUE;

44. }

我们看到PumpMessage中通过调用GetMessage、TranslateMessage、DispatchMessage等建立了消息循环并投递消息。

窗口过程函数AfxWinProc形式如下:

C++代码

1. LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam, LPARAM lParam)

2. {

3. ……

4. CWnd*pWnd=CWnd::FromHandlePermanent(hWnd);

5. ReturnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);

6. }

两者运行过程对比

到此,通过对比可以发现,MFC应用程序的运行流程与SDK程序是类似的,都是先进行一些初始化过程,再注册并创建窗口,然后显示、更新窗口,最后进入消息循环,消息都由窗口过程函数处理。现在大家是不是觉得有些头绪了?在运行流程上有基本的掌握即可。

二.MFC应用程序框架主要类之间的关系

在第二讲中,给大家演示了如何利用应用程序向导生成单文档应用程序框架,可以看到程序的基本框架和必要的代码都自动生成了,上一讲又讲解了文件组成结构,实际上在前面自动生成的框架中比较重要的类包括以下几个:CHelloWorldApp、CMainFrame、CHelloWorldDoc和CHelloWorldView,至于其他的类比如CClassView、CFileView等都是在框架窗口(CMainFrame)上创建的面板等,不是必要的。
鸡啄米就四个主要类的关系简单讲下,CHelloWorldApp类处理消息,将收到的消息分发给相应的对象。CMainFrame是视图CHelloWorldView的父窗口,视图CHelloWorldView就显示在CMainFrame的客户区中。视图类CHelloWorldView用来显示文档类CHelloWorldDoc中的数据,并根据对视图类的操作修改文档类的数据。一个视图类只能跟一个文档类相联系,而一个文档类可以跟多个视图类相联系。关于视图类和文档类的关系后面会详细讲解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐