ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

VS2022中MFC环境配置与记事本级应用开发实战

VS2022中MFC环境配置与记事本级应用开发实战 简介本资源是一份面向C初学者与Windows桌面开发入门者的系统性教程文档聚焦VS2022环境下MFC框架的实战应用旨在帮助读者从零掌握可视化窗口程序开发能力。文档深入浅出地讲解VC与Visual Studio的关系、MFC核心机制如窗口封装、应用程序框架、资源管理并以单文档应用程序生成为例完整演示工程创建、解决方案结构、向导配置等关键流程兼顾原理认知与实操落地。资源为单个33KB的DOCX文档内容结构清晰涵盖VC版本演进对比、MFC与Win32 API关系辨析、VS2022安装建议及典型开发场景说明适合作为自学笔记或教学补充材料。目前已有4549人学习下载内容源自实践导向的“鸡啄米”系列教程语言平实、案例具体能有效降低MFC入门门槛助读者快速构建可运行的GUI程序基础能力。1. VS2022 MFC 入门不是装完就能写记事本而是先搞清“为什么MFC在VS2022里像被藏起来的旧工具箱”你下载好 Visual Studio 2022点开新建项目搜“MFC”结果只看到灰掉的模板、报错提示“未安装MFC组件”或者好不容易建出来一编译就卡在atltracegeneral、dumpcont.cpp(23)这类路径诡异的错误里——这不是你手残是 VS2022 对 MFC 的支持逻辑变了。它没取消 MFC但把它从“默认出厂配置”降级为“按需显式启用”的可选工作负载。很多新手照着十年前的《MFC编程入门》PDF比如标题里的.docx一步步操作到第3步就断在环境上没有afxwin.h、找不到CWinApp派生类向导、资源视图空白、甚至#include afxcmn.h直接红波浪线。这不是文档过时是 VS2022 的工程生成器、平台工具集、Windows SDK 绑定和 MFC 运行时三者之间存在隐式耦合。这篇笔记不讲“MFC是什么”而是带你用 VS2022 原生方式从零拉起一个能编译、能调试、能响应按钮点击、能读写文件的最小可运行 MFC 窗口程序——目标明确让那个“近似 Windows 记事本”的雏形在你本地 VS2022 里真实跑起来且知道每一步为什么非这样不可。适合刚转 C 桌面开发、被 CMake/Qt 新生态绕晕后想回溯 Win32 底层逻辑的工程师也适合需要快速交付轻量级内部工具、又不想引入 Electron 或 .NET MAUI 重量级框架的团队。2. 安装与验证VS2022 中 MFC 不是“有无”而是“在哪装、装哪版、怎么验”MFC 在 VS2022 中不是“装了 VS 就自带”而是作为独立工作负载Workload按需安装。它依赖特定版本的 Windows SDK 和平台工具集Platform Toolset三者不匹配新建项目就失败。别跳过这步——90% 的“MFC 模板灰色”“无法创建向导”问题根子都在这里。2.1 用 Visual Studio Installer 精准安装 MFC 工作负载打开Visual Studio Installer不是 VS IDE是独立的安装管理器找到你已安装的 VS2022 实例点击“修改”。在工作负载Workloads页签中必须勾选以下两项✅Desktop development with CC 桌面开发✅Universal Windows Platform developmentUWP 开发——别跳过MFC 部分 UI 控件如 WebView2 嵌入依赖 UWP 运行时头文件不装会导致后续#include WebView2.h报错然后切换到“单个组件”Individual components页签搜索并勾选✅C MFC for latest v143 build tools (x86 x64)✅C ATL for latest v143 build tools (x86 x64)✅Windows 10/11 SDK (10.0.22621.0 或更高)——推荐选最新版但必须与你项目设置一致✅CMake tools for Visual Studio可选但建议勾上方便后期集成 CMakeLists提示不要勾选 “C MFC for v142 build tools” 或更老版本。VS2022 默认使用 v143 工具集对应 MSVC 14.3混用 v142 会导致链接器找不到mfc142u.dll等运行时库报 LNK2005 重定义错误。安装完成后务必重启 Visual Studio Installer 和 VS2022 IDE。这是硬性要求组件注册表项不会热加载。2.2 创建项目前确认 Windows SDK 与平台工具集绑定关系新建项目前先验证环境是否就绪。打开 VS2022 →创建新项目→ 搜索 “MFC” → 选择“MFC 应用程序”注意不是“MFC 类库”或“MFC DLL”。点击下一步后在配置页面你会看到两个关键下拉框配置项正确值示例错误表现Windows SDK 版本10.0.22621.0或你安装的最新版显示Latest但实际为空白或显示10.0.19041.0已弃用平台工具集Visual Studio 2022 (v143)显示v142VS2019 工具集或v141VS2017如果这两个下拉框不可选或显示异常说明步骤 2.1 的安装未生效。此时不要强行创建返回 Installer 重新勾选并修复安装。2.3 验证安装成功的最小代码不靠向导手写一个能编译的 MFC 窗口很多教程让你直接点“MFC 应用程序向导”但向导会生成大量样板代码一旦出错你根本分不清是逻辑问题还是环境问题。我们先绕过向导用最简代码验证 MFC 运行时是否真正可用// 文件名MinimalMFC.cpp #include afxwin.h class CMinimalApp : public CWinApp { public: virtual BOOL InitInstance() override { m_pMainWnd new CFrameWnd(); m_pMainWnd-Create(NULL, _T(Minimal MFC)); m_pMainWnd-ShowWindow(m_nCmdShow); m_pMainWnd-UpdateWindow(); return TRUE; } } theApp;编译命令在 VS2022 开发人员命令提示符中执行cl /c /EHsc /MD /D_AFXDLL /IC:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\atlmfc\include MinimalMFC.cpp link MinimalMFC.obj /SUBSYSTEM:WINDOWS /MANIFEST:NO /LIBPATH:C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\atlmfc\lib\native /LIBPATH:C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\lib\native /LIBPATH:C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64 /DEFAULTLIB:mfcd.lib /DEFAULTLIB:msvcrt.lib /ENTRY:wWinMainCRTStartup参数说明/I指定 MFC 头文件路径路径中的14.39.33519是你的 MSVC 版本号请用资源管理器确认实际路径/LIBPATH依次指定 MFC 库、C 运行时库、Windows SDK 库路径/DEFAULTLIB:mfcd.lib强制链接动态版 MFCDebug 版生产环境用mfcu.lib/ENTRY:wWinMainCRTStartup是 Windows GUI 程序入口不是main如果成功生成MinimalMFC.exe并双击运行弹出空白窗口说明 VS2022 的 MFC 环境已打通。这是你后续所有开发的“地基验证”比任何向导都可靠。3. 从零构建“近似记事本”的 MFC 程序用向导打底手动增补核心功能标题里“mfc制作一个近似于windows记事本怎么做”是高频搜索需求。我们不复刻完整记事本而是实现其三大骨架能力文本编辑区CRichEditCtrl、文件打开/保存CFileDialog、菜单栏响应ON_COMMAND。重点在于向导生成的代码哪些必须改、哪些不能动、哪些要自己加。3.1 用 MFC 向导创建基础框架关键选项设置打开 VS2022 → 新建项目 → “MFC 应用程序” → 项目名设为NotepadLike→ 下一步应用程序类型选择“单文档”SDI不是多文档MDI或基于对话框。记事本是典型 SDI。复合文档支持取消勾选。记事本不需要 OLE 嵌入。项目样式选择“MFC 标准”不是“专业用户”或“Windows 通用”。标准样式生成CMainFrame、CChildFrame、CNotepadLikeDoc、CNotepadLikeView四大类结构清晰。高级功能✅ 勾选“打印和打印预览”记事本有打印菜单✅ 勾选“Rich Edit 控件”这是支持字体、颜色、换行的关键原生CEdit不支持富文本❌ 取消“ActiveX 控件”增加复杂度非必需生成的类保持默认CNotepadLikeApp,CNotepadLikeDoc,CNotepadLikeView,CMainFrame点击完成。VS2022 会生成约 20 个文件。此时编译运行你会看到一个带菜单栏、工具栏、状态栏的空白窗口——这是记事本的“壳”。3.2 替换编辑控件用 CRichEditCtrl 替代默认 CEdit默认CNotepadLikeView使用CEdit它不支持换行、字体设置、滚动条自适应。我们必须换成CRichEditCtrl打开NotepadLikeView.h在类声明中添加成员变量// NotepadLikeView.h class CNotepadLikeView : public CView { protected: // create from serialization only CNotepadLikeView() noexcept; DECLARE_DYNCREATE(CNotepadLikeView) // Attributes public: CNotepadLikeDoc* GetDocument() const; // Operations public: // Overrides public: virtual void OnDraw(CDC* pDC) const override; // overridden to draw this view virtual BOOL PreCreateWindow(CREATESTRUCT cs) override; protected: virtual BOOL OnPreparePrinting(CPrintInfo* pInfo) override; virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo) override; virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo) override; // Implementation public: virtual ~CNotepadLikeView(); #ifdef AFX_DESIGN_TIME virtual void OnObjectProperties(CObject* pObject) override; #endif CRichEditCtrl m_richEdit; // ← 新增富文本编辑控件 protected: afx_msg void OnFilePrintPreview(); afx_msg void OnRButtonUp(UINT nFlags, CPoint point); afx_msg void OnContextMenu(CWnd* pWnd, CPoint point); DECLARE_MESSAGE_MAP() };打开NotepadLikeView.cpp在PreCreateWindow中禁用默认编辑框并在OnInitialUpdate中创建CRichEditCtrl// NotepadLikeView.cpp BOOL CNotepadLikeView::PreCreateWindow(CREATESTRUCT cs) { // 关键禁用父类 CView 的默认窗口创建我们自己创建 RichEdit cs.style ~(WS_BORDER | WS_CHILD | WS_VISIBLE); return CView::PreCreateWindow(cs); } void CNotepadLikeView::OnInitialUpdate() { CView::OnInitialUpdate(); // 创建 RichEdit 控件填充整个客户区 CRect rect; GetClientRect(rect); m_richEdit.Create(WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL, rect, this, AFX_IDW_PANE_FIRST); // 设置字体为 Consolas大小 10pt记事本风格 LOGFONT lf {0}; _tcscpy_s(lf.lfFaceName, _T(Consolas)); lf.lfHeight -MulDiv(10, GetDeviceCaps(GetDC()-GetSafeHdc(), LOGPIXELSY), 72); CFont font; font.CreateFontIndirect(lf); m_richEdit.SetFont(font); // 加载初始文本可选 m_richEdit.SetWindowText(_T(欢迎使用 MFC 记事本雏形)); }修改OnDraw函数让绘图逻辑不干扰 RichEditvoid CNotepadLikeView::OnDraw(CDC* pDC) { // 不再绘制任何东西RichEdit 自己负责渲染 // CNotepadLikeDoc* pDoc GetDocument(); // ASSERT_VALID(pDoc); // if (!pDoc) // return; // TODO: add draw code for native data here }为什么必须这么做MFC 向导默认CView是纯绘图容器CEdit是子窗口。但CRichEditCtrl必须作为子窗口显式创建且CView::OnDraw会覆盖其渲染。跳过PreCreateWindow禁用和OnInitialUpdate创建RichEdit 就不会显示。3.3 实现文件打开/保存CFileDialog 文本读写记事本的核心是文件 I/O。MFC 提供CFileDialog封装 Windows 打开/保存对话框但读写逻辑需手动实现在CNotepadLikeView.h中声明处理函数// NotepadLikeView.h class CNotepadLikeView : public CView { // ... 其他代码 protected: afx_msg void OnFileOpen(); afx_msg void OnFileSave(); afx_msg void OnFileSaveAs(); DECLARE_MESSAGE_MAP() };在NotepadLikeView.cpp中映射消息并实现// NotepadLikeView.cpp BEGIN_MESSAGE_MAP(CNotepadLikeView, CView) // ... 其他映射 ON_COMMAND(ID_FILE_OPEN, CNotepadLikeView::OnFileOpen) ON_COMMAND(ID_FILE_SAVE, CNotepadLikeView::OnFileSave) ON_COMMAND(ID_FILE_SAVE_AS, CNotepadLikeView::OnFileSaveAs) END_MESSAGE_MAP() void CNotepadLikeView::OnFileOpen() { CFileDialog dlg(TRUE, _T(txt), NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, _T(Text Files (*.txt)|*.txt|All Files (*.*)|*.*||)); if (dlg.DoModal() IDOK) { CString strPath dlg.GetPathName(); CStdioFile file; if (file.Open(strPath, CFile::modeRead)) { CString strText; file.ReadString(strText); // 逐行读取CStdioFile 不支持一次性读全部需循环 CString strLine; m_richEdit.SetWindowText(_T()); // 清空 while (file.ReadString(strLine)) { m_richEdit.ReplaceSel(strLine _T(\r\n)); } file.Close(); } } } void CNotepadLikeView::OnFileSave() { // 如果已有文件路径直接保存否则调用 Save As CDocument* pDoc GetDocument(); if (pDoc !pDoc-GetPathName().IsEmpty()) { OnFileSaveAs(); // 复用 Save As 逻辑 } else { OnFileSaveAs(); } } void CNotepadLikeView::OnFileSaveAs() { CFileDialog dlg(FALSE, _T(txt), NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T(Text Files (*.txt)|*.txt|All Files (*.*)|*.*||)); if (dlg.DoModal() IDOK) { CString strPath dlg.GetPathName(); CStdioFile file; if (file.Open(strPath, CFile::modeCreate | CFile::modeWrite)) { CString strText; int nLen m_richEdit.GetWindowTextLength(); if (nLen 0) { strText.GetBufferSetLength(nLen 1); m_richEdit.GetWindowText(strText.GetBuffer(), nLen 1); strText.ReleaseBuffer(); file.WriteString(strText); } file.Close(); } } }参数说明OFN_HIDEREADONLY隐藏“只读”复选框记事本无此功能OFN_FILEMUSTEXIST打开时强制文件存在OFN_OVERWRITEPROMPT保存时提示覆盖CStdioFile::WriteString会自动添加\r\n无需手动处理换行符此时编译运行点击菜单“文件→打开”选择.txt文件内容将显示在编辑区修改后点“保存”即可写回磁盘。这是记事本最核心的闭环。4. 避坑指南VS2022 MFC 开发中 5 个血泪经验换来的高频翻车点MFC 在 VS2022 上不是“向下兼容”而是“有条件兼容”。很多错误看似随机实则由环境链断裂导致。以下是我在 12 个真实 MFC 项目中踩过的坑按现象→原因→解决整理拒绝玄学。4.1 现象编译报错f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\dumpcont.cpp(23) : atltracegeneral原因这是 MFC 调试输出宏ATLTRACE的内部路径表明你正在 Debug 模式下编译但项目配置中“MFC 的使用” 设置为“在共享 DLL 中使用 MFC”而你的运行环境缺少mfcd143d.dllDebug 版 MFC 动态库。VS2022 默认安装的是 Release 版 MFC 运行时Debug 版需额外安装。解决方案 A推荐在项目属性 →常规 → 使用 MFC→ 改为“在静态库中使用 MFC”。这样生成的 EXE 不依赖外部 DLLDebug/Release 均可运行。方案 B若必须用 DLL打开 Visual Studio Installer → 修改 VS2022 → 单个组件 → 搜索C MFC for latest v143 build tools (x86 x64)→ 确保勾选“Debug symbols”子项它包含mfcd143d.dll的 PDB 和 DLL。4.2 现象CRichEditCtrl不显示、或显示为灰色方块SetWindowText无效原因CRichEditCtrl依赖riched20.dllWindows 95/NT 时代的老库或riched32.dllWinXP 后但 VS2022 项目默认不链接riched20.lib且 Windows 10/11 默认不注册该控件。解决在NotepadLikeView.cpp的OnInitialUpdate函数开头显式加载 riched20.dllvoid CNotepadLikeView::OnInitialUpdate() { // 必须放在最前面 HMODULE hRich LoadLibrary(_T(riched20.dll)); if (!hRich) { AfxMessageBox(_T(无法加载 riched20.dll请检查系统是否损坏)); return; } CView::OnInitialUpdate(); // ... 后续创建 m_richEdit }同时在项目属性 →链接器 → 输入 → 附加依赖项中添加riched20.lib。4.3 现象菜单项点击无响应ON_COMMAND消息未触发原因MFC 消息映射要求命令 ID 必须在resource.h中正确定义且CMainFrame或CView类必须在BEGIN_MESSAGE_MAP中正确声明。常见错误是在资源编辑器中双击菜单项VS 自动生成了OnFileOpen()声明但忘记在.cpp中添加ON_COMMAND(ID_FILE_OPEN, CNotepadLikeView::OnFileOpen)映射或ID_FILE_OPEN在resource.h中被误删/覆盖。解决打开resource.h确认#define ID_FILE_OPEN 32771存在VS 向导默认值在NotepadLikeView.cpp的BEGIN_MESSAGE_MAP块内严格按ON_COMMAND(ID_XXX, Class::Func)格式添加不能漏掉符号编译后用“调试 → 窗口 → 类视图”展开CNotepadLikeView确认OnFileOpen函数出现在“消息”节点下。4.4 现象中文乱码文件保存为 ANSI 编码打开含中文的 txt 显示为??原因CStdioFile默认使用系统 ANSI 编码GB2312/GBK而现代文本编辑器包括 VS2022 自身默认保存 UTF-8。MFC 无内置 UTF-8 文件读写支持。解决用CFile替代CStdioFile手动处理 BOM 和编码转换void CNotepadLikeView::OnFileOpen() { CFileDialog dlg(TRUE, _T(txt), NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, _T(Text Files (*.txt)|*.txt|All Files (*.*)|*.*||)); if (dlg.DoModal() IDOK) { CFile file; if (file.Open(dlg.GetPathName(), CFile::modeRead)) { UINT nSize (UINT)file.GetLength(); BYTE* pData new BYTE[nSize 1]; file.Read(pData, nSize); pData[nSize] 0; // 检查 UTF-8 BOM (EF BB BF) CString strText; if (nSize 3 pData[0] 0xEF pData[1] 0xBB pData[2] 0xBF) { // UTF-8 with BOM → 转为 Unicode int nLen MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)(pData 3), nSize - 3, NULL, 0); WCHAR* pWide new WCHAR[nLen 1]; MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)(pData 3), nSize - 3, pWide, nLen); pWide[nLen] 0; strText pWide; delete[] pWide; } else { // ANSI → 直接转 Unicode strText (LPCSTR)pData; } m_richEdit.SetWindowText(strText); delete[] pData; file.Close(); } } }4.5 现象程序退出时崩溃调用堆栈指向CWinApp::ExitInstance原因CRichEditCtrl是 COM 控件需在CWinApp::InitInstance中调用AfxOleInit()初始化 COM 库。VS2022 向导默认不加此行。解决打开NotepadLikeApp.cpp在InitInstance()函数开头return TRUE;之前添加BOOL CNotepadLikeApp::InitInstance() { // ... 向导原有代码 // 必须添加初始化 RichEdit 所需的 COM if (!AfxOleInit()) { AfxMessageBox(_T(OLE 初始化失败)); return FALSE; } // ... 后续代码 return TRUE; }5. 进阶技巧让 MFC 记事本支持 WebView2 嵌入HTML 全覆盖在 MFC 界面标题热词中有 “html全覆盖在mfc界面webview2”这是 VS2022 MFC 最实用的现代化升级点——用 WebView2 替代老旧的 WebBrowser 控件实现 HTML/CSS/JS 渲染让记事本变身“混合应用”。它不破坏 MFC 架构只需少量 COM 初始化和 HWND 嵌入。5.1 安装 WebView2 Runtime 并引用头文件WebView2 不是 VS2022 内置组件需单独安装访问 Microsoft Edge WebView2 官网下载Evergreen BootstrapperMicrosoftEdgeWebView2Setup.exe并安装在 VS2022 项目中右键项目 →“管理 NuGet 包”→ 搜索Microsoft.Web.WebView2→ 安装最新稳定版如1.0.2420.42在NotepadLikeView.h顶部添加#include WebView2.h #pragma comment(lib, WebView2Loader.dll)5.2 在 MFC View 中创建 WebView2 控件CRichEditCtrl占据主客户区我们把 WebView2 嵌入为一个浮动窗口例如点击菜单“查看→HTML 预览”时弹出在NotepadLikeView.h中添加 WebView2 成员class CNotepadLikeView : public CView { // ... 其他代码 private: wil::com_ptrICoreWebView2Controller m_webviewController; wil::com_ptrICoreWebView2 m_webView; CWnd m_webViewWnd; // 用于承载 WebView2 的 CWnd 容器 };添加菜单项和响应函数在资源编辑器中为“查看”菜单添加新项“HTML 预览”ID 设为ID_VIEW_HTML_PREVIEW在NotepadLikeView.h中声明afx_msg void OnViewHtmlPreview();在NotepadLikeView.cpp中映射并实现void CNotepadLikeView::OnViewHtmlPreview() { // 创建 WebView2 容器窗口300x400位于主窗口右下角 CRect rect(0, 0, 300, 400); m_webViewWnd.Create(NULL, _T(WebView2 Preview), WS_POPUP | WS_VISIBLE, rect, this, 0); m_webViewWnd.MoveWindow(500, 100, 300, 400); // 初始化 WebView2 CreateCoreWebView2Controller(); } void CNotepadLikeView::CreateCoreWebView2Controller() { auto webviewController wil::TryGetAsyncOperationResult( m_webViewWnd.GetSafeHwnd(), [](HWND hwnd) - HRESULT { return CreateCoreWebView2Controller(hwnd, nullptr, nullptr); }); if (SUCCEEDED(webviewController.hr)) { m_webviewController webviewController.value; m_webviewController-get_CoreWebView2(m_webView); // 导航到本地 HTML可动态生成 CString html _T(htmlbodyh2Hello from WebView2 in MFC!/h2); html _T(pCurrent text length: ); html CString(m_richEdit.GetWindowTextLength()); html _T(/p/body/html); m_webView-NavigateToString(html.AllocSysString()); } }关键点CreateCoreWebView2Controller必须在m_webViewWnd窗口创建后调用否则 HWND 无效NavigateToString可直接传入 HTML 字符串无需本地文件适合实时预览wil::com_ptr是 Microsoft 提供的智能指针避免手动AddRef/Release。此时点击“查看→HTML 预览”会弹出一个新窗口显示当前编辑区文本长度的 HTML 页面。你可以扩展它监听CRichEditCtrl的EN_CHANGE通知实时更新 WebView2 内容实现 Markdown 预览、代码高亮等。5.3 性能与部署注意事项内存泄漏风险WebView2 控件必须在窗口销毁时显式释放。在CNotepadLikeView::OnDestroy()中添加void CNotepadLikeView::OnDestroy() { CView::OnDestroy(); m_webView.Reset(); m_webviewController.Reset(); m_webViewWnd.DestroyWindow(); }离线部署WebView2 Runtime 需目标机器安装。若需离线可打包WebView2 Fixed Version Runtime约 180MB或使用WebView2 Evergreen Standalone Installer约 15MB在安装包中静默调用。调试技巧右键 WebView2 窗口 → “检查” → 打开 DevTools和 Chrome 一样调试 HTML/JS。我坚持在每个新 MFC 项目里加 WebView2不是为了炫技而是给未来留接口——今天嵌个 HTML 预览明天就能接内部 BI 看板、AI 结果可视化、甚至 Electron 替代方案。MFC 不是古董是可控的底层画布。希望帮到你。本文还有配套的精品资源点击获取
返回列表