命令传递-学习总结

本文详细探讨了MFC中命令消息WM_COMMAND的处理顺序,包括Frame窗口和View的处理流程,以及CDocument、CFrameWnd和CView中的OnCmdMsg函数如何参与消息传递。通过理解这些关键点,可以更好地掌握MFC程序中命令消息的传递路径。

MFC把消息分成了两大类,一般消息(WM_xxx)和 命令消息(WM_COMMAND)。因为一般消息的传递路线简单而明确:一定是由派生类流向基类,没有旁流的可能。所以,重中之重就落到了命令消息的头上。

对于命令消息传递路线的学习,有几点重要信息要事先请大家注意:

1、MFC对于命令消息WM_COMMAND的特殊处理顺序;

(其实在这里我们可以把特殊二字拿掉,因为特殊之处在此涉及不到)

2、与消息循环有关的成员函数皆为虚函数,所以理清它们所属的基类及是否被子类改写就成为把握程序走向的关键;

3、因为虚函数的存在,程序执行过程中对象指针的指向(实例化)就尤为重要,这直接决定了虚函数的调用。

时刻注意这三点,对于把握命令传递的路线会有所帮助。现在我把前两点再展开一下,大家可以在看后面的详述时,与这里进行对照。

1、命令消息 WM_COMMAND的处理顺序:

(1)Frame 窗口:   View

                           Frame窗口本身

   ƒCWinApp

(2)View  View本身

Document

2、虚函数脉络 (其中,小括号内是虚函数的首次定义;中括号为子类对它的改写)

CDocument  

OnCmdMsg

CCmdTarget                          CFrameWnd  

OnCmdMsg   CWnd                  【OnCommand

WindowProc)         【OnCmdMsg

OnCommand

DefWindwProc)     CView

                                        【OnCmdMsg

 

这些准备,都是自己在学习过程中因不够注意而耗费时间的地方,希望能够引起大家的重视。准备工作完成,接下来就是在这些“指导思想”的引导下,开始命令传递的长征了。

消息的传递从全局函数 AfxWndProc()开始,每调用一次便推进一个消息。

LRESULT  AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, 

LPARAM lParam, CWnd* pWnd)

{

cout << "AfxWndProc()" << endl; //追踪路线

return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);

}

看下函数AfxWndProc()的5个参数,其中:

hWnd 是一个窗口句柄,它唯一标志了一个应用程序窗口;

nMsg 是一个消息,这个参数就代表了消息本身;

wParamlParam,它们是消息nMsg的附加参数,在进行消息传递的过程中会用到;函数体内的cout 显示了追踪的路线,return AfxCallWndProc进入下一步。

LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,

   WPARAM wParam, LPARAM lParam)

{

cout << "AfxCallWndProc" << endl; //追踪路线

LRESULT lResult = pWnd->WindowProc(nMsg, wParam, lParam);

return lResult;

}

回顾一下我们的虚函数脉络,WindowProc()是CWnd类的虚函数,CWnd的子类(CFrameWndCView)都没有改写它,所以不论pWnd指针被哪个子类的对象实例化,调用的总是CWnd::WindowProc()。尽管如此,仍然有两点原因让我们必须要明晰将pWnd实例化的对象是哪个:

(1)虽然CFrameWnd::WindowProc()和CView::WindowProc()都相当于调用CWnd::WindowProc(),但意义是不同的。这点是侯捷先生着重强调的。

(2)接下来还有多个虚函数在等着我们,不可因这一次的殊途同归而掉以轻心。

进入到CWnd::WindowProc()中,就要对消息的本质做出判别了。其分类就是我们前面提到的 一般消息(WM_xxx)和 命令消息(WM_COMMAND)两大类。如果是一般消息,那么它的处理在WindowProc()函数内部就到达终点了,只需依据消息映射表循线而上即可,这里不再赘述。如果是命令消息(WM_COMMAND),那就要由CWnd::WindowProc()调用OnCommand()。

回想我们的虚函数脉络,OnCommandCWnd类的虚函数,并且子类CFrameWnd中有改写它,而CView则没有。所以,如果this指针被不同的子类对象实例化,那么就不会再殊途同归了了。

