﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-坚钢不可夺其志 天地莫敢争其锋-随笔分类-Visual C++</title><link>http://www.cppblog.com/cppblog/category/2192.html</link><description>无 限 风 光 尽 在 我 心 弥 坚</description><language>zh-cn</language><lastBuildDate>Tue, 20 May 2008 00:29:33 GMT</lastBuildDate><pubDate>Tue, 20 May 2008 00:29:33 GMT</pubDate><ttl>60</ttl><item><title>《VC++动态链接库深入浅出》学习笔记之Non-MFC DLL(非 MFC 动态库)</title><link>http://www.cppblog.com/cppblog/archive/2007/02/05/18356.html</link><dc:creator>潜龙居</dc:creator><author>潜龙居</author><pubDate>Mon, 05 Feb 2007 12:20:00 GMT</pubDate><guid>http://www.cppblog.com/cppblog/archive/2007/02/05/18356.html</guid><wfw:comment>http://www.cppblog.com/cppblog/comments/18356.html</wfw:comment><comments>http://www.cppblog.com/cppblog/archive/2007/02/05/18356.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cppblog/comments/commentRss/18356.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cppblog/services/trackbacks/18356.html</trackback:ping><description><![CDATA[
		<p>静态链接库与动态链接库总体区别：<br /> <br />        静态链接库与动态链接库都是共享代码的方式，如果采用静态链接库，则无论你愿不愿意，lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL，该 DLL 不必被包含在最终 EXE 文件中，EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库，而在动态链接库中还可以再包含其他的动态或静态链接库。静态链接库与静态链接库调用规则总体比较如下。</p>
		<p>对于静态链接库(比较简单)：<br />首先，静态链接库的使用需要库的开发者提供生成库的.h头文件和.lib文件。<br /><br />生成库的.h头文件中的声明格式如下：<br />extern "C" 函数返回类型 函数名(参数表);<br />在调用程序的.cpp源代码文件中如下：<br />#include "..\lib.h"<br />#pragma comment(lib,"..\\debug\\libTest.lib") <br />//指定与静态库一起链接<br /><br />第二，因为静态链接库是将全部指令都包含入调用程序生成的EXE文件中。因此如果用的是静态链接库，那么也就不存在“导出某个函数提供给用户使用”的情况，要想用就得全要！要不就都别要！:)</p>
		<p>对于动态链接库：<br />动态链接库的使用需要库的开发者提供生成的.lib文件和.dll文件。或者只提供dll文件。<br />首先我们必须先注意到DLL内的函数分为两种： <br />(1)DLL 导出函数，可供应用程序调用； <br />(2)DLL 内部函数，只能在 DLL 程序使用，应用程序无法调用它们。<br />因此调用程序若想调用DLL中的某个函数就要以某种形式或方式指明它到底想调用哪一个函数。</p>
		<p>因此这里衍生出两个问题：<br />第一：如何调用？即调用的方式<br />第二：不同调用方式对应的库的生成过程和调用规则</p>
		<p>我逐步展开问题。<br />第一，存在两种调用方式——动态调用和静态调用<br />第二，两种调用方式的库的生成过程和调用规则<br />1〉动态调用<br /><br />生成库的.h头文件中的声明格式如下：<br />extern "C" 函数返回类型 __declspec(dllexport) 函数名(参数表);<br />在调用程序的.cpp源代码文件中按如下流程调用：<br />在main函数代码的开始处<br />定义需要的DLL模块的句柄和此DLL模块模块中需要调用的函数的函数指针。<br />定义好后紧接着就是标准的三部曲：<br />取得需要的DLL模块------&gt;返回模块句柄<br />LoadLibrary("DLL模块路径")<br />取得需要的函数地址———&gt;返回函数指针GetProcAddress(模块句柄，"函数名")<br />从内存中卸载DLL模块———&gt;FreeLibrary(模块句柄)<br />2&gt;静态调用<br />在调用程序的.cpp源代码文件中按如下流程调用：<br />#include.........<br />告诉编译器与 DLL 相对应的.lib 文件所在的路径及文件名<br />#pragma comment(lib,"路径和文件名") <br />extern "C" 函数返回类型 _declspec(dllimport) 函数名(参数表)分号<br />int main()<br />{<br />..............<br />}<br />未完，待续.......</p>
