• 【MFC】初识MFC(8)


    MFC简介

    微软提供的一个类库(Class Libraries),封装了Windows的API,并且包含一个应用程序框架。

    1、MFC是对前面窗口编程所用到的API进行封装,在Windows C基础上引入了C++面向对象的思想,简单而言就是把API函数和处理数据捆绑一起,做成了类——当然封装、继承、多态的机制也应用起来;

    2、MFC不仅仅是类库(类的一个集合),而且还是一个应用程序框架——已经搭建好了程序的“骨架”(毛坯房),程序员只需要”修修改改“,实现实际功能即可。

    理解:

    1、Windows C 当然需要掌握,MFC把API进行了一层简单的包装,函数名称、功能、调用的方法大体相同——所以学MFC,必先了解SDK编程;

    2、框架的意义在于,不是让程序员自主地调用MFC里的类,而是以框架为基础,被动地接受这种开发的模式——降低”编码“的难度?包括了两(三)种应用程序开发模式:

    • 单文档、多文档:应用程序(实现程序初始化)、框架(管理程序的各个窗口)、视图(用户区和数据展示窗口、一个程序可以有多个)、文档(程序的数据)的窗口程序模式——单文档、多文档应用程序。
    •   对话框:简化单文档、多文档中框架和文档,集中于窗口元素(按钮、文本框、编辑框、列表等等)的可视化设计模式。

     初衷是,MFC告诉你写代码的地方(类向导里的消息响应),你去实现自己的功能吧,就这么简单!

    后果是,初学者看到这些”自动“生成代码,被”框架“ 搞得不知所措,战战兢兢,不敢下手。也就是照葫芦画瓢,照写几个范例而已。

    因此,了解MFC的实现机制,MFC与SDK编程的关系,掌握这个只能被接受的框架,才能打开程序员设计的思路,放开手脚,自由地实现自己的功能。(推荐书:《深入浅出MFC》)

    MFC机制

    窗口编程 WinMain 函数是少不了,编译型的程序,入口总是避免不了。创建MFC程序(单文档、多文档、对话框)后在工程中并没发现WinMain函数,程序流程就搞不清楚了。

    MFC在程序编译时自动补上WinMain函数,函数的实现在VS的安装目录,比如

    C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\atlmfc\src\mfc    (VS2012)

     代码是这样的:

    1. // This is a part of the Microsoft Foundation Classes C++ library.
    2. // Copyright (C) Microsoft Corporation
    3. // All rights reserved.
    4. //
    5. // This source code is only intended as a supplement to the
    6. // Microsoft Foundation Classes Reference and related
    7. // electronic documentation provided with the library.
    8. // See these sources for detailed information regarding the
    9. // Microsoft Foundation Classes product.
    10. #include "stdafx.h"
    11. #include "sal.h"
    12. /
    13. // Standard WinMain implementation
    14. // Can be replaced as long as 'AfxWinInit' is called first
    15. int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    16. _In_ LPTSTR lpCmdLine, int nCmdShow)
    17. {
    18. ASSERT(hPrevInstance == NULL);
    19. int nReturnCode = -1;
    20. CWinThread* pThread = AfxGetThread();
    21. CWinApp* pApp = AfxGetApp();
    22. // AFX internal initialization
    23. if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
    24. goto InitFailure;
    25. // App global initializations (rare)
    26. if (pApp != NULL && !pApp->InitApplication())
    27. goto InitFailure;
    28. // Perform specific initializations
    29. if (!pThread->InitInstance())
    30. {
    31. if (pThread->m_pMainWnd != NULL)
    32. {
    33. TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
    34. pThread->m_pMainWnd->DestroyWindow();
    35. }
    36. nReturnCode = pThread->ExitInstance();
    37. goto InitFailure;
    38. }
    39. nReturnCode = pThread->Run();
    40. InitFailure:
    41. #ifdef _DEBUG
    42. // Check for missing AfxLockTempMap calls
    43. if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
    44. {
    45. TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
    46. AfxGetModuleThreadState()->m_nTempMapLock);
    47. }
    48. AfxLockTempMaps();
    49. AfxUnlockTempMaps(-1);
    50. #endif
    51. AfxWinTerm();
    52. return nReturnCode;
    53. }
    54. /

    AfxWinMain 就是WinMain函数(F12可以看定义,_tWinMain等等可以忽略),可能是为了避免MFC ”非纯面向对象“ 程序设计框架(纯面向对象语言是没用全局变量、全局函数),隐藏主函数也是一种办法吧!

    开始入口:

       CWinThread* pThread = AfxGetThread();
       CWinApp* pApp = AfxGetApp();

    CWinThread  是  CWinApp 的父类,可以暂时不关心。

    CWinApp  其实是创建MFC框架时,应用程序类的父类:

    并且,在应用程序的源文件中,可以找到一个全局对象的定义:

      AfxGetApp() 的函数功能可以猜到了吧,父类的指针 指向 派生类的 对象!

    多态的思想,这时候父类指针调用的函数 就 ”自动地切换“ 到程序员写的代码里去了。

    否则,MFC这么知道程序员定义了一个什么类?怎么去调用这个类定义的函数作为程序的切入点?

    AfxWinInit 函数在同层目录下的appinit.cpp文件里,这个函数的功能可以忽略。

    下面就是InitApplication,派生类没有改写,调用  CWinApp 类(同层目录appcore.cpp),这个可以忽略。

    重点当然是 InitInstance,这个函数在派生类中进行了改写,那么转入派生类应用程序类中的函数调用:(以单文档为例,其他都类似)

    1. BOOL CMFC01App::InitInstance()
    2. {
    3. // 如果一个运行在 Windows XP 上的应用程序清单指定要
    4. // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
    5. //则需要 InitCommonControlsEx()。否则,将无法创建窗口。
    6. INITCOMMONCONTROLSEX InitCtrls;
    7. InitCtrls.dwSize = sizeof(InitCtrls);
    8. // 将它设置为包括所有要在应用程序中使用的
    9. // 公共控件类。
    10. InitCtrls.dwICC = ICC_WIN95_CLASSES;
    11. InitCommonControlsEx(&InitCtrls);
    12. CWinApp::InitInstance();
    13. // 初始化 OLE 库
    14. if (!AfxOleInit())
    15. {
    16. AfxMessageBox(IDP_OLE_INIT_FAILED);
    17. return FALSE;
    18. }
    19. AfxEnableControlContainer();
    20. EnableTaskbarInteraction(FALSE);
    21. // 使用 RichEdit 控件需要 AfxInitRichEdit2()
    22. // AfxInitRichEdit2();
    23. // 标准初始化
    24. // 如果未使用这些功能并希望减小
    25. // 最终可执行文件的大小,则应移除下列
    26. // 不需要的特定初始化例程
    27. // 更改用于存储设置的注册表项
    28. // TODO: 应适当修改该字符串,
    29. // 例如修改为公司或组织名
    30. SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
    31. LoadStdProfileSettings(4); // 加载标准 INI 文件选项(包括 MRU)
    32. // 注册应用程序的文档模板。文档模板
    33. // 将用作文档、框架窗口和视图之间的连接
    34. CSingleDocTemplate* pDocTemplate;
    35. pDocTemplate = new CSingleDocTemplate(
    36. IDR_MAINFRAME,
    37. RUNTIME_CLASS(CMFC01Doc),
    38. RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
    39. RUNTIME_CLASS(CMFC01View));
    40. if (!pDocTemplate)
    41. return FALSE;
    42. AddDocTemplate(pDocTemplate);
    43. // 分析标准 shell 命令、DDE、打开文件操作的命令行
    44. CCommandLineInfo cmdInfo;
    45. ParseCommandLine(cmdInfo);
    46. // 调度在命令行中指定的命令。如果
    47. ///RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE
    48. if (!ProcessShellCommand(cmdInfo))
    49. return FALSE;
    50. // 唯一的一个窗口已初始化,因此显示它并对其进行更新
    51. m_pMainWnd->ShowWindow(SW_SHOW);
    52. m_pMainWnd->UpdateWindow();
    53. return TRUE;
    54. }

    比较重要的是所谓的”三口组“,CSingleDocTemplate(单文档模板)捆绑框架、视图、数据,一起生成对象。这里面当然最核心的问题就是:窗户函数,涉及到消息的处理,这也是程序员非常需要关注的。

    要找窗口函数,肯定要找窗口类的注册,查找的过程可以参考《深入浅出MFC》,结论是:从WINFRM.CPP 文件的Create 到  WINCORE.CPP 里的 CreateEx函数:

    1. BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
    2. LPCTSTR lpszWindowName, DWORD dwStyle,
    3. int x, int y, int nWidth, int nHeight,
    4. HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
    5. {
    6. ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) ||
    7. AfxIsValidAtom(lpszClassName));
    8. ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName));
    9. // allow modification of several common create parameters
    10. CREATESTRUCT cs;
    11. cs.dwExStyle = dwExStyle;
    12. cs.lpszClass = lpszClassName;
    13. cs.lpszName = lpszWindowName;
    14. cs.style = dwStyle;
    15. cs.x = x;
    16. cs.y = y;
    17. cs.cx = nWidth;
    18. cs.cy = nHeight;
    19. cs.hwndParent = hWndParent;
    20. cs.hMenu = nIDorHMenu;
    21. cs.hInstance = AfxGetInstanceHandle();
    22. cs.lpCreateParams = lpParam;
    23. if (!PreCreateWindow(cs))
    24. {
    25. PostNcDestroy();
    26. return FALSE;
    27. }
    28. AfxHookWindowCreate(this);
    29. HWND hWnd = CreateWindowEx(cs.dwExStyle, cs.lpszClass,
    30. cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
    31. cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
    32. #ifdef _DEBUG
    33. if (hWnd == NULL)
    34. {
    35. TRACE(traceAppMsg, 0, "Warning: Window creation failed: GetLastError returns 0x%8.8X\n",
    36. GetLastError());
    37. }
    38. #endif
    39. if (!AfxUnhookWindowCreate())
    40. PostNcDestroy(); // cleanup if CreateWindowEx fails too soon
    41. if (hWnd == NULL)
    42. return FALSE;
    43. ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
    44. return TRUE;
    45. }

    然后又转回WINFRM.CPP 等等,这个过程对初学者而言可以忽略,核心问题是了解MFC的程序流程。

    wndcls.lpfnWndProc = DefWindowProc;  这就是答案。

    后续《深入浅出MFC》中很大篇幅来讲解MFC的消息处理机制(三种类型消息),这个也暂时可以忽略。类向导-》消息响应、虚函数的使用就比较清晰了:

     可以不问来由,只管使用了,希望简单点,直接响应!否则改写DefWindowProc函数抓取所有窗口消息,也非常的灵活淡定!

  • 相关阅读:
    内联函数 inline
    基于Android studio的个人日程时间管理系统java
    文心一言 VS 讯飞星火 VS chatgpt (84)-- 算法导论8.2 1题
    git通过SSH指定秘钥文件克隆代码的三种方法
    高德地图JSAPI 2.0使用Java代码代替Nginx进行反向代理产生CORS跨域
    MySQL为什么用b+树
    git--工作区、暂存区、本地仓库、远程仓库
    艾美捷Immunochemistry MitoPT JC-1试剂盒
    300PLCmpi转以太网通过兴达易控MPI-ETH-XD1.0在铝型材时效炉中的应用
    JVM虚拟机 总结很到位
  • 原文地址:https://blog.csdn.net/yixiaobo2001/article/details/127920670