以调用CFrameWnd::OnCommand为例,继续往下看。走到这里,或许应该留意一下函数参数的变化。OnCommandWPARAM wParam, LPARAM lParam),只有消息的两个附加参数了,因为在此消息的本质已经确定,就是命令消息。伴随着传递的深入,参数也不断完成着它们的使命。

BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)

{

Cout << "CFrameWnd::OnCommand" << endl; //追踪路线

return CWnd::OnCommand(wParam, lParam);

}

进入  CWnd::OnCommand

BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)

{

Cout << " CWnd::OnCommand" << endl; //追踪路线

return OnCmdMsg(0, 0);

}

面对OnCmdMsg,还要用到我们的虚函数脉络。OnCmdMsgCCmdTarget的虚函数,其子类(CDocumentCFrameWndCView)都改写了它。根据this指针所指,当前调用CFrameWnd::OnCmdMsg()。在这个函数里就大有文章了。之所以这么说,因为它体现出了Frame窗口处理WM_COMMAND的次序。不仅如此,看到函数参数 (UINT nID, int nCode),是否想起了数据结构AFX_MSGMAP_ENTRY中的内容呢?它里面可是记录了消息的基本信息,既然这些参数都已出现,说明我们距离最后的消息处理程序已经不远了。

WM_COMMAND的处理次序上,CFrameWnd::OnCmdMsg函数的结构十分清晰:

1CView* pView = GetActiveView();

if (pView->OnCmdMsg(nID, nCode))

return TRUE;

2if (CWnd::OnCmdMsg(nID, nCode))

return TRUE;

3CWinApp* pApp = AfxGetApp();

 if (pApp->OnCmdMsg(nID, nCode))

return TRUE;

    

最先调用pView->OnCmdMsg,即

BOOL CView::OnCmdMsg(UINT nID, int nCode)

{

if (CWnd::OnCmdMsg(nID, nCode))

return TRUE;

BOOL bHandled = FALSE;

bHandled = m_pDocument->OnCmdMsg(nID, nCode);

return bHandled;

}

这个函数,CView::OnCmdMsg又反应了View窗口处理WM_COMMAND的次序:

aif (CWnd::OnCmdMsg(nID, nCode))

return TRUE;

bBOOL bHandled = FALSE;

       bHandled = m_pDocument->OnCmdMsg(nID, nCode);

   return bHandled;

对于aCWnd::OnCmdMsg,根据虚函数脉络,即CCmdTarget::OnCmdMsg

BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode)

{

AFX_MSGMAP* pMessageMap;

AFX_MSGMAP_ENTRY lpEntry;

for (pMessageMap = GetMessageMap(); pMessageMap != NULL; 

pMessageMap = pMessageMap->pBaseMessageMap)

{

lpEntry = pMessageMap->lpEntries;

printlpEntries(lpEntry);

}

return FALSE;

}

这是走访消息映射表的操作,我们就要找到消息最后的归宿——处理程序了。如果在映射表中找到了对应的消息,就调用对应的处理程序;否则,就要回到分岔口去走另一条路,即退回到CView::OnCmdMsg执行bbHandled = m_pDocument->OnCmdMsg(nID, nCode)。如果消息到最后也没能找到对应的处理程序,那么它走过的路线应该是这样的:

(1)a -> (1) b -> (2) -> (3)

注意,这是在本示例程序调用CFrameWnd::OnCommand为开始的前提下进行的。如果换做其它的调用顺序,只要把握MFCWM_COMMAND的处理顺序和虚函数脉络,其中的条理自然就明确了。

最后,还要再重复一下,在命令传递的过程中,影响执行路线的是MFC处理WM_COMMAND的顺序this指针的指向。理解这两点,对理解本节侯捷先生仿真出的命令传递长征路线有很好的帮助。

向侯捷先生致谢,鼓励交流,只求进步。