<img src ="http://www.cppblog.com/cppblog/aggbug/18356.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cppblog/" target="_blank">潜龙居</a> 2007-02-05 20:20 <a href="http://www.cppblog.com/cppblog/archive/2007/02/05/18356.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转贴：VC++中对于处理消息的学习总结</title><link>http://www.cppblog.com/cppblog/archive/2006/07/20/10248.html</link><dc:creator>潜龙居</dc:creator><author>潜龙居</author><pubDate>Thu, 20 Jul 2006 03:25:00 GMT</pubDate><guid>http://www.cppblog.com/cppblog/archive/2006/07/20/10248.html</guid><wfw:comment>http://www.cppblog.com/cppblog/comments/10248.html</wfw:comment><comments>http://www.cppblog.com/cppblog/archive/2006/07/20/10248.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cppblog/comments/commentRss/10248.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cppblog/services/trackbacks/10248.html</trackback:ping><description><![CDATA[
		<p>Message Map</p>
		<p>struct AFX_MSGMAP //消息映射表<br />{<br />AFX_MSGMAP* pBaseMessageMap; //基类消息映射表的指针；<br />AFX_MSGMAP_ENTRY* lpEntries; //消息入口表的指针；<br />};</p>
		<p>struct AFX_MSGMAP_ENTRY //消息映射入口表<br />{<br />UINT nMessage; //消息；<br />UINT nCode; //控件的通知码或WM_NOTIFY的通知码<br />UINT nID; //控件的ID，(0时为 Window 消息)<br />UINT nLastID; //最后一个控件的ID；<br />UINT nSig; //信号类型；<br />AFX_PMSG pfn; //消息函数的指针；<br />};</p>
		<p>typedef void (CCmdTarget::*AFX_PMSG)(void); //消息映射函数；</p>
		<p>一、消息映射图<br />1、DECLARE_MESSAGE_MAP()<br />展开：<br />static AFX_MSGMAP_ENTRY _messageEntries[] //消息映射入口表 <br />static AFX_MSGMAP messageMap； //消息映射表；<br />virtual AFX_MSGMAP* GetMessageMap() const；//返回消息映射 表的指针；</p>
		<p>2、BEGIN_MESSAGE_MAP(class_name,base_class_name)<br />①、定义GetMessageMap()函数，代码如下：<br />{ return &amp;class_name::messageMap；}<br />②、初始化消息映射表，代码如下：<br />AFX_MSGMAP class_name::messageMap={<br />&amp;(base_class_name::messageMap)，<br />&amp;(class_name::_messageEntries)<br />}<br />③、初始化消息入口表<br />AFX_MSGMAP_ENTRY class_name::_messageEntries[]={<br />//在这里添加消息和映射函数；</p>
		<p>3、END_MESSAGE_MAP()<br />{0,0,0,0,AfxSig_end,(AFX_PMSG)0}<br />} ； //该括号和③相对应；</p>
		<p>
				<br />二、消息类型<br />1、命令消息(WM_COMMAND)<br />所有派生自 CCmdTarget 的类都有资格接受WM_COMMAND。</p>
		<p>2、Window消息(WM_xxx)<br />所有派生自 CWnd 的类都有资格接受 WM_xxx。</p>
		<p>3、控件消息(WM_NOTIFY)<br />控件向其父窗口通知消息。</p>
		<p>三、消息处理<br />1、WM_xxx 消息处理<br />窗口类(自身)处理→基类处理→CWnd∷DefWindowProc()处理；<br />其所对应的宏一般为在消息 WM_ 前面加上 ON_。</p>
		<p>2、命令消息处理<br />命令消息来自命令用户接口对象(菜单、加速键或工具栏按钮)发出的WM_COMMAND消息；<br />㈠、WM_COMMAND消息<br />其所包含的类型和对应的宏如下：<br />①、ON_COMMAND(ID,pfn)<br />标准的命令消息；</p>
		<p>②、ON_COMMAND_EX(ID,pfn)<br />多个对象对同一个命令 ID 的处理；<br />其函数的原型如下：<br />afx_msg BOOL pfn(UINT nID)<br />说明：<br />当返回 TRUE 时表示已经处理，不用在消息处理链中继续处理该命令；为 FALSE 时表示继续在消息处理链中处理该命令。<br />注意：<br />其一：在多对象处理中一定要使用该宏；<br />其二：pfn(UINT nID)(消息处理函数)返回值将其类型void改成BOOL，而且必须为FALSE；<br />其三：多个对象的处理是由高层向低层的过程：即视图类→主框架窗口类→应用程序类；</p>
		<p>③、ON_COMMAND_RANGE(nID,nLastID,pfn)<br />多个命令 ID 提供相同的处理；<br />注意：<br />其一：确保nID、nLastID的值在 Resource.h 中是连续的。<br />其二：一般在函数 pfn(UINT nID) 中加入参数，用来确定那一个按钮点击。</p>
		<p>㈡、CN_UPDATE_COMMAND_UI消息<br />当菜单项、工具栏按钮等[命令用户接口对象]要更新其状态时所对应的消息，它所包含的类型和对应的宏如下：<br />①、ON_UPDATE_COMMAND_UI(ID,pfn)<br />其中函数的原型如下：<br />afx_msg void pfn(CCmdUI* pCmdUI)</p>
		<p>②、ON_UPDATE_COMMAND_UI_RANGE(nID,nLastID,pfn)<br />该函数可以处理一组[命令用户接口对象]的外观；<br />其中函数的原型如下：<br />afx_msg void pfn(CCmdUI* pCmdUI)<br />重要：<br />CCmdUI 中的 m_nID 成员表示不同的 ID，因此可以利用它来进行区别处理。</p>
		<p>3、控件的通知消息<br />从控件和子窗口发送到父窗口的WM_COMMAND通知消息(即在发送命令消息中加入控件的通知码)。<br />注意：在 Window9x 新控件中不再传送WM_COMMAND通知消息，而是发送 WM_NOTIFY 消息，但为了兼容，旧有的控件还是传送WM_COMMAND消息。<br />例如：<br />CEdit控件向父窗口发送 EN_CHANGE 通知代码的WM_COMMAND消息。<br />注意：框架像传送其它 WM_ 消息一样传送通知消息，但有一个例外，即由 [按钮] 控件发送的 BN_CLICKED 通知消息，被作为命令消息特别处理。<br />㈠、WM_COMMAND 其所对应的宏如下：<br />①、ON_CONTROL(通知码, nID,fn)<br />②、ON_CONTROL_RANGE(通知码, nFirstID,nEndID,fn)<br />注意：<br />这两个宏的应用和 ON_COMMAND、ON_COMMAND_RANGE相同，所不同的是在宏前面加入[通知码]。<br />注意：可以根据不同的控件的[通知码]派生出特定的宏，其所派生的宏一般为在 [通知码] 前面加上 ON_。<br />㈡、WM_NOTIFY 其所对应的宏如下：<br />①、ON_NOTIFY(通知码, nID,fn)<br />其中函数的原型如下：<br />afx_msg void fn(NMHDR* pNotifyStruct,LRESULT* result)<br />其中结构：<br />typedef struct tagNMHDR { <br />HWND hwndFrom; //发送通知消息的控件的句柄；<br />UINT idFrom; //发送通知消息的控件的 ID；<br />UINT code; //通知码；<br />} NMHDR; </p>
		<p>②、ON_NOTIFY_EX(通知码, nID,fn)<br />表示一个消息在多个对象的成员函数中进行处理。<br />其中函数的原型如下：<br />afx_msg BOOL fn(UINT nID,NMHDR* pNotifyStruct,LRESULT* result)<br />说明：<br />它必须返回 BOOL 类型的数值，其意义和 ON_COMMAND_EX 相同。</p>
		<p>③、ON_NOTIFY_RANGE(通知码, nFirstID,nEnd,fn)<br />表示多个控件的通知消息在同一个函数中进行处理。<br />其中函数的原型如下：<br />afx_msg void fn(UINT nID,NMHDR* pNotifyStruct,LRESULT* result)<br />说明：<br />其意义和ON_COMMAND_RANGE相同。</p>
		<p>4、反射消息处理<br />父窗口在处理控件窗口的通知消息WM_CTLCOLOR、WM_COMMAND、WM_NOTIFY时，会把该消息转化为反射消息，并转交给控件子窗口处理，只有在控件子窗体不处理该消息时，父窗口才有机会处理。<br />注意：在类的属性对话框中的消息页面可查反射消息(前面有"="标志)<br />①、WM_CTLCOLOR_REFLECT反射消息<br />其所对应的宏如下：<br />ON_WM_CTLCOLOR_REFLECT()<br />反射消息函数的原型：<br />HBRUSH class_name∷CtlColor(CDC* pDC,UINT nCtlColor)<br />{<br />return NULL;<br />}<br />该函数用来重置控件的顔色；注意：必须 return CBrush才有效。</p>
		<p>
				<br />5、自定义的窗口消息<br />自定义窗口消息的消息标志都大于WM_USER(至少是WM_USER+100，因为许多控件都使用这一范围的WM_USER消息)<br />使用自定义的消息分为二步：<br />①、在 Resource.h 中定义消息标记<br />#define WM_MYMSG (WM_USER+1000)</p>
		<p>②、在消息映射表中加入消息映射宏<br />BEGIN_MESSAGE_MAP()</p>
		<p>ON_MESSAGE(WM_MYMSG,fn)<br />END_MESSAGE_MAP()<br />说明：<br />其对应的宏为 ON_MESSAGE()，其成员函数的原型为：<br />afx_msg LRESULT fn(WPARAM,LPARAM)</p>
		<p>6、登记消息<br />①、在系统中注册并获取一个登记消息的消息标记<br />UINT RegisterWindowMessage(LPCTSTR)<br />说明：<br />通过 API 函数来注册消息标记，其中 LPCTSTR 为用户的任意字符串。例如：<br />UINT WM_MYMSG=RegisterWindowMessage("MYMSG");<br />其中 WM_MYMSG 是自定义无符号整型的消息标记。</p>
		<p>②、在消息映射表中加入消息映射宏<br />BEGIN_MESSAGE_MAP()</p>
		<p>ON_REGISTERED_MESSAGE(WM_MYMSG,fn)<br />END_MESSAGE_MAP()<br />说明：<br />其对应的宏为 ON_REGISTERED_MESSAGE()，其成员函数的原型为：<br />afx_msg LRESULT fn(WPARAM,LPARAM)<br />注意：登记消息可以实现跨进程的数据通讯。</p>
		<p>7、线程消息<br />只有继承自CWinThread类才能允许处理线程消息。<br />①、定义线程的消息标记<br />有两种方法：<br />(1)、使用自定义的消息标记，即：WM_USER；<br />(2)、使用登记的消息标记，即：RegisterWindowMessage；</p>
		<p>②、在CWinThread继承类的消息映射表中添加宏<br />ON_THREAD_MESSAGE(消息标记,fn) //自定义的消息；<br />ON_REGISTERED_THREAD_MESSAGE(消息标记,fn) //登记的 //消息<br />③、其函数的原型如下：<br />afx_msg void fn(WPARAM wPARAM,LPARAM lParam)</p>
		<p>④、引发线程消息<br />线程消息的引发必须调用 CWinThread 类的PostThreadMessage将消息投递到线程消息队列中。<br />注意：可以通过 AfxGetApp() 函数获取一个全局的应用对象。<br />PostThreadMessage(UINT,WPARAM,LPARAM)</p>
		<p>8、WM_COPYDATA<br />操作系统维护一块内存来管理 WM_COPYDATA 消息，该消息主要用于跨进程传递数据，传递的数据量达到 232。<br />①、定义一个 COPYDATASTRUCT 数据结构<br />typedef struct tagCOPYDATASTRUCT { <br />DWORD dwData; //自定义的特殊数据；<br />DWORD cbData; //以字节为单位的 lpData 的大小；<br />PVOID lpData; //传送的数据内存块的指针；<br />} COPYDATASTRUCT; </p>
		<p>②、其所对应的宏<br />ON_WM_COPYDATA()</p>
		<p>③、其所对应的函数的原型<br />afx_msg BOOL OnCopyData(CWnd*,COPYDATASTRUCT*)<br />说明：<br />CWnd*：发送该消息的窗口的指针；</p>
		<p>
				<br />9、投递和发送消息<br />通过向一个窗体投递或发送消息，可以间接地驱动窗体的消息过程。<br />投递(PostMessage)：将消息放到线程的消息队列中，然后不等线程处理该消息就直接返回到调用方。<br />发送(SendMessage)：当一个线程向目标线程发送消息时，该线程要一直等待，直到目标线程处理了该消息为止。<br />①、投递消息<br />BOOL CWnd∷PostMessage(UINT,WPARAM=0,LPARAM=0)<br />说明：<br />CWnd：目标窗口；<br />该函数将一条消息放入到应用程序的消息队列，然后不等窗口处理就直接返回。</p>
		<p>②、发送消息<br />LRESULT CWnd∷SendMessage(UINT,WPARAM=0,LPARAM=0)<br />说明：<br />CWnd：目标窗口；<br />该函数将一条消息放入到应用程序的消息队列，等待窗口处理后才返回。<br />为了避免线程陷入永久等待状态，可以用SendMessageTimeout代替SendMessage：<br />LRESULT SendMessageTimeout(HWND,UINT,WPARAM,LPARAM,UINT,UINT,PDWORD_PTR)<br />说明：<br />HWND：窗口句柄；<br />UINT：消息发送的选项，为SMTO_BLOCK时，可以防止线程无 限等待，即根据一定的超时值返回。<br />UINT：超时，以毫秒为单位；<br />PDWORD_PTR：返回值；<br />注意：CWnd没有对该函数的包装。</p>
		<p>③、投递和发送消息<br />BOOL CWnd∷SendNotifyMessage(UINT,WPARAM,LPARAM)<br />说明：<br />CWnd：目标窗口；<br />该消息具有SendMessage和PostMessage两种功能：<br />当目标窗口和发送窗口为同一个线程时，则相当于SendMessage的功能；否则当不为同一个线程时，则为PostMessage的功能。</p>
		<p>6-1、投递和发送 WM_XXX 消息<br />在发送标准的 WINDOW 消息时，只要将该消息的 ID、wParam、lParam参数放在 SendMessage()和PostMessage()函数的相应位置即可。</p>
		<p>6-2、投递和发送命令消息和控件的通知消息<br />在投递和发送命令消息时，消息的 ID为 WM_COMMADN，而对于不同的菜单项、加速键、控件则wParam、lParam的取值不同。<br />wParam分成低、高两部分，低部分为菜单项、加速键、控件的ID。高部分则：<br />菜单项：0；加速键：1；控件：通知码<br />lParam：当控件时是控件的句柄，否则为 NULL。</p>
		<p>对于wParam参数可以采用自定义宏：<br />WPARAM MAKEWPARAM(WORD wLow,WORD wHigh)</p>
		<p>6-3、投递和发送自定义的窗口消息<br />在投递和发送自定义的窗口消息时，参数 wParam、lParam 没有特别的涵义，只和普通函数的形参一样进行数据的传递。<br />注意：<br />PostMessage 和 SendMessage 是不同的，前者投递后就返回，而后者必须等到消息处理后再返回；所以在参数是 [局部] 或 [临时]时，使用PostMessage函数会引发错误(除非参数使用 指针，则可避免错误)，而必须使用SendMessage函数。<br />6-4、投递和发送注册的窗口消息<br />和 6-3 基本一样，但它要特别注意的问题是：在跨进程的处理消息时，如果将消息PostMessage、SendMessage到某个进程 A，则必须在进程 B 中获取进程 A 的窗口类名，并通过窗口类名获取窗口的指针，最后再根据指针调用 PostMessage、SendMessage 函数。<br />注意：在获取窗口的指针时，可以根据窗口类名或窗口的标题。</p>
		<p>
				<br />6-5、投递和发送WM_COPYDATA消息<br />SendMessage(消息标记,WPARAM,LPARAM)<br />其中：<br />消息标记：WM_COPYDATA；<br />WPARAM：发送该消息的窗口句柄；<br />LPARAM：COPYDATASTRUCT结构的指针，先通过(LPVOID)进行转换，再通过(LPARAM)进行转换，如下形式：<br />(LPARAM)(LPVOID)&amp;cds</p>
<img src ="http://www.cppblog.com/cppblog/aggbug/10248.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cppblog/" target="_blank">潜龙居</a> 2006-07-20 11:25 <a href="http://www.cppblog.com/cppblog/archive/2006/07/20/10248.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转贴：VC常用数据类型使用转换详解</title><link>http://www.cppblog.com/cppblog/archive/2006/07/20/10247.html</link><dc:creator>潜龙居</dc:creator><author>潜龙居</author><pubDate>Thu, 20 Jul 2006 03:24:00 GMT</pubDate><guid>http://www.cppblog.com/cppblog/archive/2006/07/20/10247.html</guid><wfw:comment>http://www.cppblog.com/cppblog/comments/10247.html</wfw:comment><comments>http://www.cppblog.com/cppblog/archive/2006/07/20/10247.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cppblog/comments/commentRss/10247.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cppblog/services/trackbacks/10247.html</trackback:ping><description><![CDATA[
		<p>我们先定义一些常见类型变量借以说明 </p>
		<p>int i = 100; <br />long l = 2001; <br />float f=300.2; <br />double d=12345.119; <br />char username[]="程佩君"; <br />char temp[200]; <br />char *buf; <br />CString str; <br />_variant_t v1; <br />_bstr_t v2; </p>
		<p>一、其它数据类型转换为字符串 </p>
		<p>
				<br />短整型(int) <br />itoa(i,temp,10);///将i转换为字符串放入temp中,最后一个数字表示十进制 <br />itoa(i,temp,2); ///按二进制方式转换 <br />长整型(long) <br />ltoa(l,temp,10); <br />浮点数(float,double) <br />用fcvt可以完成转换,这是MSDN中的例子: <br />int decimal, sign; <br />char *buffer; <br />double source = 3.1415926535; <br />buffer = _fcvt( source, 7, &amp;decimal, &amp;sign ); <br />运行结果:source: 3.1415926535 buffer: '31415927' decimal: 1 sign: 0 <br />decimal表示小数点的位置,sign表示符号:0为正数，1为负数 <br />CString变量 <br />str = "2008北京奥运"; <br />buf = (LPSTR)(LPCTSTR)str; <br />BSTR变量 <br />BSTR bstrValue = ::SysAllocString(L"程序员"); <br />char * buf = _com_util::ConvertBSTRToString(bstrValue); <br />SysFreeString(bstrValue); <br />AfxMessageBox(buf); <br />delete(buf); <br />CComBSTR变量 <br />CComBSTR bstrVar("test"); <br />char *buf = _com_util::ConvertBSTRToString(bstrVar.m_str); <br />AfxMessageBox(buf); <br />delete(buf); </p>
		<p>_bstr_t变量 <br />_bstr_t类型是对BSTR的封装，因为已经重载了=操作符，所以很容易使用 <br />_bstr_t bstrVar("test"); <br />const char *buf = bstrVar;///不要修改buf中的内容 <br />AfxMessageBox(buf); </p>
		<p>
				<br />通用方法(针对非COM数据类型) <br />用sprintf完成转换 <br />char  buffer[200]; <br />char  c = '1'; <br />int   i = 35; <br />long  j = 1000; <br />float f = 1.7320534f; <br />sprintf( buffer, "%c",c); <br />sprintf( buffer, "%d",i); <br />sprintf( buffer, "%d",j); <br />sprintf( buffer, "%f",f); </p>
		<p>二、字符串转换为其它数据类型 <br />strcpy(temp,"123"); </p>
		<p>短整型(int) <br />i = atoi(temp); <br />长整型(long) <br />l = atol(temp); <br />浮点(double) <br />d = atof(temp); <br />CString变量 <br />CString name = temp; <br />BSTR变量 <br />BSTR bstrValue = ::SysAllocString(L"程序员"); <br />...///完成对bstrValue的使用 <br />SysFreeString(bstrValue); </p>
		<p>CComBSTR变量 <br />CComBSTR类型变量可以直接赋值 <br />CComBSTR bstrVar1("test"); <br />CComBSTR bstrVar2(temp); </p>
		<p>_bstr_t变量 <br />_bstr_t类型的变量可以直接赋值 <br />_bstr_t bstrVar1("test"); <br />_bstr_t bstrVar2(temp); </p>
		<p>
				<br />三、其它数据类型转换到CString <br />使用CString的成员函数Format来转换,例如: </p>
		<p>
				<br />整数(int) <br />str.Format("%d",i); <br />浮点数(float) <br />str.Format("%f",i); <br />字符串指针(char *)等已经被CString构造函数支持的数据类型可以直接赋值 <br />str = username; <br />对于Format所不支持的数据类型，可以通过上面所说的关于其它数据类型转化到char *的方法先转到char *，然后赋值给CString变量。 </p>
		<p>四、BSTR、_bstr_t与CComBSTR </p>
		<p>
				<br />CComBSTR 是ATL对BSTR的封装，_bstr_t是C++对BSTR的封装,BSTR是32位指针,但并不直接指向字串的缓冲区。 <br />char *转换到BSTR可以这样: <br />BSTR b=_com_util::ConvertStringToBSTR("数据");///使用前需要加上comutil.h和comsupp.lib <br />SysFreeString(bstrValue); <br />反之可以使用 <br />char *p=_com_util::ConvertBSTRToString(b); <br />delete p; <br />具体可以参考一，二段落里的具体说明。 </p>
		<p>CComBSTR与_bstr_t对大量的操作符进行了重载，可以直接进行=,!=,==等操作，所以使用非常方便。 <br />特别是_bstr_t,建议大家使用它。 </p>
		<p>
				<br />五、VARIANT 、_variant_t 与 COleVariant </p>
		<p>
				<br />VARIANT的结构可以参考头文件VC98\Include\OAIDL.H中关于结构体tagVARIANT的定义。 <br />对于VARIANT变量的赋值：首先给vt成员赋值，指明数据类型，再对联合结构中相同数据类型的变量赋值，举个例子： <br />VARIANT va; <br />int a=2001; <br />va.vt=VT_I4;///指明整型数据 <br />va.lVal=a; ///赋值 </p>
		<p>对于不马上赋值的VARIANT，最好先用Void VariantInit(VARIANTARG FAR* pvarg);进行初始化,其本质是将vt设置为VT_EMPTY,下表我们列举vt与常用数据的对应关系: </p>
		<p>Byte bVal;  // VT_UI1. <br />Short iVal;  // VT_I2. <br />long lVal;  // VT_I4. <br />float fltVal;  // VT_R4. <br />double dblVal;  // VT_R8. <br />VARIANT_BOOL boolVal;  // VT_BOOL. <br />SCODE scode;  // VT_ERROR. <br />CY cyVal;  // VT_CY. <br />DATE date;  // VT_DATE. <br />BSTR bstrVal;  // VT_BSTR. <br />DECIMAL FAR* pdecVal  // VT_BYREF|VT_DECIMAL. <br />IUnknown FAR* punkVal;  // VT_UNKNOWN. <br />IDispatch FAR* pdispVal;  // VT_DISPATCH. <br />SAFEARRAY FAR* parray;  // VT_ARRAY|*. <br />Byte FAR* pbVal;  // VT_BYREF|VT_UI1. <br />short FAR* piVal;  // VT_BYREF|VT_I2. <br />long FAR* plVal;  // VT_BYREF|VT_I4. <br />float FAR* pfltVal;  // VT_BYREF|VT_R4. <br />double FAR* pdblVal;  // VT_BYREF|VT_R8. <br />VARIANT_BOOL FAR* pboolVal;  // VT_BYREF|VT_BOOL. <br />SCODE FAR* pscode;  // VT_BYREF|VT_ERROR. <br />CY FAR* pcyVal;  // VT_BYREF|VT_CY. <br />DATE FAR* pdate;  // VT_BYREF|VT_DATE. <br />BSTR FAR* pbstrVal;  // VT_BYREF|VT_BSTR. <br />IUnknown FAR* FAR* ppunkVal;  // VT_BYREF|VT_UNKNOWN. <br />IDispatch FAR* FAR* ppdispVal;  // VT_BYREF|VT_DISPATCH. <br />SAFEARRAY FAR* FAR* pparray;  // VT_ARRAY|*. <br />VARIANT FAR* pvarVal;  // VT_BYREF|VT_VARIANT. <br />void FAR* byref;  // Generic ByRef. <br />char cVal;  // VT_I1. <br />unsigned short uiVal;  // VT_UI2. <br />unsigned long ulVal;  // VT_UI4. <br />int intVal;  // VT_INT. <br />unsigned int uintVal;  // VT_UINT. <br />char FAR * pcVal;  // VT_BYREF|VT_I1. <br />unsigned short FAR * puiVal;  // VT_BYREF|VT_UI2. <br />unsigned long FAR * pulVal;  // VT_BYREF|VT_UI4. <br />int FAR * pintVal;  // VT_BYREF|VT_INT. <br />unsigned int FAR * puintVal;  //VT_BYREF|VT_UINT. </p>
		<p>
				<br />_variant_t是VARIANT的封装类，其赋值可以使用强制类型转换，其构造函数会自动处理这些数据类型。 <br />使用时需加上#include <br />例如： <br />long l=222; <br />ing i=100; <br />_variant_t lVal(l); <br />lVal = (long)i; </p>
		<p>
				<br />COleVariant的使用与_variant_t的方法基本一样，请参考如下例子： <br />COleVariant v3 = "字符串", v4 = (long)1999; <br />CString str =(BSTR)v3.pbstrVal; <br />long i = v4.lVal; </p>
		<p>
				<br />六、其它一些COM数据类型 </p>
		<p>根据ProgID得到CLSID <br />HRESULT CLSIDFromProgID( LPCOLESTR lpszProgID,LPCLSID pclsid); <br />CLSID clsid; <br />CLSIDFromProgID( L"MAPI.Folder",&amp;clsid); </p>
		<p>根据CLSID得到ProgID <br />WINOLEAPI ProgIDFromCLSID( REFCLSID clsid,LPOLESTR * lplpszProgID); <br />例如我们已经定义了 CLSID_IApplication,下面的代码得到ProgID <br />LPOLESTR pProgID = 0; <br />ProgIDFromCLSID( CLSID_IApplication,&amp;pProgID); <br />...///可以使用pProgID <br />CoTaskMemFree(pProgID);//不要忘记释放 </p>
		<p>七、ANSI与Unicode <br />Unicode称为宽字符型字串,COM里使用的都是Unicode字符串。 </p>
		<p>将ANSI转换到Unicode <br />(1)通过L这个宏来实现，例如: CLSIDFromProgID( L"MAPI.Folder",&amp;clsid); <br />(2)通过MultiByteToWideChar函数实现转换,例如: <br />char *szProgID = "MAPI.Folder"; <br />WCHAR szWideProgID[128]; <br />CLSID clsid; <br />long lLen = MultiByteToWideChar(CP_ACP,0,szProgID,strlen(szProgID),szWideProgID,sizeof(szWideProgID)); <br />szWideProgID[lLen] = '\0'; <br />(3)通过A2W宏来实现,例如: <br />USES_CONVERSION; <br />CLSIDFromProgID( A2W(szProgID),&amp;clsid); <br />将Unicode转换到ANSI <br />(1)使用WideCharToMultiByte,例如: <br />// 假设已经有了一个Unicode 串 wszSomeString... <br />char szANSIString [MAX_PATH]; <br />WideCharToMultiByte ( CP_ACP, WC_COMPOSITECHECK, wszSomeString, -1, szANSIString, sizeof(szANSIString), NULL, NULL ); <br />(2)使用W2A宏来实现,例如: <br />USES_CONVERSION; <br />pTemp=W2A(wszSomeString); <br />八、其它 </p>
		<p>对消息的处理中我们经常需要将WPARAM或LPARAM等32位数据（DWORD)分解成两个16位数据（WORD),例如： <br />LPARAM lParam; <br />WORD loValue = LOWORD(lParam);///取低16位 <br />WORD hiValue = HIWORD(lParam);///取高16位 </p>
		<p>
				<br />对于16位的数据(WORD)我们可以用同样的方法分解成高低两个8位数据(BYTE),例如: <br />WORD wValue; <br />BYTE loValue = LOBYTE(wValue);///取低8位 <br />BYTE hiValue = HIBYTE(wValue);///取高8位 </p>
		<p>
				<br />两个16位数据（WORD）合成32位数据(DWORD,LRESULT,LPARAM,或WPARAM) <br />LONG MAKELONG( WORD wLow, WORD wHigh ); <br />WPARAM MAKEWPARAM( WORD wLow, WORD wHigh ); <br />LPARAM MAKELPARAM( WORD wLow, WORD wHigh ); <br />LRESULT MAKELRESULT( WORD wLow, WORD wHigh ); </p>
		<p>
				<br />两个8位的数据(BYTE)合成16位的数据(WORD) <br />WORD MAKEWORD( BYTE bLow, BYTE bHigh ); </p>
		<p>
				<br />从R(red),G(green),B(blue)三色得到COLORREF类型的颜色值 <br />COLORREF RGB( BYTE byRed,BYTE byGreen,BYTE byBlue ); <br />例如COLORREF bkcolor = RGB(0x22,0x98,0x34); </p>
		<p>
				<br />从COLORREF类型的颜色值得到RGB三个颜色值 <br />BYTE Red = GetRValue(bkcolor); ///得到红颜色 <br />BYTE Green = GetGValue(bkcolor); ///得到绿颜色 <br />BYTE Blue = GetBValue(bkcolor); ///得到兰颜色 </p>
		<p>
				<br />九、注意事项 <br />假如需要使用到ConvertBSTRToString此类函数,需要加上头文件comutil.h,并在setting中加入comsupp.lib或者直接加上#pragma comment( lib, "comsupp.lib" ) 。<br /></p>
