VC++6.0 MFC入门知识地图:消息机制、消息映射与GDI绘图

发布时间:2026/10/31 4:07:38
VC++6.0 MFC入门知识地图:消息机制、消息映射与GDI绘图 简介这份资源面向具备一定C/C基础、准备踏入Windows桌面开发的学习者聚焦VC6.0环境下MFC框架的入门路径帮助读者理清从语言基础到消息驱动编程的学习顺序避免一上手就陷入窗口代码的误区。压缩包内为1个doc文档体积约521KB以文字讲义形式系统整理了入门要点便于随时查阅与做笔记。文档围绕C特性运用、Windows消息机制、窗口句柄与GUI句柄、MFC消息映射等核心概念展开并给出消息组成、窗口过程、消息队列与消息循环的伪代码示例同时附有参考书选择原则与阅读他人代码的建议可作为入门阶段的知识框架与自查清单。目前已有730人学习适合希望低成本建立MFC整体认知的初学者。1. 从消息驱动到 MFC 封装一张 VC6.0 入门知识地图很多人学 MFC 的第一反应是打开向导、拖控件、点编译结果窗口是弹出来了可一旦要改行为就完全不知道代码该往哪写。问题不在手笨而在于跳过了中间那层地图Windows 是消息驱动的MFC 只是把消息机制、句柄和 API 用 C 类包了一层。这张地图大致分四块C/C 语言底子、Windows 消息机制与句柄、MFC 消息映射与常用类、GDI 绘图对象。前两块决定你能不能看懂程序在干什么后两块决定你能不能写出东西。这份笔记按「基础 → 机制 → 封装 → 绘图 → 查漏」的顺序梳理适合已经能写 C 字符界面程序、正准备进 Windows 开发的人也适合学过一遍 MFC 但知识点散落一地、需要重新串起来复习的人。2. 语言与机制地基C 底子和 Windows 消息模型2.1 为什么先写字符界面程序再碰窗口原文把「好的 C/C 基础」放在第一条并且明确建议最开始接触 VC 时不要急于做 Windows 程序先写一些字符界面程序。这个建议听起来像拖延其实是在降低后面的调试成本。MFC 开发里大量报错不是框架问题而是指针、对象生命周期、构造析构顺序没搞清。字符界面阶段练的是三件事对语法的熟悉度、编程思维、以及对常见错误的敏感度。更关键的是 C 特性在 MFC 里的直接映射。原文说得很清楚利用封装性更容易理解和操作各种窗口对象利用派生性可以减少开发自定义窗口的时间、造出可重用代码利用虚拟性可以在必要时更好地控制窗口活动。换句话说MFC 的类层次就是继承加虚函数CWnd 派生、CView 派生、自定义窗口类全是这套东西。C 不熟看 MFC 头文件就像看天书。复习时可以用一张对照表把语言特性和 MFC 用法绑在一起记C 特性在 MFC 中的体现复习时要能回答的问题封装句柄被包进 C 对象如 CWnd 包 HWND成员函数背后调的是哪个 API继承CView、CFrameWnd、CDialog 的类层次自己的类该从哪个基类派生虚函数窗口过程、OnDraw 等可重写行为哪些函数是框架回调、哪些要自己调构造/析构GUI 对象创建与销毁、DeleteObject对象何时创建、何时必须销毁提示判断自己基础够不够一个简单标准是——能不能不查书说清一个类的构造、使用、销毁三段生命周期。说不清就先回去补字符界面程序。2.2 消息的组成、投递与窗口过程Windows 是消息驱动的操作系统。原文给了一个很实用的拆解一个消息由消息名称UINT和两个参数WPARAM、LPARAM组成。当用户输入或窗口状态改变时系统把消息发送到某个窗口。比如菜单被选中后会有 WM_COMMAND 消息WPARAM 的高字HIWORD(wParam)是命令 ID对菜单来说就是菜单 ID。用户也可以自定义消息名称用自定义消息来发送通知和传送数据。消息必须由一个窗口接收窗口过程WNDPROC里对消息做分析只处理自己感兴趣的部分。没处理的消息去哪了系统为窗口编写了默认窗口过程负责处理你不处理的消息。正因为有它拖动窗口产生的一堆消息可以完全不理交给系统自己处理。这一点是理解 MFC 消息映射的前提。窗口句柄是另一根支柱。系统通过窗口句柄在整个系统中唯一标识一个窗口发送消息时必须指定句柄表明由哪个窗口接收。原文举的例子很到位两个窗口共用一份窗口过程代码在窗口一上按下鼠标消息会通过窗口一的句柄送到窗口一而不是窗口二。句柄是「哪个窗口」窗口过程是「怎么处理」两者分开记后面就不会混。消息循环的伪代码值得抄一遍它解释了 Windows 为什么能同时跑多个任务// 消息循环没有消息时 getMessage 不返回控制权交回系统 while (1) { id getMessage(...); // 从本程序的消息队列取一条消息 if (id quit) break; // 收到退出消息则结束循环 translateMessage(...); // 翻译消息如键盘字符消息 dispatchMessage(...); // 按窗口句柄分派到对应窗口过程 }这段逻辑的要点在最后一句注释没有消息通知时 getMessage 不返回也就不占 CPU。原文还对比了 16 位和 32 位系统16 位系统只有一个消息队列系统必须等当前任务处理完消息才能发下一条一个程序死循环系统就拿不到控制权这叫协同式多任务32 位系统每个运行的程序都有自己的消息队列系统可以在多个队列间转换不必等当前程序处理完这叫抢先式多任务。这个对比在选择题和面试里都常见记「队列数量」和「是否等待」两个差异点即可。2.3 消息映射宏展开后到底变成了什么MFC 对消息的处理用消息映射基础是宏定义通过宏把消息分派到不同成员函数。原文给了一段典型的映射表BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) ON_WM_CREATE() //}}AFX_MSG_MAP ON_COMMAND(ID_FONT_DROPDOWN, DoNothing) END_MESSAGE_MAP()编译后它被替换成类似下面的形式原文说明这只是讲解实际情况复杂得多// 宏展开后的等价结构窗口过程里用 switch 分派消息 CMainFrame::newWndProc(...) { switch (...) { case WM_CREATE: OnCreate(...); // ON_WM_CREATE 映射到这里 break; case WM_COMMAND: if (HIWORD(wP) ID_FONT_DROPDOWN) { DoNothing(...); // ON_COMMAND 映射到这里 } break; } }理解这段展开就理解了 MFC 开发的基本思路newWndProc 就是窗口过程只要是该类的实例生成的窗口都使用这个窗口过程。所以「消息机制 消息映射」两件事打通剩下的就是查类、查函数。复习时不要背宏的写法要能画出「消息 → 映射宏 → 成员函数 → 窗口过程」这条链。3. MFC 常用类与开发套路从 CRect 到文档视图选型3.1 常用类速查CRect、CPoint、CString、CStringArray原文列出的常用类不多但都是高频。CRect 表示矩形四个成员变量 top、left、bottom、right分别表示左上角和右下角坐标。构造方式有五种四个坐标、由 RECT 结构构造两个重载、由左上角坐标加尺寸构造、由两个点构造。成员函数要记住 Width、Height、Size、TopLeft、BottomRight、CenterPoint。还有一个容易忽略的点矩形可以和点相加做位移和另一个矩形相加得到「并」操作后的矩形。CPoint 表示点坐标两个成员 x、y可以和另一个点相加。CString 表示可变长度字符串不用指明内存大小按需自行分配。常用成员函数包括 GetLength、GetAt、operator相当于 strcat、Format相当于 sprintf、Find、Compare、CompareNoCase、MakeUpper、MakeLower。CStringArray 是可变长度字符串数组元素为 CString 实例常用 Add、RemoveAt、RemoveAll、GetAt、SetAt、InsertAt。这几个类建议按「构造 → 读写 → 增删 → 转换」四类操作整理成自己的卡片。原文还列了常用宏 RGB、TRACE、ASSERT、VERIFY以及常用函数 AfxGetApp、AfxGetInstanceHandle、AfxGetResourceHandle、AfxMessageBox。AfxMessageBox 用于弹出消息框参数为文本、类型默认 MB_OK、帮助 ID默认 0。这些名字在调试和查错时出现频率极高混个脸熟比死记签名有用。3.2 开发通用方法文档视图结构怎么选原文 1.4 节给了一组很实在的选型经验本质是「按交互复杂度和文件读写复杂度选结构」。整理成决策表更好复习场景特征推荐结构理由需要读写文件输入输出简单单文档视结构结构简单够用注重交互的简单程序以对话框为基础的窗口交互集中文件读写简单时可用 CFile注重交互且文件读写复杂以 CFormView 为基础视的单文档视结构表单式布局加文档管理多文档要求不强烈单文档多视结构用分隔条避免多文档的复杂度需要在多个文档间传递数据多文档视结构这是多文档真正的用武之地其余几条是「避坑型」建议利用对话框得到用户输入水平提高后再用就地输入学会利用子窗口在自定义子窗口里包含多个控件达到封装功能尽量避免多文档多视结构不要使用多重继承尽量减少一个类中封装过多功能。这几条背后是同一个原则——控制复杂度。复习时可以把它们当成「默认选择 例外条件」来记而不是九条孤立规则。3.3 参考书怎么挑初学者与进阶者两套标准原文对选书给了明确原则这部分常被跳过但对自学的人价值很高。初学者应选内容全面、安排合理、能循序渐进的书代码要有详细讲解尽量买翻译的书因为一般比较易懂、语言轻松买书前一定要慎重买到不好用的书可能打击学习积极性。已经掌握 VC 的人则应加深对系统原理和技术要点的认识选原理讲得透彻的书最好对技术应用有一定阐述尽量选示范代码精简的书。配套的学习习惯原文也列了训练自己写代码时不翻参考书而是用 Help Online记住一些常用消息名称和参数意义学会看别人的代码多看书少买书闲下来就看参考书。这些不是鸡汤是降低「查资料成本」的具体手段。Help Online 用熟了等于随身带一份和编译器版本匹配的文档比任何二手笔记都准。4. GDI 绘图对象DC、字体、画笔与刷子的使用规则4.1 GUI 对象的通用使用规则与安全写法Windows 中有各种 GUI 对象注意不要和 C 对象混淆。原文列出的对象包括字体对象 CFont风格、大小、斜体、粗体、字体名称、下划线等颜色和背景色不属于字体属性、刷子 CBrush填充区域的颜色或模板可用 8×8 位图创建自定义模板刷子、画笔 CPen颜色、宽度、线风格如虚线实线点划线、位图 CBitmap可保存在资源中以及多边形用于限制作图区域或改变窗口外型。使用规则是这一节的重点也是最容易翻车的地方先创建合法对象把对象选入 DC同时保存 DC 中原来的对象选入非法对象会引起异常使用完恢复原来的对象如果保存的是临时对象而临时对象被销毁后可能引起异常每一个对象在重新创建前必须先销毁。原文给了一段安全写法void CMyView::OnDraw(CDC* pDC) { CPen pen1, pen2; pen1.CreatePen(PS_SOLID, 2, RGB(128,128,128)); // 创建第一个画笔 pen2.CreatePen(PS_SOLID, 2, RGB(128,128,0)); // 创建第二个画笔 CPen* pOldPen (CPen*)pDC-SelectObject(pen1); // 选入并保存原对象 // drawWithPen1... pDC-SelectObject(pen2); // 换第二个画笔 // drawWithPen2... pen1.DeleteObject(); // 重新创建前先销毁 pen1.CreatePen(PS_SOLID, 2, RGB(0,0,0)); // 再次创建 pDC-SelectObject(pen1); // 再次选入 // drawWithPen1... pDC-SelectObject(pOldPen); // 恢复原来的对象 }这段代码的注释就是检查清单创建、选入、保存旧对象、销毁、重建、恢复六步一个不能少。另外系统提供库存 GUI 对象可用 CDC::SelectStockObject 选入包括 BLACK_BRUSH、DKGRAY_BRUSH、GRAY_BRUSH、HOLLOW_BRUSH、LTGRAY_BRUSH、NULL_BRUSH、WHITE_BRUSH、BLACK_PEN、NULL_PEN、WHITE_PEN 以及若干字体和 DEFAULT_PALETTE。这些对象留在 DC 中是安全的所以可以用选入库存对象的方式恢复 DC。DC设备环境是抽象的作图环境可能对应屏幕也可能对应打印机。它设备无关对不同设备输出只需换 DC作图方式不变。原文用照相机和复印机打比方很贴切。OnDraw 的优点也在这里窗口重绘时自动调用传入的 CDC* 对应 DC打印时传入打印机 DC打印预览时传入 CPreviewDC一份代码完成窗口、打印预览、打印机三重功能。4.2 文字输出TextOut 与 DrawText 的参数组合输出文字一般用两个函数CDC::TextOut(x, y, str) 只能输出单行CDC::DrawText(str, lpRect, nFormat) 可以在矩形中输出单行或多行并规定对齐方式和风格。nFormat 是多个标记的位或组合常用标记整理如下标记作用使用注意DT_SINGLELINE单行输出回车换行不再断行DT_CENTER / DT_LEFT / DT_RIGHT水平对齐居中、左、右DT_VCENTER垂直居中必须与 DT_SINGLELINE 组合DT_BOTTOM / DT_TOP底部/顶部对齐底部对齐须与 DT_SINGLELINE 组合DT_WORDBREAK按单词折行超出矩形自动断行DT_CALCRECT只计算所需矩形尺寸不绘制文字返回高度DT_END_ELLIPSIS / DT_PATH_ELLIPSIS省略号替换路径省略保留最后反斜杠后文本DT_NOPREFIX禁止 前缀处理避免 被当作下划线指令DT_EXPANDTABS / DT_TABSTOP展开制表符/设宽度默认每 TAB 八个字符DT_NOCLIP不裁剪绘制速度略快颜色和背景用 CDC::SetTextColor 和 CDC::SetBkColor 设置需要透明背景用 CDC::SetBkMode参数 OPAQUE默认先用背景色填充或 TRANSPARENT不改变背景。字体分库存字体CDC::CreateStockObject和自定义字体自定义时填充 LOGFONT 结构后用 CFont::CreateFontIndirect或直接用 CFont::CreateFont 的十四个参数。原文详细列了 nHeight逻辑单位零为缺省高度、nWidth零则按可用横纵比匹配、nEscapement出口矢量与 X 轴角度、nOrientation基线与 X 轴角度、nWeightFW_NORMAL 为 400、FW_BOLD 为 700 等、bItalic、bUnderline、cStrikeOut、nCharSet、nOutPrecision、nClipPrecision、nQuality、nPitchAndFamily、lpszFacename。文本坐标计算用 CDC::GetTextExtent 得到字符串宽高手工输出多行时算行距更精确的计算用 CDC::GetTextMetrics 填充 TEXTMETRIC 结构。4.3 画笔与刷子风格取值和宽度限制画点用 CDC::SetPixel(x, y, crColor)返回原颜色取色用 CDC::GetPixel。原文提醒应少用画点函数因为执行效率比较低。画笔通过 CPen::CreatePen(nPenStyle, nWidth, crColor) 创建nPenStyle 可取 PS_SOLID实线、PS_DASH虚线、PS_DOT点线、PS_DASHDOT点划线、PS_DASHDOTDOT双点划线、PS_NULL空线不产生输出、PS_ENDCAP_ROUND结束处圆形、PS_ENDCAP_SQUARE结束处方形。这里有一个高频易错点PS_DASH、PS_DOT、PS_DASHDOT、PS_DASHDOTDOT 都要求宽度为 1 或更小设备单位宽度大于 1 时这些风格不生效。刷子用于画封闭曲线时填充内部固定色刷子的属性是颜色、是否采用网格和网格类型水平、垂直、交叉等。画笔和刷子创建后必须选入 DC 才会在绘图中产生效果这条和 4.1 的规则是同一条只是对象不同。5. 易错点清单消息、映射与 GDI 对象最容易翻车的地方5.1 易错点一把句柄和窗口过程混为一谈为什么漏句柄和窗口过程经常在同一段代码里出现初学者容易记成「一个窗口一个处理函数」忽略了多个窗口可以共用一份窗口过程代码。怎么补记住分工——句柄回答「消息发给哪个窗口」窗口过程回答「收到后怎么处理」。复习时用原文那个双窗口例子自测两个窗口共用窗口过程在窗口一按下鼠标消息通过窗口一的句柄送到窗口一。能复述这个例子概念就稳了。5.2 易错点二以为没处理的消息会丢失为什么漏写业务代码时习惯「不处理就不管」于是以为消息没人接就消失了。怎么补系统有默认窗口过程负责处理你不处理的消息拖动窗口产生的大量消息就是交给它。理解这一点才不会在消息映射表里试图穷举所有消息也才明白为什么只写自己关心的 ON_ 宏就够了。5.3 易错点三GDI 对象用完不恢复、重建前不销毁为什么漏代码能跑通、画面看起来正常问题往往在多次重绘或打印预览时才暴露属于典型的「当时不报错、后面翻车」。怎么补把 4.1 那段安全写法当模板每次写绘图代码强制走一遍「创建 → 选入 → 保存旧对象 → 销毁 → 重建 → 恢复」。特别注意保存的是临时对象时临时对象销毁后再恢复会出异常。5.4 易错点四虚线画笔设了大于 1 的宽度为什么漏参数表里 nWidth 和 nPenStyle 并排很容易随手写个宽度 2 配 PS_DASH然后疑惑为什么画出来是实线。怎么补记死「虚线、点线、点划线、双点划线宽度必须为 1 或更小」需要粗虚线只能自己用画点或分段绘制实现。这类参数限制在原文里是明写的复习时把画笔风格和宽度限制做成一行对照。5.5 易错点五文档视图结构一上来就选最复杂的为什么漏向导里多文档、多视选项看起来「功能全」初学者容易觉得选全一点不会错。怎么补回到 3.2 的决策表默认从单文档或对话框起步只有确实需要在多个文档间传递数据时才上多文档多文档多视结构尽量避免。结构选错后面每一行代码都在为这个决定还债。6. 复习落地一份可执行的 MFC 查漏补缺清单复习这件事最怕的是把笔记从头读一遍读完还是不会写。我的做法是把这份材料拆成「能自测的动作」按天推进每天只验证一个模块验证不过就回到对应章节补。下面这张清单可以直接抄走模块自测动作通过标准C 地基不查书说清一个类的构造、使用、销毁能说出对象何时创建、何时必须销毁消息机制手写消息循环伪代码并解释 getMessage 为何不占 CPU能讲清 16 位与 32 位消息队列差异消息映射画出「消息 → 映射宏 → 成员函数 → 窗口过程」链能解释 BEGIN_MESSAGE_MAP 展开后长什么样常用类默写 CRect 五种构造和 CString 六个常用函数能说出矩形相加的两种含义结构选型给五个场景各选一种文档视图结构并说明理由与 3.2 决策表一致GDI 绘图默写安全使用画笔的六步不漏「保存旧对象」和「恢复」文字输出说出 DT_VCENTER、DT_BOTTOM 的组合限制知道二者须配 DT_SINGLELINE具体执行上我建议用「三遍法」。第一遍只做地图把第 2 章到第 4 章的一级标题默写出来写不出的章节就是薄弱区。第二遍做细节每天挑一个模块合上笔记复述该模块的表格和代码注释复述卡壳的地方做标记第二天先补标记。第三遍做串联找一个最小的模拟项目比如一个能画线、能输出文字、能响应菜单命令的单文档程序从向导生成开始每一步都问自己「这行代码对应地图上哪一块」对不上就说明还有盲区。还有一个习惯值得养成把 Help Online 当第一查询入口。原文把「训练自己写代码时不使用参考书而是使用 Help Online」单列一条是有道理的——参考书会过时在线帮助和你的编译环境是配套的。查一个函数时顺手看它的参数表和返回值比背十遍笔记都牢。常用消息名称和参数意义也要靠这个方式积累比如 WM_COMMAND 的 WPARAM 高字是命令 ID这种细节查一次记一次几次之后就成条件反射了。最后说个我自己的教训。早些年复习这类框架我总喜欢把笔记读得很顺觉得「看懂了就是会了」结果一上手写绘图代码就忘了恢复旧画笔窗口一重绘颜色就乱查了半天才定位到 SelectObject 之后没恢复。从那以后我每次碰 GDI 对象都强制走一遍「创建、选入、保存、销毁、重建、恢复」这六步哪怕代码只有三行。复习不是把知识再读一遍而是把每个容易翻车的动作变成肌肉记忆。希望这份清单能帮到你把散落的知识点真正串成一张能用的地图。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询