<think>嗯,用户问的是shell命令中短横线"-"和双短横线"--"的区别,这是个很基础的shell语法问题。从用户引用的资料来看,ta应该正在学习Bash命令的基础知识,之前可能接触过cd/ls等基础命令,也看过set/read等命令的用法说明。 用户的问题虽然简单,但涉及到Unix/Linux命令的通用设计规范。我需要把这个问题拆解清楚:首先解释单短横线用于单字符选项的惯例(比如-a -l),然后说明双短横线用于完整单词选项的情况(比如--all --long),最后补充它们处理参数时的差异。特别要注意的是,有些命令对这两种形式支持程度不同,比如find命令就只用单短横线。 用户引用的资料里提到过tail -f这种参数,正好可以作为单短横线的典型例子;而set -e也出现在引用中,这个例子可以延伸说明特殊选项对短横线数量的要求(比如set命令必须用单短横线)。虽然用户没问,但结尾可以提下GNU风格选项这个背景知识,帮助理解为什么会有两种形式存在。 考虑到用户是初学者,回答时要避免引入太多晦涩概念。重点用ls命令这种最直观的例子展示差异,比如对比ls -a和ls --all。最后补充注意事项时,要强调"不是绝对规则"这一点,因为像tar这样的命令就有例外情况(tar -xzf 和 tar --extract --gzip --file混用)。 相关问题的设计要围绕知识延伸:第一个问题聚焦应用场景,第二个针对语法细节,第三个则引导查看帮助文档的实践。这样能帮助用户建立系统认知,而不是孤立记忆知识点。 </think>在Shell命令中,`-` 和 `--` 主要用于命令行选项(参数)的标识,两者的核心区别在于**选项的命名形式和处理方式**: ### 1. **单短横线 `-`(短选项)** - **功能**:用于单字符选项(通常一个字母)。 - **语法**:`-字母`(如 `-a`, `-l`)。 - **组合使用**:可合并多个单字符选项(如 `ls -al` 等价于 `ls -a -l`)。 - **参数传递**:选项后可直接跟参数值(如 `tar -xf file.tar` 中 `-f` 的参数是 `file.tar`)。 - **典型示例**: ```bash ls -a # 显示隐藏文件(-a 为单字符) grep -i "text" # 忽略大小写搜索(-i 为单字符) ``` ### 2. **双短横线 `--`(长选项)** - **功能**:用于完整单词或短语的选项(更具描述性)。 - **语法**:`--完整单词`(如 `--all`, `--help`)。 - **组合限制**:**不支持合并**(如 `--all --help` 不可简写)。 - **参数传递**:需用 `=` 或空格分隔参数(如 `--file=test.txt` 或 `--file test.txt`)。 - **终止选项解析**:单独使用 `--` 表示后续内容均为参数而非选项(如 `rm -- -f.txt` 删除名为 `-f.txt` 的文件)。 - **典型示例**: ```bash ls --all # 同 ls -a,但使用长选项 git --version # 显示版本信息 ``` ### 3. **核心区别总结** | **特性** | `-`(短选项) | `--`(长选项) | |------------------|------------------------|-----------------------------| | 选项形式 | 单字符(`-a`) | 完整单词(`--all`) | | 多选项合并 | 支持(`-al`) | 不支持 | | 参数传递 | 紧跟选项(`-f file`) | `=` 或空格(`--file=file`) | | 终止选项解析 | 不支持 | 单独 `--` 可实现 | | 可读性 | 较低(需记忆字母) | 更高(语义明确) | ### 4. **注意事项** - **兼容性差异**:部分命令(如 `find`)仅支持短选项(`-name`),而GNU工具(如 `grep`)通常同时支持长短选项。 - **特殊行为**:`--` 可用于处理以 `-` 开头的文件名(如 `rm -- -f.txt`),避免被解析为选项。 - **优先级规则**:短选项更高效,长选项更易读,脚本中推荐长选项以提高可维护性[^1][^2]。 --- ### 相关问题 1. **如何查看命令支持的短选项和长选项?** > 使用 `man 命令名` 或 `命令--help` 查看手册(如 `man ls`)[^1]。 2. **为什么某些命令的选项既有短格式(`-h`)又有长格式(`--help`)?** > 为兼容性和可读性设计,短选项输入快捷,长选项更易理解。 3. **在脚本中处理命令行参数时,如何正确解析 `-` 和 `--`?** > 可使用 `getopts`(短选项)或 `getopt`(长短选项)工具解析[^3][^4]。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值