免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

VS2017 MFC源码包实战:从编译调试到内存泄漏排查

VS2017 MFC源码包实战:从编译调试到内存泄漏排查 简介这份资源是任哲《MFC Windows应用程序设计》第三版的配套VS2017源码包面向正在学习Windows桌面开发、希望从API编程过渡到MFC框架的C开发者也适合高校课程实验与课程设计参考。压缩包共约2000个文件体积12.73MB以572个h头文件、346个cpp源文件为核心配合75个rc资源脚本、148个ico图标与86个bmp位图另有80个vcxproj工程文件、74个sln解决方案及若干def、lib、dll、rgs等工程与组件文件覆盖从框架搭建到动态库开发的完整示例。内容围绕CWinApp、CFrameWnd、文档视图结构、对话框与控件、消息映射、DLL及异常处理等知识点展开示例按章节编号组织便于对照教材逐章调试运行。目前已有799人学习下载适合需要动手实践、通过可编译工程理解MFC运行机制与UI交互流程的读者。1. 从一份 VS2017 源码包说起MFC 桌面程序到底还值不值得学很多人第一次接触 Windows 桌面开发都是从一份 MFC 源码包开始的。你手上这份《MFC WINDOWS应用程序设计(第3版)》配套的 VS2017 源码本质上是一堆用 C 写的、跑在 Win32 之上的窗口程序集合。它解决的不是「怎么写出一个炫酷界面」而是「怎么用最原始的方式把窗口、消息、控件、文档/视图这套机制跑通」。适合谁适合那些被 Qt、Electron 惯坏、却突然要维护一套老工控上位机、医疗设备界面、或者银行内部工具的人。MFC 教程满天飞但真正能让你把 VS2017 打开、编译、改一个按钮响应函数、再定位内存泄漏的往往是这种带完整源码的工程包。别指望它教你现代 C它教的是 Windows 消息循环的底层脾气——你点一下鼠标系统怎么把WM_LBUTTONDOWN塞进你的OnLButtonDown这才是 MFC 四大类CWinApp、CFrameWnd、CView、CDocument真正在干的事。2. 把源码包跑起来VS2017 环境配置与第一个可编译工程2.1 为什么是 VS2017 而不是更新的版本这份源码包明确标注了 VS2017这不是随便写的。MFC 在 VS2017 里的工具集是 v141而 VS2019 是 v142VS2022 是 v143。高版本打开低版本工程通常会触发「重定向项目」对话框重定向后大部分代码能编但有几个坑stdafx.h预编译头在 VS2017 之后默认改名pch.h如果你直接重定向会报一堆「无法打开预编译头文件」。另一个是 MFC 库的 Unicode 与多字节字符集设置VS2017 默认新建工程是 Unicode但老源码包很多是 Multi-Byte重定向后字符集不会自动改导致CString和char*混用报错。我一般会先装 VS2017 离线安装包勾选「使用 C 的桌面开发」右侧务必勾上「MFC 和 ATL 支持」。如果你只有 VS2019/2022也能跑但建议在项目属性里把「平台工具集」手动改成你本机有的版本而不是无脑点重定向。2.2 从 .rar 到第一个能跑的窗口拿到.rar后解压路径里不要有中文和空格这是血泪经验。MFC 的资源编译器rc.exe对中文路径的容忍度极低经常报「无法打开文件 xxx.rc」。解压后你会看到若干.sln和.vcxproj用 VS2017 打开.sln先别急着 F5。# 解压后建议的目录结构示例 D:\MFC_Book_Code\ ├── Chapter03\ │ ├── Ex03_01\Ex03_01.sln │ └── Ex03_02\Ex03_02.sln ├── Chapter05\ │ └── ...打开第一个工程后做三件事第一右键解决方案 → 属性 → 检查「Windows SDK 版本」是否为你本机已安装的版本VS2017 默认 10.0.17763.0如果你没装这个 SDK改成你有的第二项目属性 → C/C → 代码生成 → 运行库Debug 下应为「多线程调试 DLL (/MDd)」如果源码包是/MTd而你链接的是 DLL 版 MFC会报LNK2005重定义第三项目属性 → 常规 → 字符集确认是「使用 Unicode 字符集」还是「使用多字节字符集」这个必须和源码一致。// 典型的 MFC 入口点在 VS2017 中编译通过的前提是字符集匹配 #include stdafx.h #include MyApp.h #include MyFrame.h CMyApp theApp; // 全局应用对象MFC 四大类之一 BOOL CMyApp::InitInstance() { CWinApp::InitInstance(); CMyFrame* pFrame new CMyFrame(); m_pMainWnd pFrame; pFrame-ShowWindow(SW_SHOW); pFrame-UpdateWindow(); return TRUE; }这段代码的逻辑是theApp是全局唯一应用对象MFC 的WinMain在库内部已经写好它会调用InitInstance。参数说明SW_SHOW表示以正常方式显示窗口UpdateWindow强制立即发送WM_PAINT。如果你把m_pMainWnd忘了赋值程序启动后窗口一闪而过这是新手最常见的翻车点。2.3 编译不过时的排查顺序先看「输出」窗口不要只看「错误列表」。MFC 的报错经常是连锁的第一个错误才是根因。常见顺序stdafx.h找不到 → 预编译头设置问题afxwin.h找不到 → 没装 MFC 组件LNK1104无法打开mfc140ud.lib→ 运行库或 MFC 链接方式不匹配。把这三个查完80% 的编译问题就没了。3. 消息映射与四大类看懂源码包里的骨架3.1 消息映射表到底在干什么MFC 最让人迷惑的就是BEGIN_MESSAGE_MAP和ON_COMMAND这些宏。它们不是 C 语法是宏展开后生成了一张静态表把「消息 ID」和「成员函数指针」关联起来。Windows 把消息发给窗口过程WndProcMFC 的WndProc查这张表找到对应函数就调用。// MyFrame.h class CMyFrame : public CFrameWnd { DECLARE_MESSAGE_MAP() public: afx_msg void OnPaint(); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); }; // MyFrame.cpp BEGIN_MESSAGE_MAP(CMyFrame, CFrameWnd) ON_WM_PAINT() ON_WM_LBUTTONDOWN() END_MESSAGE_MAP() void CMyFrame::OnLButtonDown(UINT nFlags, CPoint point) { CString str; str.Format(_T(点击坐标: %d, %d), point.x, point.y); AfxMessageBox(str); CFrameWnd::OnLButtonDown(nFlags, point); }逻辑说明DECLARE_MESSAGE_MAP在类里声明静态表BEGIN_MESSAGE_MAP在 cpp 里定义。ON_WM_LBUTTONDOWN不需要你写消息 IDMFC 内部用WM_LBUTTONDOWN常量。参数point是客户区坐标nFlags表示是否按了 Ctrl/Shift。注意最后要调用基类同名函数否则某些默认行为比如窗口激活会丢。3.2 四大类在源码包里的分工CWinApp管进程和消息循环一个程序只有一个CFrameWnd是主窗口框架管菜单、工具栏、状态栏CView是客户区负责显示和交互CDocument管数据负责存盘和加载。源码包里如果出现CSingleDocTemplate或CMultiDocTemplate那就是文档/视图架构。你改代码时数据放CDocument显示放CView别在CView里存业务数据否则打印和保存时会发现数据对不上。3.3 用 VS2017 调试消息响应的技巧在OnLButtonDown里下断点F5 运行点击窗口断下后看「调用堆栈」。你会看到AfxWndProc→CWnd::OnWndMsg→CMyFrame::OnLButtonDown。这个堆栈就是 MFC 消息分发的黑匣子。如果断点没进检查三件事消息映射宏写没写、函数签名是否带afx_msg、类是否DECLARE_MESSAGE_MAP。这三个缺一个消息就石沉大海。4. 避坑与排查源码包在 VS2017 下的五个高频翻车点4.1 现象编译报「无法打开预编译头文件 stdafx.h」原因VS2017 新建工程默认用pch.h但老源码包用stdafx.h且项目属性里「预编译头」指向的文件名没改。解决项目属性 → C/C → 预编译头 → 预编译头文件改成stdafx.h同时确保stdafx.cpp的「预编译头」设置为「创建 (/Yc)」其他文件为「使用 (/Yu)」。4.2 现象运行时报「Debug Assertion Failed」原因多半是CString或CWnd*指针为空或者对话框资源 ID 找不到。源码包里资源 ID 是硬编码在resource.h的如果你合并工程时resource.h冲突ID 会错位。解决检查resource.h是否有重复定义用「资源视图」重新确认对话框 ID在DoDataExchange里检查DDX_Control绑定的控件是否存在。4.3 现象中文乱码或AfxMessageBox显示问号原因字符集不匹配。源码是 Multi-Byte你工程是 Unicode_T(中文)在 Unicode 下是L中文但资源文件里的对话框文字还是 ANSI。解决项目属性 → 常规 → 字符集改成「使用多字节字符集」或者把所有字符串用_T()包起来资源文件用 VS 的「资源编辑器」重新保存为 Unicode。4.4 现象链接报LNK2019无法解析的外部符号原因用了某个 MFC 类但没包含对应头文件或者没链接对应的 lib。比如用了CListCtrl却没#include afxcmn.h。解决在stdafx.h里统一包含常用 MFC 头afxwin.h、afxext.h、afxcmn.h、afxdisp.h。如果还不行看「输出」窗口里具体缺哪个符号反查 MSDN。4.5 现象程序退出时崩溃在ExitInstance原因全局对象析构顺序问题或者CDocument里delete了不该删的指针。MFC 的theApp是全局对象它的析构在main之后如果此时还访问窗口对象必崩。解决把清理逻辑放在ExitInstance里不要放在析构函数用delete后立即置空指针避免二次释放。5. 从源码包到自己的 MFC 工具三个可复用的改造技巧5.1 把对话框程序改成单文档程序源码包里很多是对话框模板但你想做记事本那种带菜单和编辑区的。步骤用 VS2017 新建「MFC 应用程序」→ 选「单文档」→ 在CView派生类里重写OnDraw或直接用CEditView。CEditView自带编辑、复制粘贴、查找替换改造成本最低。如果你要近似 Windows 记事本直接继承CEditView然后在InitInstance里m_pMainWnd-ShowWindow(SW_SHOWMAXIMIZED)。// 用 CEditView 快速实现一个记事本骨架 class CNotepadView : public CEditView { DECLARE_DYNCREATE(CNotepadView) public: virtual void OnDraw(CDC* pDC) override { // CEditView 自己处理绘制这里留空即可 } };参数说明DECLARE_DYNCREATE让框架能动态创建视图CEditView内部已经处理了WM_CHAR和剪贴板。你只需要在CDocument里处理文件读写Serialize函数里用CArchive读写CString。5.2 用CListCtrl显示数据并支持排序源码包里如果有列表控件通常只显示不排序。加排序在CListCtrl派生类里处理LVN_COLUMNCLICK调用SortItems传一个静态比较函数。比较函数里用GetItemText取文本转成数字或字符串比较。注意SortItems的lParam可以传this指针方便在比较函数里访问控件。5.3 检测内存泄漏MFC 自带的CMemoryState在InitInstance开头和ExitInstance结尾分别创建CMemoryState对象调用Checkpoint和Difference。如果输出窗口报「Detected memory leaks」它会列出泄漏的字节数和分配序号。配合_CrtSetBreakAlloc(序号)可以在分配时断下。这个技巧对排查CString内存泄漏特别有用因为CString内部是new出来的。// 在 App 类里加内存检查 #ifdef _DEBUG CMemoryState msOld, msNew, msDiff; msOld.Checkpoint(); #endif // ... 程序运行 ... #ifdef _DEBUG msNew.Checkpoint(); if (msDiff.Difference(msOld, msNew)) { TRACE(_T(检测到内存泄漏\n)); msDiff.DumpStatistics(); } #endif逻辑说明Checkpoint记录当前堆状态Difference比较两次快照。DumpStatistics输出泄漏块的大小和数量。注意这个机制只对new/delete有效对malloc无效。如果你在 MFC 里混用了 STL 容器STL 的分配器可能绕过 MFC 的调试堆导致漏报。5.4 用libxl读写 Excel 的注意点热词里有人问「mfc 程序中怎么使用 libxl 库」。libxl是第三方库不是 MFC 自带。用法把libxl.lib和libxl.dll放到工程目录项目属性 → 链接器 → 输入 → 附加依赖项加libxl.lib代码里#include libxl.h。注意libxl的Book*和Sheet*需要手动release否则内存泄漏。另外它不支持.xlsx的某些新特性读老.xls更稳。5.5 验证改造是否成功三个检查点第一编译零警告至少零LNK警告第二用CMemoryState跑一遍无泄漏输出第三把程序拷到一台没装 VS 的干净 Windows 上看是否缺mfc140u.dll。如果缺把「运行库」改成/MT静态链接或者用「发布」配置重新编译。我一般习惯在发布前用Dependency Walker看一眼依赖虽然它有点老但查 MFC 依赖依然好使。希望帮到你。本文还有配套的精品资源点击获取
返回列表