<img src ="http://www.cppblog.com/cppblog/aggbug/10247.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cppblog/" target="_blank">潜龙居</a> 2006-07-20 11:24 <a href="http://www.cppblog.com/cppblog/archive/2006/07/20/10247.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转贴：深度解析VC中的消息（下）</title><link>http://www.cppblog.com/cppblog/archive/2006/07/20/10246.html</link><dc:creator>潜龙居</dc:creator><author>潜龙居</author><pubDate>Thu, 20 Jul 2006 03:23:00 GMT</pubDate><guid>http://www.cppblog.com/cppblog/archive/2006/07/20/10246.html</guid><wfw:comment>http://www.cppblog.com/cppblog/comments/10246.html</wfw:comment><comments>http://www.cppblog.com/cppblog/archive/2006/07/20/10246.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cppblog/comments/commentRss/10246.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cppblog/services/trackbacks/10246.html</trackback:ping><description><![CDATA[消息的接收 <br /><br />　　 消息的接收主要有３个函数：GetMessage、PeekMessage、WaitMessage。 <br /><br />　　　 GetMessage原型如下：BOOL GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax); <br /><br />　　 该函数用来获取与hWnd参数所指定的窗口相关的且wMsgFilterMin和wMsgFilterMax参数所给出的消息值范围内的消息。需要注意的是，如果hWnd为NULL，则GetMessage获取属于调用该函数应用程序的任一窗口的消息，如果wMsgFilterMin和wMsgFilterMax都是０，则GetMessage就返回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息，至于WM_PAINT则只有在其处理之后才被删除。 <br /><br />　　 PeekMessage原型如下：BOOL PeekMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg）； <br /><br />　　 该函数用于查看应用程序的消息队列，如果其中有消息就将其放入lpMsg所指的结构中，不过，与GetMessage不同的是，PeekMessage函数不会等到有消息放入队列时才返回。同样，如果hWnd为NULL，则PeekMessage获取属于调用该函数应用程序的任一窗口的消息，如果hWnd=-1，那么函数只返回把hWnd参数为NULL的PostAppMessage函数送去的消息。如果wMsgFilterMin和wMsgFilterMax都是０，则PeekMessage就返回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息，至于WM_PAINT则只有在其处理之后才被删除。 <br /><br />　　 WaitMessage原型如下：BOOL VaitMessage();当一个应用程序无事可做时，该函数就将控制权交给另外的应用程序，同时将该应用程序挂起，直到一个新的消息被放入应用程序的队列之中才返回。 <br /><br />　　 消息的处理 <br /><br />　　 接下来我们谈一下消息的处理，首先我们来看一下VC中的消息泵： <br /><br />　　 while(GetMessage(&amp;msg, NULL, 0, 0)) <br />{ <br />if(!TranslateAccelerator(msg.hWnd, hAccelTable, &amp;msg)) <br />{ <br />TranslateMessage(&amp;msg); <br />DispatchMessage(&amp;msg); <br />} <br />} <br /><br />　　 首先，GetMessage从进程的主线程的消息队列中获取一个消息并将它复制到MSG结构，如果队列中没有消息，则GetMessage函数将等待一个消息的到来以后才返回。 如果你将一个窗口句柄作为第二个参数传入GetMessage，那么只有指定窗口的的消息可以从队列中获得。GetMessage也可以从消息队列中过滤消息只接受消息队列中落在范围内的消息。这时候就要利用GetMessage／PeekMessage指定一个消息过滤器。这个过滤器是一个消息标识符的范围或者是一个窗体句柄，或者两者同时指定。当应用程序要查找一个后入消息队列的消息是很有用。WM_KEYFIRST 和 WM_KEYLAST 常量用于接受所有的键盘消息。 WM_MOUSEFIRST 和 WM_MOUSELAST 常量用于接受所有的鼠标消息。 <br /><br />　　 然后TranslateAccelerator判断该消息是不是一个按键消息并且是一个加速键消息，如果是，则该函数将把几个按键消息转换成一个加速键消息传递给窗口的回调函数。处理了加速键之后，函数TranslateMessage将把两个按键消息WM_KEYDOWN和WM_KEYUP转换成一个WM_CHAR，不过需要注意的是，消息WM_KEYDOWN,WM_KEYUP仍然将传递给窗口的回调函数。 <br /><br />　　 处理完之后，DispatchMessage函数将把此消息发送给该消息指定的窗口中已设定的回调函数。如果消息是WM_QUIT，则GetMessage返回０，从而退出循环体。应用程序可以使用PostQuitMessage来结束自己的消息循环。通常在主窗口的WM_DESTROY消息中调用。 <br /><br />　　 下面我们举一个常见的小例子来说明这个消息泵的运用： <br /><br />　　 if (::PeekMessage(&amp;msg, m_hWnd, WM_KEYFIRST,WM_KEYLAST, PM_REMOVE)) <br />{ <br />if (msg.message == WM_KEYDOWN &amp;&amp; msg.wParam == VK_ESCAPE)．．． <br />} <br /><br />　　 这里我们接受所有的键盘消息，所以就用WM_KEYFIRST 和 WM_KEYLAST作为参数。最后一个参数可以是PM_NOREMOVE 或者 PM_REMOVE，表示消息信息是否应该从消息队列中删除。 <br /><br />　　 所以这段小代码就是判断是否按下了Esc键，如果是就进行处理。 <br /><br />窗口过程 <br /><br />　　 窗口过程是一个用于处理所有发送到这个窗口的消息的函数。任何一个窗口类都有一个窗口过程。同一个类的窗口使用同样的窗口过程来响应消息。 系统发送消息给窗口过程将消息数据作为参数传递给他，消息到来之后，按照消息类型排序进行处理，其中的参数则用来区分不同的消息，窗口过程使用参数产生合适行为。 <br /><br />　　 一个窗口过程不经常忽略消息，如果他不处理，它会将消息传回到执行默认的处理。窗口过程通过调用DefWindowProc来做这个处理。窗口过程必须return一个值作为它的消息处理结果。大多数窗口只处理小部分消息和将其他的通过DefWindowProc传递给系统做默认的处理。窗口过程被所有属于同一个类的窗口共享，能为不同的窗口处理消息。下面我们来看一下具体的实例： <br /><br />　　　 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) <br />　　　 { <br />int wmId, wmEvent; <br />PAINTSTRUCT ps; <br />HDC hdc; <br />TCHAR szHello[MAX_LOADSTRING]; <br />LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); <br /><br />switch (message) <br />{ <br />case WM_COMMAND: <br />wmId = LOWORD(wParam); <br />wmEvent = HIWORD(wParam); <br />// Parse the menu selections: <br />switch (wmId) <br />{ <br />case IDM_ABOUT: <br />DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); <br />break; <br />case IDM_EXIT: <br />DestroyWindow(hWnd); <br />break; <br />default: <br />return DefWindowProc(hWnd, message, wParam, lParam); <br />} <br />break; <br />case WM_PAINT: <br />hdc = BeginPaint(hWnd, &amp;ps); <br />// TODO: Add any drawing code here... <br />RECT rt; <br />GetClientRect(hWnd, &amp;rt); <br />DrawText(hdc, szHello, strlen(szHello), &amp;rt, DT_CENTER); <br />EndPaint(hWnd, &amp;ps); <br />break; <br />case WM_DESTROY: <br />PostQuitMessage(0); <br />break; <br />default: <br />return DefWindowProc(hWnd, message, wParam, lParam); <br />　　　 } <br />　　 return 0; <br />　　　 } <br /><br />　　 消息分流器 <br /><br />　　 通常的窗口过程是通过一个switch语句来实现的，这个事情很烦，有没有更简便的方法呢？有，那就是消息分流器，利用消息分流器，我们可以把switch语句分成更小的函数，每一个消息都对应一个小函数，这样做的好处就是对消息更容易管理。<br /><br />          之所以被称为消息分流器，就是因为它可以对任何消息进行分流。下面我们做一个函数就很清楚了： <br /><br />　　 void MsgCracker(HWND hWnd,int id,HWND hWndCtl,UINT codeNotify) <br />{ <br />switch(id) <br />{ <br />case ID_A: <br />if(codeNotify==EN_CHANGE)... <br />break; <br />case ID_B: <br />if(codeNotify==BN_CLICKED)... <br />break; <br />.... <br />} <br />} <br /><br />　　 然后我们修改一下窗口过程： <br /><br />　　 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) <br />{ <br />switch(message) <br />{ <br />HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker); <br />HANDLE_MSG(hWnd,WM_DESTROY,MsgCracker); <br />default: <br />return DefWindowProc(hWnd, message, wParam, lParam); <br />　　　 } <br />　　 return 0; <br />　　　 } <br /><br />　　 在WindowsX.h中定义了如下的HANDLE_MSG宏： <br /><br />　　　 #define HANDLE_MSG(hwnd,msg,fn) \ <br />switch(msg): return HANDLE_##msg((hwnd),(wParam),(lParam),(fn)); <br /><br />　　 实际上，HANDLE_WM_XXXX都是宏，例如：HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);将被转换成如下定义： <br /><br />　　　 #define HANDLE_WM_COMMAND(hwnd,wParam,lParam,fn)\ <br />((fn)((hwnd),(int)(LOWORD(wParam)),(HWND)(lParam),(UINT)HIWORD(wParam)),0L); <br /><br />　　 好了，事情到了这一步，应该一切都明朗了。 <br /><br />　　 不过，我们发现在windowsx.h里面还有一个宏：FORWARD_WM_XXXX，我们还是那WM_COMMAND为例，进行分析： <br /><br />　　　 #define FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn) \ <br />(void)(fn)((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),(UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl)) <br /><br />　　 所以实际上，FORWARD_WM_XXXX将消息参数进行了重新构造，生成了wParam &amp;&amp; lParam，然后调用了我们定义的函数。 <br /><br />MFC消息的处理实现方式 <br /><br />　　 初看MFC中的各种消息，以及在头脑中根深蒂固的C++的影响，我们可能很自然的就会想到利用C++的三大特性之一：虚拟机制来实现消息的传递，但是经过分析，我们看到事情并不是想我们想象的那样，在MFC中消息是通过一种所谓的消息映射机制来处理的。 <br /><br />　　 为什么呢？在潘爱民老师翻译的《Visual C++技术内幕》（第4版）中给出了详细的原因说明，我再简要的说一遍。在CWnd类中大约有110个消息，还有其它的MFC的类呢，算起来消息太多了，在C++中对程序中用到的每一个派生类都要有一个vtable，每一个虚函数在vtable中都要占用一个4字节大小的入口地址，这样一来，对于每个特定类型的窗口或控件，应用程序都需要一个440KB大小的表来支持虚拟消息控件函数。 <br /><br />　　 如果说上面的窗口或控件可以勉强实现的话，那么对于菜单命令消息及按钮命令消息呢？因为不同的应用程序有不同的菜单和按钮，我们怎么处理呢？在MFC库的这种消息映射系统就避免了使用大的vtable，并且能够在处理常规Windows消息的同时处理各种各样的应用程序的命令消息。 <br /><br />　　 说白了，MFC中的消息机制其实质是一张巨大的消息及其处理函数的一一对应表，然后加上分析处理这张表的应用框架内部的一些程序代码.这样就可以避免在SDK编程中用到的繁琐的CASE语句。 <br /><br />　　 MFC的消息映射的基类CCmdTarget <br /><br />　　 如果你想让你的控件能够进行消息映射，就必须从CCmdTarget类中派生。CCmdTarget类是MFC处理命令消息的基础、核心。MFC为该类设计了许多成员函数和一些成员数据，基本上是为了解决消息映射问题的，所有响应消息或事件的类都从它派生，例如：应用程序类、框架类、文档类、视图类和各种各样的控件类等等，还有很多。 <br /><br />　　 不过这个类里面有２个函数对消息映射非常重要，一个是静态成员函数DispatchCmdMsg，另一个是虚函数OnCmdMsg。 <br /><br />　　 DispatchCmdMsg专门供MFC内部使用，用来分发Windows消息。OnCmdMsg用来传递和发送消息、更新用户界面对象的状态。 <br /><br />　　 CCmdTarget对OnCmdMsg的默认实现：在当前命令目标(this所指)的类和基类的消息映射数组里搜索指定命令消息的消息处理函数。 <br /><br />　　 这里使用虚拟函数GetMessageMap得到命令目标类的消息映射入口数组_messageEntries，然后在数组里匹配命令消息ID相同、控制通知代码也相同的消息映射条目。其中GetMessageMap是虚拟函数，所以可以确认当前命令目标的确切类。 <br /><br />　　 如果找到了一个匹配的消息映射条目，则使用DispachCmdMsg调用这个处理函数； <br /><br />　　 如果没有找到，则使用_GetBaseMessageMap得到基类的消息映射数组，查找，直到找到或搜寻了所有的基类（到CCmdTarget）为止； <br /><br />　　 如果最后没有找到，则返回FASLE。 <br /><br />　　 每个从CCmdTarget派生的命令目标类都可以覆盖OnCmdMsg，利用它来确定是否可以处理某条命令，如果不能，就通过调用下一命令目标的OnCmdMsg，把该命令送给下一个命令目标处理。通常，派生类覆盖OnCmdMsg时 ，要调用基类的被覆盖的OnCmdMsg。 <br /><br />　　 在MFC框架中，一些MFC命令目标类覆盖了OnCmdMsg，如框架窗口类覆盖了该函数，实现了MFC的标准命令消息发送路径。必要的话，应用程序也可以覆盖OnCmdMsg，改变一个或多个类中的发送规定，实现与标准框架发送规定不同的发送路径。例如，在以下情况可以作这样的处理：在要打断发送顺序的类中把命令传给一个非MFC默认对象；在新的非默认对象中或在可能要传出命令的命令目标中。 <br /><br />　　 消息映射的内容 <br /><br />　　 通过ClassWizard为我们生成的代码，我们可以看到，消息映射基本上分为2大部分： <br /><br />　　 在头文件(.h)中有一个宏DECLARE_MESSAGE_MAP()，他被放在了类的末尾，是一个public属性的；与之对应的是在实现部分（.cpp)增加了一章消息映射表，内容如下： <br /><br />BEGIN_MESSAGE_MAP(当前类, 当前类的基类) <br />file://{{AFX_MSG_MAP(CMainFrame) <br /><br />　 消息的入口项 <br /><br />file://}}AFX_MSG_MAP <br />END_MESSAGE_MAP() <br /><br />　　 但是仅是这两项还远不足以完成一条消息，要是一个消息工作，必须有以下3个部分去协作： <br />1.在类的定义中加入相应的函数声明； <br /><br />　 2.在类的消息映射表中加入相应的消息映射入口项； <br /><br />　 3.在类的实现中加入相应的函数体； <br /><br />　　 消息的添加 <br /><br />　　 有了上面的这些只是作为基础，我们接下来就做我们最熟悉、最常用的工作：添加消息。MFC消息的添加主要有2种方法：自动/手动，我们就以这2种方法为例，说一下如何添加消息。 <br /><br />　　 1、利用Class Wizard实现自动添加<br />　　 在菜单中选择View--&gt;Class Wizard，也可以用单击鼠标右键，选择Class Wizard，同样可以激活Class Wizard。选择Message Map标签，从Class name组合框中选取我们想要添加消息的类。在Object IDs列表框中，选取类的名称。此时， Messages列表框显示该类的大多数(若不是全部的话)可重载成员函数和窗口消息。类重载显示在列表的上部，以实际虚构成员函数的大小写字母来表示。其他为窗口消息，以大写字母出现，描述了实际窗口所能响应的消息ID。选中我们向添加的消息，单击Add Function按钮，Class Wizard自动将该消息添加进来。 <br /><br />　　 有时候，我们想要添加的消息本应该出现在Message列表中，可是就是找不到，怎么办？不要着急，我们可以利用Class Wizard上Class Info标签以扩展消息列表。在该页中，找到Message Filter组合框，通过它可以改变首页中Messages列表框中的选项。这里，我们选择Window，从而显示所有的窗口消息，一把情况下，你想要添加的消息就可以在Message列表框中出现了，如果还没有，那就接着往下看：） <br /><br />　　 2、手动地添加消息处理函数 <br /><br />　　 如果在Messages列表框中仍然看不到我们想要的消息，那么该消息可能是被系统忽略掉或者是你自己创建的，在这种情况下，就必须自己手工添加。根据我们前面所说的消息工作的3个部件，我们一一进行处理： <br /><br />　　 1) 在类的. h文件中添加处理函数的声明，紧接在//}}AFX_MSG行之后加入声明，注意：一定要以afx_msg开头。 <br /><br />　　 通常，添加处理函数声明的最好的地方是源代码中Class Wizard维护的表下面，但是在它标记其领域的｛｛｝｝括弧外面。这些括弧中的任何东西都将会被Class Wizard销毁。 <br /><br />　　 2) 接着，在用户类的.cpp文件中找到//}}AFX_MSG_MAP行，紧接在它之后加入消息入口项。同样，也是放在{ {} }的外面 <br /><br />　　 3) 最后，在该文件中添加消息处理函数的实体<img src ="http://www.cppblog.com/cppblog/aggbug/10246.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cppblog/" target="_blank">潜龙居</a> 2006-07-20 11:23 <a href="http://www.cppblog.com/cppblog/archive/2006/07/20/10246.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转贴：深度解析VC中的消息(中)</title><link>http://www.cppblog.com/cppblog/archive/2006/07/20/10245.html</link><dc:creator>潜龙居</dc:creator><author>潜龙居</author><pubDate>Thu, 20 Jul 2006 03:20:00 GMT</pubDate><guid>http://www.cppblog.com/cppblog/archive/2006/07/20/10245.html</guid><wfw:comment>http://www.cppblog.com/cppblog/comments/10245.html</wfw:comment><comments>http://www.cppblog.com/cppblog/archive/2006/07/20/10245.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cppblog/comments/commentRss/10245.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cppblog/services/trackbacks/10245.html</trackback:ping><description><![CDATA[
		<font size="5">
				<font color="#e61a1a">队列消息和非队列消息<br /></font>   </font>从消息的发送途径来看，消息可以分成2种：队列消息和非队列消息。消息队列由可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护，线程消息队列则由每个GUI线程自己进行维护，为避免给non-GUI现成创建消息队列，所有线程产生时并没有消息队列，仅当线程第一次调用GDI函数数系统给线程创建一个消息队列。队列消息送到系统消息队列，然后到线程消息队列；非队列消息直接送给目的窗口过程。<br />     对于队列消息，最常见的是鼠标和键盘触发的消息，例如WM_MOUSERMOVE,WM_CHAR等消息，还有一些其它的消息，例如：WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后，相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息，然后输送到系统消息队列，由Windows系统去进行处理。Windows系统则在适当的时机，从系统消息队列中取出一个消息，根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口，然后把取出的消息送往创建窗口的线程的相应队列，下面的事情就该由线程消息队列操心了，Windows开始忙自己的事情去了。线程看到自己的消息队列中有消息，就从队列中取出来，通过操作系统发送到合适的窗口过程去处理。<br />     一般来讲，系统总是将消息Post在消息队列的末尾。这样保证窗口以先进先出的顺序接受消息。然而,WM_PAINT是一个例外，同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息, 合并所有的无效区域到一个无效区域。合并WM_PAIN的目的是为了减少刷新窗口的次数。<br />    非队列消息将会绕过系统队列和消息队列，直接将消息发送到窗口过程，。系统发送非队列消息通知窗口，系统发送消息通知窗口。 例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。这些消息通知窗口它被激活了。非队列消息也可以由当应用程序调用系统函数产生。例如,当程序调用SetWindowPos系统发送WM_WINDOWPOSCHANGED消息。一些函数也发送非队列消息，例如下面我们要谈到的函数。<br />     <br />     <font color="#ee1111" size="5">消息的发送</font><br />     了解了上面的这些基础理论之后，我们就可以进行一下简单的消息发送与接收。<br />     把一个消息发送到窗口有3种方式：发送、寄送和广播。<br />     发送消息的函数有SendMessage、SendMessageCallback、SendNotifyMessage、SendMessageTimeout；寄送消息的函数主要有PostMessage、PostThreadMessage、PostQuitMessage；广播消息的函数我知道的只有BroadcastSystemMessage、BroadcastSystemMessageEx。<br />     SendMessage的原型如下：LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)，这个函数主要是向一个或多个窗口发送一条消息，一直等到消息被处理之后才会返回。不过需要注意的是，如果接收消息的窗口是同一个应用程序的一部分，那么这个窗口的窗口函数就被作为一个子程序马上被调用；如果接收消息的窗口是被另外的线程所创建的，那么窗口系统就切换到相应的线程并且调用相应的窗口函数，这条消息不会被放进目标应用程序队列中。函数的返回值是由接收消息的窗口的窗口函数返回，返回的值取决于被发送的消息。<br />     PostMessage的原型如下：BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)，该函数把一条消息放置到创建hWnd窗口的线程的消息队列中，该函数不等消息被处理就马上将控制返回。需要注意的是，如果hWnd参数为HWND_BROADCAST，那么，消息将被寄送给系统中的所有的重叠窗口和弹出窗口，但是子窗口不会收到该消息；如果hWnd参数为NULL，则该函数类似于将dwThreadID参数设置成当前线程的标志来调用PostThreadMEssage函数。<br />　　　从上面的这２个具有代表性的函数，我们可以看出消息的发送方式和寄送方式的区别所在：被发送的消息是否会被立即处理，函数是否立即返回。被发送的消息会被立即处理，处理完毕后函数才会返回；被寄送的消息不会被立即处理，他被放到一个先进先出的队列中，一直等到应用程序空线的时候才会被处理，不过函数放置消息后立即返回。<br />　　　实际上，发送消息到一个窗口处理过程和直接调用窗口处理过程之间并没有太大的区别，他们直接的唯一区别就在于你可以要求操作系统截获所有被发送的消息，但是不能够截获对窗口处理过程的直接调用。<br />　　　以寄送方式发送的消息通常是与用户输入事件相对应的，因为这些事件不是十分紧迫，可以进行缓慢的缓冲处理，例如鼠标、键盘消息会被寄送，而按钮等消息则会被发送。<br />　　　广播消息用得比较少，BroadcastSystemMessage函数原型如下：<br />      long BroadcastSystemMessage(DWORD dwFlags,LPDWORD lpdwRecipients,UINT uiMessage,WPARAM wParam,LPARAM lParam);该函数可以向指定的接收者发送一条消息，这些接收者可以是应用程序、可安装的驱动程序、网络驱动程序、系统级别的设备驱动消息和他们的任意组合。需要注意的是，如果dwFlags参数是BSF_QUERY并且至少一个接收者返回了BROADCAST_QUERY_DENY，则返回值为０，如果没有指定BSF_QUERY，则函数将消息发送给所有接收者，并且忽略其返回值。 
<p>　　　<font color="#e61a1a" size="5"><font color="#f70909">消息的接收</font><br />　　</font>消息的接收主要有３个函数：GetMessage、PeekMessage、WaitMessage。<br />　　　GetMessage原型如下：BOOL GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax);该函数用来获取与hWnd参数所指定的窗口相关的且wMsgFilterMin和wMsgFilterMax参数所给出的消息值范围内的消息。需要注意的是，如果hWnd为NULL，则GetMessage获取属于调用该函数应用程序的任一窗口的消息，如果wMsgFilterMin和wMsgFilterMax都是０，则GetMessage就返回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息，至于WM_PAINT则只有在其处理之后才被删除。<br />　　　PeekMessage原型如下：BOOL PeekMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg）；该函数用于查看应用程序的消息队列，如果其中有消息就将其放入lpMsg所指的结构中，不过，与GetMessage不同的是，PeekMessage函数不会等到有消息放入队列时才返回。同样，如果hWnd为NULL，则PeekMessage获取属于调用该函数应用程序的任一窗口的消息，如果hWnd=-1，那么函数只返回把hWnd参数为NULL的PostAppMessage函数送去的消息。如果wMsgFilterMin和wMsgFilterMax都是０，则PeekMessage就返回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息，至于WM_PAINT则只有在其处理之后才被删除。<br />　　　WaitMessage原型如下：BOOL VaitMessage();当一个应用程序无事可做时，该函数就将控制权交给另外的应用程序，同时将该应用程序挂起，直到一个新的消息被放入应用程序的队列之中才返回。</p><p>      <font color="#ee3d11" size="5">消息的处理</font><br />　　　接下来我们谈一下消息的处理，首先我们来看一下VC中的消息泵：<br />　　　while(GetMessage(&amp;msg, NULL, 0, 0))<br />      {<br /> if(!TranslateAccelerator(msg.hWnd, hAccelTable, &amp;msg))<br />         { <br />    TranslateMessage(&amp;msg);<br />    DispatchMessage(&amp;msg);<br />         }<br />      }</p><p>      首先，GetMessage从进程的主线程的消息队列中获取一个消息并将它复制到MSG结构，如果队列中没有消息，则GetMessage函数将等待一个消息的到来以后才返回。 如果你将一个窗口句柄作为第二个参数传入GetMessage，那么只有指定窗口的的消息可以从队列中获得。GetMessage也可以从消息队列中过滤消息只接受消息队列中落在范围内的消息。这时候就要利用GetMessage／PeekMessage指定一个消息过滤器。这个过滤器是一个消息标识符的范围或者是一个窗体句柄，或者两者同时指定。当应用程序要查找一个后入消息队列的消息是很有用。WM_KEYFIRST 和 WM_KEYLAST 常量用于接受所有的键盘消息。 WM_MOUSEFIRST 和 WM_MOUSELAST 常量用于接受所有的鼠标消息。 <br />　　　然后TranslateAccelerator判断该消息是不是一个按键消息并且是一个加速键消息，如果是，则该函数将把几个按键消息转换成一个加速键消息传递给窗口的回调函数。处理了加速键之后，函数TranslateMessage将把两个按键消息WM_KEYDOWN和WM_KEYUP转换成一个WM_CHAR，不过需要注意的是，消息WM_KEYDOWN,WM_KEYUP仍然将传递给窗口的回调函数。     <br />　　　处理完之后，DispatchMessage函数将把此消息发送给该消息指定的窗口中已设定的回调函数。如果消息是WM_QUIT，则GetMessage返回０，从而退出循环体。应用程序可以使用PostQuitMessage来结束自己的消息循环。通常在主窗口的WM_DESTROY消息中调用。<br />　　　下面我们举一个常见的小例子来说明这个消息泵的运用：<br />　　　if (::PeekMessage(&amp;msg, m_hWnd, WM_KEYFIRST,WM_KEYLAST, PM_REMOVE))<br />      {<br />          if (msg.message == WM_KEYDOWN &amp;&amp; msg.wParam == VK_ESCAPE)．．．<br />      }<br />  　　这里我们接受所有的键盘消息，所以就用WM_KEYFIRST 和 WM_KEYLAST作为参数。最后一个参数可以是PM_NOREMOVE 或者 PM_REMOVE，表示消息信息是否应该从消息队列中删除。                 <br />    　所以这段小代码就是判断是否按下了Esc键，如果是就进行处理。</p><p>　　　<font color="#f73809" size="5">窗口过程</font><br />　　　窗口过程是一个用于处理所有发送到这个窗口的消息的函数。任何一个窗口类都有一个窗口过程。同一个类的窗口使用同样的窗口过程来响应消息。 系统发送消息给窗口过程将消息数据作为参数传递给他，消息到来之后，按照消息类型排序进行处理，其中的参数则用来区分不同的消息，窗口过程使用参数产生合适行为。<br />　　　一个窗口过程不经常忽略消息，如果他不处理，它会将消息传回到执行默认的处理。窗口过程通过调用DefWindowProc来做这个处理。窗口过程必须return一个值作为它的消息处理结果。大多数窗口只处理小部分消息和将其他的通过DefWindowProc传递给系统做默认的处理。窗口过程被所有属于同一个类的窗口共享，能为不同的窗口处理消息。下面我们来看一下具体的实例：<br />　　　LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)<br />　　　{<br /> int wmId, wmEvent;<br /> PAINTSTRUCT ps;<br /> HDC hdc;<br /> TCHAR szHello[MAX_LOADSTRING];<br /> LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);</p><p> switch (message) <br /> {<br />  case WM_COMMAND:<br />   wmId    = LOWORD(wParam); <br />   wmEvent = HIWORD(wParam); <br />   // Parse the menu selections:<br />   switch (wmId)<br />   {<br />    case IDM_ABOUT:<br />       DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);<br />       break;<br />    case IDM_EXIT:<br />       DestroyWindow(hWnd);<br />       break;<br />    default:<br />       return DefWindowProc(hWnd, message, wParam, lParam);<br />   }<br />   break;<br />  case WM_PAINT:<br />   hdc = BeginPaint(hWnd, &amp;ps);<br />   // TODO: Add any drawing code here...<br />   RECT rt;<br />   GetClientRect(hWnd, &amp;rt);<br />   DrawText(hdc, szHello, strlen(szHello), &amp;rt, DT_CENTER);<br />   EndPaint(hWnd, &amp;ps);<br />   break;<br />  case WM_DESTROY:<br />   PostQuitMessage(0);<br />   break;<br />  default:<br />   return DefWindowProc(hWnd, message, wParam, lParam);<br />  　　　 }<br />  　　 return 0;<br />　　　}</p><p>　　　<font color="#ee1111" size="5">消息分流器</font><br />　　　通常的窗口过程是通过一个switch语句来实现的，这个事情很烦，有没有更简便的方法呢？有，那就是消息分流器，利用消息分流器，我们可以把switch语句分成更小的函数，每一个消息都对应一个小函数，这样做的好处就是对消息更容易管理。<br />　　　之所以被称为消息分流器，就是因为它可以对任何消息进行分流。下面我们做一个函数就很清楚了：<br />　　　void MsgCracker(HWND hWnd,int id,HWND hWndCtl,UINT codeNotify)<br />      {<br />          switch(id)<br />          {<br />     case ID_A:<br />                  if(codeNotify==EN_CHANGE)...<br />                  break;<br />             case ID_B:<br />                  if(codeNotify==BN_CLICKED)...<br />                  break;<br />             ....<br />           }<br />      }<br />      然后我们修改一下窗口过程：<br />　　　LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)<br />      {<br /> switch(message)<br />         {<br />             HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);<br />             HANDLE_MSG(hWnd,WM_DESTROY,MsgCracker);<br />     default:<br />  return DefWindowProc(hWnd, message, wParam, lParam);<br />  　　　 }<br />  　　   return 0;<br />　　　}<br />      在WindowsX.h中定义了如下的HANDLE_MSG宏：<br />　　　#define HANDLE_MSG(hwnd,msg,fn) \<br />             switch(msg): return HANDLE_##msg((hwnd),(wParam),(lParam),(fn));<br />      实际上，HANDLE_WM_XXXX都是宏，例如：HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);将被转换成如下定义：<br />　　　#define HANDLE_WM_COMMAND(hwnd,wParam,lParam,fn)\ <br />             ((fn)((hwnd),(int)(LOWORD(wParam)),(HWND)(lParam),(UINT)HIWORD(wParam)),0L);<br />      好了，事情到了这一步，应该一切都明朗了。<br />　　　不过，我们发现在windowsx.h里面还有一个宏：FORWARD_WM_XXXX，我们还是那WM_COMMAND为例，进行分析：<br />　　　#define FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn) \<br />     (void)(fn)((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),(UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl))<br />　　　所以实际上，FORWARD_WM_XXXX将消息参数进行了重新构造，生成了wParam &amp;&amp; lParam，然后调用了我们定义的函数。<br />　　　好了，事情到这里也算是也段落了，明天我们再分析消息在MFC中的处理。</p><img src ="http://www.cppblog.com/cppblog/aggbug/10245.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cppblog/" target="_blank">潜龙居</a> 2006-07-20 11:20 <a href="http://www.cppblog.com/cppblog/archive/2006/07/20/10245.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转贴:深度解析VC中的消息(上)</title><link>http://www.cppblog.com/cppblog/archive/2006/07/20/10244.html</link><dc:creator>潜龙居</dc:creator><author>潜龙居</author><pubDate>Thu, 20 Jul 2006 03:19:00 GMT</pubDate><guid>http://www.cppblog.com/cppblog/archive/2006/07/20/10244.html</guid><wfw:comment>http://www.cppblog.com/cppblog/comments/10244.html</wfw:comment><comments>http://www.cppblog.com/cppblog/archive/2006/07/20/10244.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cppblog/comments/commentRss/10244.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cppblog/services/trackbacks/10244.html</trackback:ping><description><![CDATA[hustli（原作）转自csdn.net摘要：Windows编程和Dos编程，一个很大的区别就是，windows编程是事件驱动，消息传递的。所以，要做好windows编程，必须对消息机制有一个清楚的认识，本文希望能够对消息的传递做一个全面的论述，由于小生初学VC，里面可能有一些错误的地方，还往各位大虾批评、指正。<br /><br />消息是指什么？<br />消息系统对于一个win32程序来说十分重要，它是一个程序运行的动力源泉。一个消息，是系统定义的一个32位的值，他唯一的定义了一个事件，向Windows发出一个通知，告诉应用程序某个事情发生了。例如，单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。<br />消息本身是作为一个记录传递给应用程序的，这个记录中包含了消息的类型以及其他信息。例如，对于单击鼠标所产生的消息来说，这个记录中包含了单击鼠标时的坐标。这个记录类型叫做MSG，MSG含有来自windows应用程序消息队列的消息信息，它在Windows中声明如下：<br />typedef struct tagMsg<br />{<br />HWND hwnd; 接受该消息的窗口句柄<br />UINT message; 消息常量标识符，也就是我们通常所说的消息号<br />WPARAM wParam; 32位消息的特定附加信息，确切含义依赖于消息值<br />LPARAM lParam; 32位消息的特定附加信息，确切含义依赖于消息值<br />DWORD time; 消息创建时的时间<br />POINT pt; 消息创建时的鼠标/光标在屏幕坐标系中的位置<br />}MSG;<br />消息可以由系统或者应用程序产生。系统在发生输入事件时产生消息。举个例子, 当用户敲键, 移动鼠标或者单击控件。系统也产生消息以响应由应用程序带来的变化, 比如应用程序改变系统字体改变窗体大小。应用程序可以产生消息使窗体执行任务，或者与其他应用程序中的窗口通讯。 <br /><br />消息中有什么？<br />我们给出了上面的注释，是不是会对消息结构有了一个比较清楚的认识？如果还没有，那么我们再试着给出下面的解释：<br />hwnd 32位的窗口句柄。窗口可以是任何类型的屏幕对象，因为Win32能够维护大多数可视对象的句柄(窗口、对话框、按钮、编辑框等)。<br />message用于区别其他消息的常量值，这些常量可以是Windows单元中预定义的常量，也可以是自定义的常量。消息标识符以常量命名的方式指出消息的含义。当窗口过程接收到消息之后，他就会使用消息标识符来决定如何处理消息。例如、WM_PAINT告诉窗口过程窗体客户区被改变了需要重绘。符号常量指定系统消息属于的类别，其前缀指明了处理解释消息的窗体的类型。<br />wParam 通常是一个与消息有关的常量值，也可能是窗口或控件的句柄。<br />lParam 通常是一个指向内存中数据的指针。由于WParam、lParam和Pointer都是32位的，因此，它们之间可以相互转换。<br /><br />消息标识符的值<br />系统保留消息标识符的值在0x0000在0x03ff(WM_USER-1)范围。这些值被系统定义消息使用。 应用程序不能使用这些值给自己的消息。我们顺便说一下具有标志性的消息值：<br />WM_NULL---0x0000 空消息。<br />0x0001----0x0087 主要是窗口消息。<br />0x00A0----0x00A9 非客户区消息 <br />0x0100----0x0108 键盘消息<br />0x0111----0x0126 菜单消息<br />0x0132----0x0138 颜色控制消息<br />0x0200----0x020A 鼠标消息<br />0x0211----0x0213 菜单循环消息<br />0x0220----0x0230 多文档消息<br />0x03E0----0x03E8 DDE消息<br />0x0400 WM_USER<br />0x8000 WM_APP<br />0x0400----0x7FFF 应用程序自定义私有消息<br /><br />消息有哪几种？<br />其实，windows中的消息虽然很多，但是种类并不繁杂，大体上有3种：窗口消息、命令消息和控件通知消息。<br />窗口消息大概是系统中最为常见的消息，它是指由操作系统和控制其他窗口的窗口所使用的消息。例如CreateWindow、DestroyWindow和MoveWindow等都会激发窗口消息，还有我们在上面谈到的单击鼠标所产生的消息也是一种窗口消息。<br />命令消息，这是一种特殊的窗口消息，他用来处理从一个窗口发送到另一个窗口的用户请求，例如按下一个按钮，他就会向主窗口发送一个命令消息。<br />控件通知消息，是指这样一种消息，一个窗口内的子控件发生了一些事情，需要通知父窗口。通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框，以及Windows公共控件如树状视图、列表视图等。例如，单击或双击一个控件、在控件中选择部分文本、操作控件的滚动条都会产生通知消息。 她类似于命令消息，当用户与控件窗口交互时，那么控件通知消息就会从控件窗口发送到它的主窗口。但是这种消息的存在并不是为了处理用户命令，而是为了让主窗口能够改变控件，例如加载、显示数据。例如按下一个按钮，他向父窗口发送的消息也可以看作是一个控件通知消息；单击鼠标所产生的消息可以由主窗口直接处理，然后交给控件窗口处理。<br />其中窗口消息及控件通知消息主要由窗口类即直接或间接由CWND类派生类处理。相对窗口消息及控件通知消息而言，命令消息的处理对象范围就广得多，它不仅可以由窗口类处理，还可以由文档类，文档模板类及应用类所处理。<br />由于控件通知消息很重要的，人们用的也比较多，但是具体的含义往往令初学者晕头转向，所以我决定把常见的几个列出来供大家参考：<br />按扭控件<br />BN_CLICKED 用户单击了按钮<br />BN_DISABLE 按钮被禁止<br />BN_DOUBLECLICKED 用户双击了按钮<br />BN_HILITE 用/户加亮了按钮<br />BN_PAINT 按钮应当重画<br />BN_UNHILITE 加亮应当去掉<br />组合框控件<br />CBN_CLOSEUP 组合框的列表框被关闭<br />CBN_DBLCLK 用户双击了一个字符串<br />CBN_DROPDOWN 组合框的列表框被拉出<br />CBN_EDITCHANGE 用户修改了编辑框中的文本<br />CBN_EDITUPDATE 编辑框内的文本即将更新<br />CBN_ERRSPACE 组合框内存不足<br />CBN_KILLFOCUS 组合框失去输入焦点<br />CBN_SELCHANGE 在组合框中选择了一项<br />CBN_SELENDCANCEL 用户的选择应当被取消<br />CBN_SELENDOK 用户的选择是合法的<br />CBN_SETFOCUS 组合框获得输入焦点<br />编辑框控件<br />EN_CHANGE 编辑框中的文本己更新<br />EN_ERRSPACE 编辑框内存不足<br />EN_HSCROLL 用户点击了水平滚动条<br />EN_KILLFOCUS 编辑框正在失去输入焦点<br />EN_MAXTEXT 插入的内容被截断<br />EN_SETFOCUS 编辑框获得输入焦点<br />EN_UPDATE 编辑框中的文本将要更新<br />EN_VSCROLL 用户点击了垂直滚动条消息含义<br />列表框控件<br />LBN_DBLCLK 用户双击了一项<br />LBN_ERRSPACE 列表框内存不够<br />LBN_KILLFOCUS 列表框正在失去输入焦点<br />LBN_SELCANCEL 选择被取消<br />LBN_SELCHANGE 选择了另一项<br />LBN_SETFOCUS 列表框获得输入焦点<br /><img src ="http://www.cppblog.com/cppblog/aggbug/10244.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cppblog/" target="_blank">潜龙居</a> 2006-07-20 11:19 <a href="http://www.cppblog.com/cppblog/archive/2006/07/20/10244.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CWnd下几个相似的WindowMessage Functions的讨论</title><link>http://www.cppblog.com/cppblog/archive/2006/07/20/10242.html</link><dc:creator>潜龙居</dc:creator><author>潜龙居</author><pubDate>Thu, 20 Jul 2006 00:40:00 GMT</pubDate><guid>http://www.cppblog.com/cppblog/archive/2006/07/20/10242.html</guid><wfw:comment>http://www.cppblog.com/cppblog/comments/10242.html</wfw:comment><comments>http://www.cppblog.com/cppblog/archive/2006/07/20/10242.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/cppblog/comments/commentRss/10242.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cppblog/services/trackbacks/10242.html</trackback:ping><description><![CDATA[
		<strong>首先概要的说<br /></strong>SendMessage<br />向CWnd对象发送一个消息，<strong>直到这条消息被处理之后才返回<br /></strong>PostMessage<br />将一条消息放入应用程序的消息队列，<strong>然后不等窗口处理这条消息直接返回<br /></strong>SendNotifyMessage<br />将一条消息发送到窗口并尽快返回，<strong>返回的速度取决于该窗口是否是由调用线程所创建<br />下面我逐条细细说明<br /></strong>CWnd::SendMessage<br />LRESULT SendMessage( UINT message, WPARAM wParam = 0, LPARAMlParam = 0 );<br />返回值 : <strong>消息处理的结果；它的值依赖于发送的消息。</strong><br />参数 : message    指定了要发送的消息。<br />            wParam    指定了与消息有关的附加信息。<br />            lParam      指定了与消息有关的附加信息。<br />说明 :  这个函数向窗口发送指定的消息 。<br />             SendMessage成员函数直接调用窗口过程并在窗口过程处理了消息以后才返回。<br />              这与PostMessage成员函数形成对比，该函数将消息放入窗口的消息队列并立即返回。<br /><br /><br />CWnd::PostMessage<br />BOOL PostMessage( UINT message, WPARAM wParam = 0, LPARAM lParam =0 );<br />返回值 : <strong>如果公布了消息，则返回非零值；否则返回0。</strong><br />参数 :    message    指定了要公布的消息。<br />               wParam    指定了附加的消息信息。这个参数的内容依赖于要公布的消息。<br />                lParam     指定了附加的消息信息。这个参数的内容依赖于要公布的消息。<br />说明 : 这个函数将一个消息放入窗口的消息队列，然后直接返回，并不等待对应的窗口处理消息。<br />           消息队列中的消息是通过调用Windows的GetMessage或PeekMessage函数来获得的。可以通过Windows的PostMessage函数来访问其它应用程序。<br /><br /><br />CWnd::SendNotifyMessage<br />BOOL SendNotifyMessage( UINT message, WPARAM wParam, LPARAMlParam );<br />返回值  : <strong>如果函数成功，则返回非零值；否则返回0。<br /></strong>参数 :     message    指定了要发送的消息。<br />                wParam    指定了与消息有关的附加信息。<br />                lParam      指定了与消息有关的附加信息。<br />说明 : 这个函数向窗口发送指定的消息。<br />            如果窗口是由调用线程创建的，则SendNotifyMessage调用窗口的窗口过程，并在窗口处理了消息之后返回。如果窗口是由其它线程创建的，则SendNotifyMessage将消息传递给窗口过程并立即返回；它并不等待窗口过程结束处理消息。<br /><br /><br />简单总结一下，<br />   <strong>PostMessage只负责将消息放到消息队列中，不确定何时及是否处理<br />    SendMessage要等到受到消息处理的返回码（DWord类型）后才继续<br />    PostMessage执行后马上返回<br />    SendMessage必须等到消息被处理后才会返回。<br /></strong>   <strong>SendMessage消息发出后，如不能尽快得到解决响应，则会导致程序的停滞，所以此函数慎用。PostMessage则无此后果，不过你要是使用PostMessage，那你的消息不知道要等到猴年马月才能被处理。SendMessage可以保证你的消息在得不到处理的情况下谁也别想动你的窗口。<br />     无论是PostMessage还是SendMessage均由主线程处理。<br />Windows的线程有UI(用户接口)线程和工作线程，一般工作线程没有自己的消息循环，而UI(用户接口)线程有自己的消息循环。<br />    所以一般主线程应该是UI(用户接口)线程，其余的处理特定任务的线程都是工作线程，它们把消息发给主线程，由主线程的消息处理函数处理。<br />SendMessage是阻塞的，它要等到主线程处理完消息后才返回的。<br />而PostMessage是非阻塞的，调用之后，立即返回，而没有去关心主线程又没有处理这个消息。<br /></strong><img src ="http://www.cppblog.com/cppblog/aggbug/10242.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cppblog/" target="_blank">潜龙居</a> 2006-07-20 08:40 <a href="http://www.cppblog.com/cppblog/archive/2006/07/20/10242.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简述VC/MFC下窗口的拖放</title><link>http://www.cppblog.com/cppblog/archive/2006/07/18/10200.html</link><dc:creator>潜龙居</dc:creator><author>潜龙居</author><pubDate>Tue, 18 Jul 2006 09:40:00 GMT</pubDate><guid>http://www.cppblog.com/cppblog/archive/2006/07/18/10200.html</guid><wfw:comment>http://www.cppblog.com/cppblog/comments/10200.html</wfw:comment><comments>http://www.cppblog.com/cppblog/archive/2006/07/18/10200.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/cppblog/comments/commentRss/10200.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cppblog/services/trackbacks/10200.html</trackback:ping><description><![CDATA[当鼠标在窗口的非客户区上单击或移动时，Windows就会给窗口发送一个非客户区鼠标消息（消息列表略）。非客户区鼠标消息和客户区鼠标消息唯一的不同是消息ID中的字母ID前缀NC。对于非客户区鼠标消息OnNcMouseMove消息处理程序的原型为afx_msg void OnMsgName(UINT nHitTest,CPoint point)，其中point指的是非客户区的屏幕坐标。nHitTest参数包含表示窗口非客户区上事件发生地方的命中测试码（命中测试码列表略）。<br />窗口在接收客户区或非客户区的鼠标消息之前，它都一定首先接收到光标的屏幕坐标和WM_NCHITTEST消息。大多数应用程序并不处理WM_NCHITTEST消息，而是让Windows自行处理WM_NCHITTEST消息。<br />例程1[工程名ht]      本例程阻止在一个标题栏上的双击影响窗口<br />头文件htView.h中添加afx_msg void OnNcLButtonDblClk(UINT nHitTest,CPoint point); <br />主文件htView.cpp中添加ON_WM_NCLBUTTONDBLCLK()和<br />void CHtView::OnNcLButtonDblClk(UINT nHitTest,CPoint point);<br />{<br />    if( nHitTest != HTCAPTION)<br />       CView::OnNcLButtonDblClk(nHitTest,point);<br />}<br /><br /><br /><br />例程2 [工程名hit]     本例程实现了客户区的窗口拖动<br />头文件hitView.h中添加afx_msg UINT OnNcHitTest(CPoint point); <br />主文件hitView.cpp中添加ON_WM_NCHITTEST()和<br />UINT CHitView::OnNcHitTest(CPoint point)<br />{<br />    UINT nHitTest = CView::OnNcHitTest(point);<br />    if(nHitTest == HTCLIENT)<br />    nHitTest = HTCAPTION;<br />    return nHitTest;<br />}<br /><br />需要源代码例程的可以给我留言。<img src ="http://www.cppblog.com/cppblog/aggbug/10200.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cppblog/" target="_blank">潜龙居</a> 2006-07-18 17:40 <a href="http://www.cppblog.com/cppblog/archive/2006/07/18/10200.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转载自天蝎之巢:Windows编程中SetViewportOrg与SetWindowOrg的理解 </title><link>http://www.cppblog.com/cppblog/archive/2006/07/12/9716.html</link><dc:creator>潜龙居</dc:creator><author>潜龙居</author><pubDate>Wed, 12 Jul 2006 02:08:00 GMT</pubDate><guid>http://www.cppblog.com/cppblog/archive/2006/07/12/9716.html</guid><wfw:comment>http://www.cppblog.com/cppblog/comments/9716.html</wfw:comment><comments>http://www.cppblog.com/cppblog/archive/2006/07/12/9716.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/cppblog/comments/commentRss/9716.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cppblog/services/trackbacks/9716.html</trackback:ping><description><![CDATA[
		<div class="postTitle">Windows编程中SetViewportOrg与SetWindowOrg的理解</div>
		<div class="postText">
				<div class="entity">
						<b>关键词</b>： <a href="http://tag.bokee.com/tag/MFC%2C" target="_blank"><font color="#000080">MFC,</font></a>    <a href="http://tag.bokee.com/tag/SetWindowOrg%2C" target="_blank"><font color="#000080">SetWindowOrg,</font></a>    <a href="http://tag.bokee.com/tag/SetViewportOrg%2C" target="_blank"><font color="#000080">SetViewportOrg,</font></a>    <a href="http://tag.bokee.com/tag/%C9%EE%C8%EB%C7%B3%B3%F6MFC" target="_blank"><font color="#000080">深入浅出MFC</font></a>                                           
<p>最近突然又很有激情的开始看Jeff Prosise的那本"Programming Windows with MFC, 2 ed."。尽管是英文版的，但是感觉这本书上手比喉结的那本所谓的 深入浅出MFC 要容易理解的多。候同学给人一种故弄玄虚故作深沉的感觉，而Jeff Prosise的这本书才真正的称得上是深入浅出。</p><p>尽管如此，其中有关GDI绘图中的坐标映射部分还是有一个问题没有搞清楚，那就是SetWindowOrg和SetViewportOrg这两个函数到底应该如何理解。潘爱民翻译的那本VC内幕没有讲清楚；Jeff Prosise的这本书没有讲清楚；MSDN上的东西看的也是一头雾水；Charles Petzold的那本书还没有来得及看。因为这个问题，昨天晚上是带着遗憾的困惑入睡的。</p><p>总的来说，我对这两个函数的理解导致的结果是与实际程序运行的结果截然相反。依据MSDN上的解释，有一个很严重的问题没有阐述清楚，那就是：所谓的SetWindowOrg(x, y)函数，到底是表示set window origin to (x, y)还是set window origin as (x, y)；to和as在执行的时候，其操作的效果是截然相反的。</p><p>set window origin to (x, y)表示将坐标原点设置到(x, y)；即以(x, y)作为坐标原点，此时原点坐标不再为(0, 0)；<br />set window origin as (x, y)表示将原来的原点(0, 0)的坐标改为(x, y)；即将所有点的坐标增加(+x, +y)；</p><p>现在我的理解是：应该是 set window origin to (x, y)。这种理解基于以下几个前提：<br />1. 所有绘图语句中给出的坐标，全部是逻辑坐标，即在 window 中的坐标(相对于viewport所表示的设备坐标而言);<br />2. 所有用户能看到的点，其设备坐标一定是位于(0, 0)和(1024, 768)范围内；(假设显示器为输出设备，采用MM_TEXT映射方式，且屏幕分辨率为1024*768);<br />3. 所谓“(0,0)就原点，原点的坐标一定就是(0,0)”这种理解，是错误的；<br />4. Viewport中的坐标表示设备坐标；Window中的坐标表示逻辑坐标；<br />5. 当在逻辑坐标中指定新的原点后，在执行映射时，设备坐标的原点一定要与逻辑坐标的新原点重合；反过来也是一样，即两个坐标系的原点一定要重合。</p><p>下面举例说明：(MM_TEXT映射模式)</p><p><br />(1)<br />CRect rect(0, 0, 200, 200);<br />dc.rectangle(rect);<br />上面的语句在屏幕的最左上角绘制一个正方形;(因为此时逻辑坐标与设备坐标没有偏移)</p><p>(2)<br />dc.SetViewportOrg(100, 100);<br />CRect rect(0, 0, 200, 200);<br />dc.rectangle(rect);<br />将设备坐标的原点设置到(100, 100)；即设备坐标的原点不在(0, 0)处，而是在(100, 100)处；此时若执行映射的话，逻辑坐标的原点(0, 0)需要与设备坐标的原点(100, 100)重合(参考前提5)；那么此时绘制的矩形(0, 0, 200, 200)的坐标(为逻辑坐标，参考前提1)在设备坐标中就会映射为(100, 100, 300, 300)，最终我们在显示器上看到的会是一个向右下方偏移(100, 100)的一个边长为200的正方形(用户看到的点是在设备坐标中的，参考前提2)</p><p>(3)<br />dc.SetWindowOrg(100, 100);<br />CRect rect(0, 0, 200, 200);<br />dc.rectangle(rect);<br />将逻辑坐标的原点设置到(100, 100)；即逻辑坐标的原点不在(0, 0)处，而是在(100, 100)处；此时若执行映射的话，设备坐标的原点(0, 0)需要与逻辑坐标的原点(100, 100)重合(参考前提5)；那么此时绘制的矩形(0, 0, 200, 200)的坐标(为逻辑坐标，参考前提1)在设备坐标中就会映射为(-100, -100, 100, 100)，最终我们在显示器上看到的会是一个只有1/4个大小的矩形的一部分(事实上相当于向左上方偏移(100, 100)的一个边长为200的正方形。注意：用户看到的点是在设备坐标中的，参考前提2)<br /><br /></p><p class="diaryFoot">【作者: <a onclick="window.open('http://publishblog.blogchina.com/blog/postMessage.b?receiver=1840330','发送短消息','width=520, height=455')" href="javascript:void(0);"><font color="#000080">xqscorpion</font></a>】 </p></div>
				<div class="operation">
						<a name="trackback">
								<h3>引用地址如下 http://publishblog.blogchina.com/blog/tb.b?diaryID=5172295</h3>
						</a>
				</div>
		</div>
<img src ="http://www.cppblog.com/cppblog/aggbug/9716.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cppblog/" target="_blank">潜龙居</a> 2006-07-12 10:08 <a href="http://www.cppblog.com/cppblog/archive/2006/07/12/9716.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转载：理解SetWindowOrg,SetViewportOrg,SetWindowExt,SetViewportExt</title><link>http://www.cppblog.com/cppblog/archive/2006/07/12/9715.html</link><dc:creator>潜龙居</dc:creator><author>潜龙居</author><pubDate>Wed, 12 Jul 2006 02:07:00 GMT</pubDate><guid>http://www.cppblog.com/cppblog/archive/2006/07/12/9715.html</guid><wfw:comment>http://www.cppblog.com/cppblog/comments/9715.html</wfw:comment><comments>http://www.cppblog.com/cppblog/archive/2006/07/12/9715.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/cppblog/comments/commentRss/9715.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cppblog/services/trackbacks/9715.html</trackback:ping><description><![CDATA[
		<div class="postTitle">
				<b>理解SetWindowOrg,SetViewportOrg,SetWindowExt,SetViewportExt<br /></b>按习惯，(0,0)就原点，原点就是(0,0)，但是如果用此来理解windows的map mode，就会走弯路。其实，稍微改变一下观念，windows的map mode就比较好理解了。举例说明：<br /><br />page space----&gt;device space<br />pDC-&gt;SetMapMode(MM_LOMETRIC);<br />pDC-&gt;SetWindowOrg(40,0);　　//这句“设定”page space的原点为(40,0)，注意，<br />//这时(40,0)就是原点，原点就是(40,0)这个点，其实，(0,0)与原点没有必然联系。这<br />//一句对下面的画图函数在page space中所作的图不会有任何影响。一句话：SetWindowOrg <br />//就是指定一下，page space中哪个点为原点。<br />pDC-&gt;Rectangle(0,0,100,-100);<br />pDC-&gt;Rectangle(0,-100,50,-200);<br /><br />同理，SetViewportOrg也是指定一下，device space中哪个点为原点，两个坐标系映射时，两个原点重合。<br /><br />SetWindowExt设定page space的大小，SetViewportOrg设定device space的大小，其实，真正有意义的只是两者的比例关系，例如，在一个1024*768的显示屏上：<br /><br />pDC-&gt;SetMapMode(MM_ISOTROPIC);<br />pDC-&gt;SetWindowExt(10240,7680);<br />pDC-&gt;SetViewportExt(1024,768);<br />pDC-&gt;Rectangle(0,0,100,100);<br /><br />　　就会画一个10 pixels*10 pixels的矩形。其本质就是，X方向，每个逻辑单位有1024/10240个象素，Y方向每个逻辑单位有768/7680个象素。因此，下面的代码有相同的作用：<br /><br />pDC-&gt;SetMapMode(MM_ISOTROPIC);<br />pDC-&gt;SetWindowExt(102400,76800);<br />pDC-&gt;SetViewportExt(10240,7680);<br />pDC-&gt;Rectangle(0,0,100,100);<br /><br />两者本质一样，前者更易于理解。<br />作者: johnson    来源: 九九星个人主页 -- <a href="http://network.99star.net/type.asp?typeid=2"><font color="#000080">程序设计</font></a> -- <a href="http://network.99star.net/type.asp?typeid=2&amp;ntypeid=21"><font color="#000080">C++程序设计</font></a></div>
<img src ="http://www.cppblog.com/cppblog/aggbug/9715.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cppblog/" target="_blank">潜龙居</a> 2006-07-12 10:07 <a href="http://www.cppblog.com/cppblog/archive/2006/07/12/9715.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>