﻿<?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++博客-李亚@中国-随笔分类-MFC/VC</title><link>http://www.cppblog.com/lilac/category/5636.html</link><description>做.成为.拥有.</description><language>zh-cn</language><lastBuildDate>Wed, 21 May 2008 02:39:10 GMT</lastBuildDate><pubDate>Wed, 21 May 2008 02:39:10 GMT</pubDate><ttl>60</ttl><item><title>[转]常用数据类型的使用与转换</title><link>http://www.cppblog.com/lilac/archive/2008/01/12/41062.html</link><dc:creator>李亚</dc:creator><author>李亚</author><pubDate>Sat, 12 Jan 2008 15:07:00 GMT</pubDate><guid>http://www.cppblog.com/lilac/archive/2008/01/12/41062.html</guid><wfw:comment>http://www.cppblog.com/lilac/comments/41062.html</wfw:comment><comments>http://www.cppblog.com/lilac/archive/2008/01/12/41062.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lilac/comments/commentRss/41062.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lilac/services/trackbacks/41062.html</trackback:ping><description><![CDATA[<p><font size=4>我们先定义一些常见类型变量借以说明</font> </p>
<p><font size=4>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;<br><br><font color=#6699ff><strong>一、其它数据类型转换为字符串</strong></font><br></font></p>
<ul>
    <li><font size=4><font color=#6699ff>短整型(int)</font><br>itoa(i,temp,10);///将i转换为字符串放入temp中,最后一个数字表示十进制<br>itoa(i,temp,2); ///按二进制方式转换 </font>
    <li><font size=4><font color=#6699ff>长整型(long)</font><br>ltoa(l,temp,10); </font>
    <li><font size=4><font color=#6699ff>浮点数(float,double)</font><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为负数 </font>
    <li><font size=4><font color=#6699ff>CString变量</font><br>str = "2008北京奥运";<br>buf = (LPSTR)(LPCTSTR)str; </font>
    <li><font size=4><font color=#6699ff>BSTR变量</font><br>BSTR bstrValue = ::SysAllocString(L"程序员"); <br>char * buf = _com_util::ConvertBSTRToString(bstrValue); <br>SysFreeString(bstrValue); <br>AfxMessageBox(buf); <br>delete(buf); </font>
    <li><font size=4><font color=#6699ff>CComBSTR变量</font><br>CComBSTR bstrVar("test"); <br>char *buf = _com_util::ConvertBSTRToString(bstrVar.m_str); <br>AfxMessageBox(buf); <br>delete(buf); <br></font>
    <li><font size=4><font color=#6699ff>_bstr_t变量</font><br>_bstr_t类型是对BSTR的封装，因为已经重载了=操作符，所以很容易使用<br>_bstr_t bstrVar("test"); <br>const char *buf = bstrVar;///不要修改buf中的内容 <br>AfxMessageBox(buf); <br><br></font>
    <li><font size=4><font color=#6699ff>通用方法(针对非COM数据类型)</font><br>用sprintf完成转换<br></font>
    <pre><font face=Arial size=4>char  buffer[200];char  c = '1';int   i = 35;long  j = 1000;float f = 1.7320534f;sprintf( buffer, "%c",c);sprintf( buffer, "%d",i);sprintf( buffer, "%d",j);sprintf( buffer, "%f",f);</font></pre>
    </li>
</ul>
<p><font size=4><strong><font color=#6699ff>二、字符串转换为其它数据类型</font></strong><br>strcpy(temp,"123"); </font></p>
<ul>
    <li><font size=4><font color=#6699ff>短整型(int)</font><br>i = atoi(temp); </font>
    <li><font size=4><font color=#6699ff>长整型(long)</font><br>l = atol(temp); </font>
    <li><font size=4><font color=#6699ff>浮点(double)</font><br>d = atof(temp); </font>
    <li><font size=4><font color=#6699ff>CString变量</font><br>CString name = temp; </font>
    <li><font size=4><font color=#6699ff>BSTR变量</font> <br>BSTR bstrValue = ::SysAllocString(L"程序员"); <br>...///完成对bstrValue的使用<br>SysFreeString(bstrValue); <br></font>
    <li><font size=4><font color=#6699ff>CComBSTR变量</font><br>CComBSTR类型变量可以直接赋值<br>CComBSTR bstrVar1("test");<br>CComBSTR bstrVar2(temp);<br></font>
    <li><font size=4><font color=#6699ff>_bstr_t变量</font><br>_bstr_t类型的变量可以直接赋值<br>_bstr_t bstrVar1("test"); <br>_bstr_t bstrVar2(temp); <br><br></font></li>
</ul>
<p><font size=4><strong><font color=#6699ff>三、其它数据类型转换到CString</font></strong><br>使用CString的成员函数Format来转换,例如:<br></font></p>
<ul>
    <li><font size=4>整数(int)<br>str.Format("%d",i); </font>
    <li><font size=4>浮点数(float)<br>str.Format("%f",i); </font>
    <li><font size=4>字符串指针(char *)等已经被CString构造函数支持的数据类型可以直接赋值<br>str = username; </font>
    <li><font size=4>对于Format所不支持的数据类型，可以通过上面所说的关于其它数据类型转化到char *的方法先转到char *，然后赋值给CString变量。<br></font></li>
</ul>
<p><font size=4><strong><font color=#6699ff>四、BSTR、_bstr_t与CComBSTR</font></strong><br></font></p>
<ul>
    <li><font size=4>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>具体可以参考一，二段落里的具体说明。<br><br>CComBSTR与_bstr_t对大量的操作符进行了重载，可以直接进行=,!=,==等操作，所以使用非常方便。<br>特别是_bstr_t,建议大家使用它。<br></font></li>
</ul>
<p><font size=4></font></p>
<p><font size=4><strong><font color=#6699ff>五、VARIANT 、_variant_t 与 COleVariant</font></strong><br></font></p>
<ul>
    <li><font size=4>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; ///赋值<br><br>对于不马上赋值的VARIANT，最好先用Void VariantInit(VARIANTARG FAR* pvarg);进行初始化,其本质是将vt设置为VT_EMPTY,下表我们列举vt与常用数据的对应关系:<br><br></font>
    <table cellSpacing=1 cellPadding=0 width=792 bgColor=#333333 border=0>
        <tbody>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>Byte bVal; </font></td>
                <td width=338><font size=4>// VT_UI1.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>Short iVal; </font></td>
                <td width=338><font size=4>// VT_I2.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>long lVal; </font></td>
                <td width=338><font size=4>// VT_I4.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>float fltVal; </font></td>
                <td width=338><font size=4>// VT_R4.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>double dblVal; </font></td>
                <td width=338><font size=4>// VT_R8.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>VARIANT_BOOL boolVal; </font></td>
                <td width=338><font size=4>// VT_BOOL.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>SCODE scode; </font></td>
                <td width=338><font size=4>// VT_ERROR.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>CY cyVal; </font></td>
                <td width=338><font size=4>// VT_CY.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>DATE date; </font></td>
                <td width=338><font size=4>// VT_DATE.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>BSTR bstrVal; </font></td>
                <td width=338><font size=4>// VT_BSTR.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>DECIMAL FAR* pdecVal </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_DECIMAL.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>IUnknown FAR* punkVal; </font></td>
                <td width=338><font size=4>// VT_UNKNOWN.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>IDispatch FAR* pdispVal; </font></td>
                <td width=338><font size=4>// VT_DISPATCH.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>SAFEARRAY FAR* parray; </font></td>
                <td width=338><font size=4>// VT_ARRAY|*.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>Byte FAR* pbVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_UI1.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>short FAR* piVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_I2.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>long FAR* plVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_I4.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>float FAR* pfltVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_R4.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>double FAR* pdblVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_R8.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>VARIANT_BOOL FAR* pboolVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_BOOL.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>SCODE FAR* pscode; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_ERROR.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>CY FAR* pcyVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_CY.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>DATE FAR* pdate; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_DATE.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>BSTR FAR* pbstrVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_BSTR.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>IUnknown FAR* FAR* ppunkVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_UNKNOWN.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>IDispatch FAR* FAR* ppdispVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_DISPATCH.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>SAFEARRAY FAR* FAR* pparray; </font></td>
                <td width=338><font size=4>// VT_ARRAY|*.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>VARIANT FAR* pvarVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_VARIANT.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>void FAR* byref; </font></td>
                <td width=338><font size=4>// Generic ByRef.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>char cVal; </font></td>
                <td width=338><font size=4>// VT_I1.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>unsigned short uiVal; </font></td>
                <td width=338><font size=4>// VT_UI2.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>unsigned long ulVal; </font></td>
                <td width=338><font size=4>// VT_UI4.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>int intVal; </font></td>
                <td width=338><font size=4>// VT_INT.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>unsigned int uintVal; </font></td>
                <td width=338><font size=4>// VT_UINT.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>char FAR * pcVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_I1.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>unsigned short FAR * puiVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_UI2.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>unsigned long FAR * pulVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_UI4.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>int FAR * pintVal; </font></td>
                <td width=338><font size=4>// VT_BYREF|VT_INT.</font></td>
            </tr>
            <tr bgColor=#ffffff>
                <td width=442><font size=4>unsigned int FAR * puintVal; </font></td>
                <td width=338><font size=4>//VT_BYREF|VT_UINT.</font></td>
            </tr>
        </tbody>
    </table>
    <br>
    <li><font size=4>_variant_t是VARIANT的封装类，其赋值可以使用强制类型转换，其构造函数会自动处理这些数据类型。<br>使用时需加上#include &lt;comdef.h&gt;<br>例如：<br>long l=222;<br>ing i=100;<br>_variant_t lVal(l);<br>lVal = (long)i;<br><br></font>
    <li><font size=4>COleVariant的使用与_variant_t的方法基本一样，请参考如下例子：<br>COleVariant v3 = "字符串", v4 = (long)1999;<br>CString str =(BSTR)v3.pbstrVal;<br>long i = v4.lVal;<br><br></font></li>
</ul>
<p><font color=#6699ff size=4><strong>六、其它一些COM数据类型</strong></font></p>
<ul>
    <li><font size=4>根据ProgID得到CLSID<br>HRESULT CLSIDFromProgID( LPCOLESTR lpszProgID,LPCLSID pclsid);<br>CLSID clsid;<br>CLSIDFromProgID( L"MAPI.Folder",&amp;clsid);<br></font>
    <li><font size=4>根据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);//不要忘记释放 <br></font></li>
</ul>
<p><font size=4><font color=#6699ff><strong>七、ANSI与Unicode<br></strong></font>Unicode称为宽字符型字串,COM里使用的都是Unicode字符串。</font></p>
<ul>
    <li><font size=4>将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); </font>
    <li><font size=4>将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); </font></li>
</ul>
<p><font color=#6699ff size=4><strong>八、其它</strong></font></p>
<ul>
    <li><span style="COLOR: red">对消息的处理中我们经常需要将WPARAM或LPARAM等32位数据（DWORD)分解成两个16位数据（WORD),例如：<br>LPARAM lParam;<br>WORD loValue = LOWORD(lParam);///取低16位<br>WORD hiValue = HIWORD(lParam);///取高16位<br><br></span>
    <li><span style="COLOR: red">对于16位的数据(WORD)我们可以用同样的方法分解成高低两个8位数据(BYTE),例如:<br>WORD wValue;<br>BYTE loValue = LOBYTE(wValue);///取低8位<br>BYTE hiValue = HIBYTE(wValue);///取高8位<br><br></span>
    <li><span style="COLOR: red">两个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 ); <br><br></span>
    <li><span style="COLOR: red">两个8位的数据(BYTE)合成16位的数据(WORD)<br>WORD MAKEWORD( BYTE bLow, BYTE bHigh ); <br><br></span>
    <li><font size=4><span style="COLOR: red">从R(red),G(green),B(blue)三色得到COLORREF类型的颜色值<br>COLORREF RGB( BYTE byRed,BYTE byGreen,BYTE byBlue );<br>例如COLORREF bkcolor = RGB(0x22,0x98,0x34);</span><br><br></font>
    <li><font size=4><span style="COLOR: red">从COLORREF类型的颜色值得到RGB三个颜色值<br>BYTE Red = GetRValue(bkcolor); ///得到红颜色<br>BYTE Green = GetGValue(bkcolor); ///得到绿颜色<br>BYTE Blue = GetBValue(bkcolor); ///得到兰颜色</span><br></font></li>
</ul>
<p><font size=4><font color=#6699ff><strong>九、注意事项</strong></font><br>假如需要使用到ConvertBSTRToString此类函数,需要加上头文件comutil.h,并在setting中加入comsupp.lib或者直接加上#pragma comment( lib, "comsupp.lib" )</font></p>
<img src ="http://www.cppblog.com/lilac/aggbug/41062.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lilac/" target="_blank">李亚</a> 2008-01-12 23:07 <a href="http://www.cppblog.com/lilac/archive/2008/01/12/41062.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CString与其他类型的转换----CString字符串转换续      </title><link>http://www.cppblog.com/lilac/archive/2008/01/12/41060.html</link><dc:creator>李亚</dc:creator><author>李亚</author><pubDate>Sat, 12 Jan 2008 15:00:00 GMT</pubDate><guid>http://www.cppblog.com/lilac/archive/2008/01/12/41060.html</guid><wfw:comment>http://www.cppblog.com/lilac/comments/41060.html</wfw:comment><comments>http://www.cppblog.com/lilac/archive/2008/01/12/41060.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lilac/comments/commentRss/41060.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lilac/services/trackbacks/41060.html</trackback:ping><description><![CDATA[CString与其他类型的转换<br>这里只是介绍了很常见方法,还有其他方法也可以实现相关的功能!<br><span style="COLOR: red">1、字符串与数的转换：<br></span>atof(字符串-&gt;double,int,long),itoa(int-&gt;字符串),ltoa(long int-&gt;字符串)<br>double-&gt;CString的方法：CString::Format("%d", &amp;dX);<br>&nbsp;<br><span style="COLOR: red">2、CString to char*</span><br>//经过类型强制转换，可以将CString类型转换成char*，例如：<br>CString cStr = "Hello,world!";<br>char *zStr = (char*)(LPCTSTR)cStr;<br>当然，这只是最快捷的一种方法。因为CString自带一个函数可以将CString对象转换成const char*，也就是LPCTSTR。<br>&nbsp;<br><span style="COLOR: red">3、char* to CString</span><br>//char*类型可以直接给CString，完成自动转换，例如：<br>char *zStr = "Hello,world!";<br>CString cStr = zStr;<br>&nbsp;<br><span style="COLOR: red">4、CString to LPCSTR&nbsp;</span><br>//将CString转换成LPCSTR，需要获得CString的长度CString cStr=_T("Hello,world!");<br>int nLen = cStr.GetLength();<br>LPCSTR lpszBuf = cStr.GetBuffer(nLen);<br>&nbsp;<br><span style="COLOR: red">5、CString to LPSTR</span><br>//这个和第4个技巧是一样的，例如：<br>CString cStr = _T("Hello,world!");<br>int nLen = str.GetLength();<br>LPSTR lpszBuf = str.GetBuffer(nLen);&nbsp;&nbsp;<span style="COLOR: red"></span>
<img src ="http://www.cppblog.com/lilac/aggbug/41060.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lilac/" target="_blank">李亚</a> 2008-01-12 23:00 <a href="http://www.cppblog.com/lilac/archive/2008/01/12/41060.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]CString 操作指南</title><link>http://www.cppblog.com/lilac/archive/2008/01/12/41059.html</link><dc:creator>李亚</dc:creator><author>李亚</author><pubDate>Sat, 12 Jan 2008 14:55:00 GMT</pubDate><guid>http://www.cppblog.com/lilac/archive/2008/01/12/41059.html</guid><wfw:comment>http://www.cppblog.com/lilac/comments/41059.html</wfw:comment><comments>http://www.cppblog.com/lilac/archive/2008/01/12/41059.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lilac/comments/commentRss/41059.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lilac/services/trackbacks/41059.html</trackback:ping><description><![CDATA[<p>CString 是一种很有用的数据类型。它们很大程度上简化了MFC中的许多操作，使得MFC在做字符串操作的时候方便了很多。不管怎样，使用CString有很多特殊的技巧，特别是对于纯C背景下走出来的程序员来说有点难以学习。这篇文章就来讨论这些技巧。<br>　　使用CString可以让你对字符串的操作更加直截了当。这篇文章不是CString的完全手册，但囊括了大部分常见基本问题。</p>
<p>这篇文章包括以下内容： <br><span style="COLOR: #ff0000">CString 对象的连接</span><br><span style="COLOR: #ff0000">格式化字符串（包括 int 型转化为 CString ）</span> <br><span style="COLOR: #ff0000">CString 型转化成 int 型</span> <br><span style="COLOR: #ff0000">CString 型和 char* 类型的相互转化</span><br><span style="COLOR: #ff0000">&nbsp; 1.char* 转化成 CString <br>&nbsp; 2.CString 转化成 char* 之一：使用LPCTSTR强制转化 <br>&nbsp; 3.CString 转化成 char* 之二：使用CString对象的GetBuffer方法 <br>&nbsp; 4.CString 转化成 char* 之三: 和控件的接口 <br>&nbsp; 5.CString 型转化成 BSTR 型；</span> <br><span style="COLOR: #ff0000">BSTR 型转化成 CString 型； <br>VARIANT 型转化成 CString 型； <br>载入字符串表资源； <br>CString 和临时对象；</span> <br><span style="COLOR: #ff0000">CString 的效率； <br>总结 <br></span>下面我分别讨论。</p>
<p>&nbsp;1、CString 对象的连接</p>
<p>　　能体现出 CString 类型方便性特点的一个方面就字符串的连接，使用 CString 类型，你能很方便地连接两个字符串，正如下面的例子：</p>
<p>CString gray("Gray");CString cat("Cat");CString graycat = gray + cat;<br>要比用下面的方法好得多：</p>
<p>char gray[] = "Gray";char cat[] = "Cat";char * graycat = malloc(strlen(gray) + strlen(cat) + 1);strcpy(graycat, gray);strcat(graycat, cat);<br>&nbsp;2、格式化字符串</p>
<p>　　与其用 sprintf() 函数或 wsprintf() 函数来格式化一个字符串，还不如用 CString 对象的Format()方法：</p>
<p>CString s;s.Format(_T("The total is %d"), total);<br>　　用这种方法的好处是你不用担心用来存放格式化后数据的缓冲区是否足够大，这些工作由CString类替你完成。<br>　　格式化是一种把其它不是字符串类型的数据转化为CString类型的最常用技巧，比如，把一个整数转化成CString类型，可用如下方法：</p>
<p>CString s;s.Format(_T("%d"), total);<br>　　我总是对我的字符串使用_T()宏，这是为了让我的代码至少有Unicode的意识，当然，关于Unicode的话题不在这篇文章的讨论范围。_T()宏在8位字符环境下是如下定义的：</p>
<p>#define _T(x) x // 非Unicode版本（non-Unicode version）<br>而在Unicode环境下是如下定义的：</p>
<p>#define _T(x) L##x // Unicode版本（Unicode version）<br>所以在Unicode环境下，它的效果就相当于：</p>
<p>s.Format(L"%d", total);<br>　　如果你认为你的程序可能在Unicode的环境下运行，那么开始在意用 Unicode 编码。比如说，不要用 sizeof() 操作符来获得字符串的长度，因为在Unicode环境下就会有2倍的误差。我们可以用一些方法来隐藏Unicode的一些细节，比如在我需要获得字符长度的时候，我会用一个叫做DIM的宏，这个宏是在我的dim.h文件中定义的，我会在我写的所有程序中都包含这个文件：</p>
<p>#define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )<br>　　这个宏不仅可以用来解决Unicode的字符串长度的问题，也可以用在编译时定义的表格上，它可以获得表格的项数，如下：</p>
<p>class Whatever { ... };Whatever data[] = {&nbsp;&nbsp; { ... },&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp; { ... },};for(int i = 0; i &lt; DIM(data); i++) // 扫描表格寻找匹配项。<br>　　这里要提醒你的就是一定要注意那些在参数中需要真实字节数的API函数调用，如果你传递字符个数给它，它将不能正常工作。如下：<br>TCHAR data[20];lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!lstrcpyn(data, longstring, DIM(data) - 1); // RIGHTWriteFile(f, data, DIM(data), &amp;bytesWritten, NULL); // WRONG!WriteFile(f, data, sizeof(data), &amp;bytesWritten, NULL); // RIGHT<br>造成以上原因是因为lstrcpyn需要一个字符个数作为参数，但是WriteFile却需要字节数作为参数。<br>同样需要注意的是有时候需要写出数据的所有内容。如果你仅仅只想写出数据的真实长度，你可能会认为你应该这样做：</p>
<p>WriteFile(f, data, lstrlen(data), &amp;bytesWritten, NULL); // WRONG<br>但是在Unicode环境下，它不会正常工作。正确的做法应该是这样：</p>
<p>WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &amp;bytesWritten, NULL); // RIGHT<br>　　因为WriteFile需要的是一个以字节为单位的长度。（可能有些人会想&#8220;在非Unicode的环境下运行这行代码，就意味着总是在做一个多余的乘1操作，这样不会降低程序的效率吗？&#8221;这种想法是多余的，你必须要了解编译器实际上做了什么，没有哪一个C或C++编译器会把这种无聊的乘1操作留在代码中。在Unicode环境下运行的时候，你也不必担心那个乘2操作会降低程序的效率，记住，这只是一个左移一位的操作而已，编译器也很乐意为你做这种替换。）<br>　　使用_T宏并不是意味着你已经创建了一个Unicode的程序，你只是创建了一个有Unicode意识的程序而已。如果你在默认的8-bit模式下编译你的程序的话，得到的将是一个普通的8-bit的应用程序（这里的8-bit指的只是8位的字符编码，并不是指8位的计算机系统）；当你在Unicode环境下编译你的程序时，你才会得到一个Unicode的程序。记住，CString 在 Unicode 环境下，里面包含的可都是16位的字符哦。</p>
<p>&nbsp;3、CString 型转化成 int 型</p>
<p>　　把 CString 类型的数据转化成整数类型最简单的方法就是使用标准的字符串到整数转换例程。<br>　　虽然通常你怀疑使用_atoi()函数是一个好的选择，它也很少会是一个正确的选择。如果你准备使用 Unicode 字符，你应该用_ttoi()，它在 ANSI 编码系统中被编译成_atoi()，而在 Unicode 编码系统中编译成_wtoi()。你也可以考虑使用_tcstoul()或者_tcstol()，它们都能把字符串转化成任意进制的长整数（如二进制、八进制、十进制或十六进制），不同点在于前者转化后的数据是无符号的（unsigned），而后者相反。看下面的例子：</p>
<p>CString hex = _T("FAB");CString decimal = _T("4011");ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));<br>&nbsp;4、CString 型和 char* 类型的相互转化</p>
<p>　　这是初学者使用 CString 时最常见的问题。有了 C++ 的帮助，很多问题你不需要深入的去考虑它，直接拿来用就行了，但是如果你不能深入了解它的运行机制，又会有很多问题让你迷惑，特别是有些看起来没有问题的代码，却偏偏不能正常工作。<br>比如，你会奇怪为什么不能写向下面这样的代码呢：</p>
<p>CString graycat = "Gray" + "Cat";<br>或者这样：</p>
<p>CString graycat("Gray" + "Cat");<br>　　事实上，编译器将抱怨上面的这些尝试。为什么呢？因为针对CString 和 LPCTSTR数据类型的各种各样的组合，&#8220; +&#8221; 运算符 被定义成一个重载操作符。而不是两个 LPCTSTR 数据类型，它是底层数据类型。你不能对基本数据（如 int、char 或者 char*）类型重载 C++ 的运算符。你可以象下面这样做：</p>
<p>CString graycat = CString("Gray") + CString("Cat");<br>或者这样：</p>
<p>CString graycat = CString("Gray") + "Cat";<br>研究一番就会发现：&#8220; +&#8221;总是使用在至少有一个 CString 对象和一个 LPCSTR 的场合。</p>
<p>注意，编写有 Unicode 意识的代码总是一件好事，比如：</p>
<p>CString graycat = CString(_T("Gray")) + _T("Cat");<br>这将使得你的代码可以直接移植。</p>
<p>char* 转化为 CString</p>
<p>　　现在你有一个 char* 类型的数据，或者说一个字符串。怎么样创建 CString 对象呢？这里有一些例子：</p>
<p>char * p = "This is a test";<br>或者象下面这样更具有 Unicode 意识：</p>
<p>TCHAR * p = _T("This is a test")<br>或</p>
<p>LPTSTR p = _T("This is a test");<br>你可以使用下面任意一种写法：</p>
<p>CString s = "This is a test"; // 8-bit onlyCString s = _T("This is a test"); // Unicode-awareCString s("This is a test"); // 8-bit onlyCString s(_T("This is a test")); // Unicode-awareCString s = p;CString s(p);<br>　　用这些方法可以轻松将常量字符串或指针转换成 CString。需要注意的是，字符的赋值总是被拷贝到 CString 对象中去的，所以你可以象下面这样操作：</p>
<p>TCHAR * p = _T("Gray");CString s(p);p = _T("Cat");s += p;<br>结果字符串肯定是&#8220;GrayCat&#8221;。</p>
<p>CString 类还有几个其它的构造函数，但是这里我们不考虑它，如果你有兴趣可以自己查看相关文档。</p>
<p>事实上，CString 类的构造函数比我展示的要复杂，比如：</p>
<p>CString s = "This is a test"; <br>　　这是很草率的编码，但是实际上它在 Unicode 环境下能编译通过。它在运行时调用构造函数的 MultiByteToWideChar 操作将 8 位字符串转换成 16 位字符串。不管怎样，如果 char * 指针是网络上传输的 8 位数据，这种转换是很有用的。</p>
<p>CString 转化成 char* 之一：强制类型转换为 LPCTSTR；</p>
<p>　　这是一种略微硬性的转换，有关&#8220;正确&#8221;的做法，人们在认识上还存在许多混乱，正确的使用方法有很多，但错误的使用方法可能与正确的使用方法一样多。<br>　　我们首先要了解 CString 是一种很特殊的 C++ 对象，它里面包含了三个值：一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字符记数以及一个缓冲区长度。 有效字符数的大小可以是从0到该缓冲最大长度值减1之间的任何数（因为字符串结尾有一个NULL字符）。字符记数和缓冲区长度被巧妙隐藏。<br>　　除非你做一些特殊的操作，否则你不可能知道给CString对象分配的缓冲区的长度。这样，即使你获得了该0缓冲的地址，你也无法更改其中的内容，不能截短字符串，也 绝对没有办法加长它的内容，否则第一时间就会看到溢出。<br>　　LPCTSTR 操作符（或者更明确地说就是 TCHAR * 操作符）在 CString 类中被重载了，该操作符的定义是返回缓冲区的地址，因此，如果你需要一个指向 CString 的 字符串指针的话，可以这样做：</p>
<p><br>CString s("GrayCat");LPCTSTR p = s;<br>　　它可以正确地运行。这是由C语言的强制类型转化规则实现的。当需要强制类型转化时，C++规测容许这种选择。比如，你可以将（浮点数）定义为将某个复数 （有一对浮点数）进行强制类型转换后只返回该复数的第一个浮点数（也就是其实部）。可以象下面这样：</p>
<p>Complex c(1.2f, 4.8f);float realpart = c;<br>如果(float)操作符定义正确的话，那么实部的的值应该是1.2。<br>　　这种强制转化适合所有这种情况，例如，任何带有 LPCTSTR 类型参数的函数都会强制执行这种转换。 于是，你可能有这样一个函数（也许在某个你买来的DLL中）：</p>
<p>BOOL DoSomethingCool(LPCTSTR s);<br>你象下面这样调用它：</p>
<p>CString file("c:\\myfiles\\coolstuff")BOOL result = DoSomethingCool(file);<br>　　它能正确运行。因为 DoSomethingCool 函数已经说明了需要一个 LPCTSTR 类型的参数，因此 LPCTSTR 被应用于该参数，在 MFC 中就是返回的串地址。</p>
<p>如果你要格式化字符串怎么办呢？</p>
<p>CString graycat("GrayCat");CString s;s.Format("Mew! I love %s", graycat);<br>　　注意由于在可变参数列表中的值（在函数说明中是以&#8220;...&#8221;表示的）并没有隐含一个强制类型转换操作符。你会得到什么结果呢？<br>　　一个令人惊讶的结果，我们得到的实际结果串是：</p>
<p>"Mew! I love GrayCat"。<br>　　因为 MFC 的设计者们在设计 CString 数据类型时非常小心， CString 类型表达式求值后指向了字符串，所以这里看不到任何象 Format 或 sprintf 中的强制类型转换，你仍然可以得到正确的行为。描述 CString 的附加数据实际上在 CString 名义地址之后。<br>　　有一件事情你是不能做的，那就是修改字符串。比如，你可能会尝试用&#8220;,&#8221;代替&#8220;.&#8221;（不要做这样的，如果你在乎国际化问题，你应该使用十进制转换的 National Language Support 特性，），下面是个简单的例子：</p>
<p>CString v("1.00"); // 货币金额，两位小数LPCTSTR p = v;p[lstrlen(p) - 3] = '','';<br>　　这时编译器会报错，因为你赋值了一个常量串。如果你做如下尝试，编译器也会错：</p>
<p>strcat(p, "each");<br>　　因为 strcat 的第一个参数应该是 LPTSTR 类型的数据，而你却给了一个 LPCTSTR。</p>
<p>　　不要试图钻这个错误消息的牛角尖，这只会使你自己陷入麻烦！</p>
<p>　　原因是缓冲有一个计数，它是不可存取的（它位于 CString 地址之下的一个隐藏区域），如果你改变这个串，缓冲中的字符计数不会反映所做的修改。此外，如果字符串长度恰好是该字符串物理限制的长度（梢后还会讲到这个问题），那么扩展该字符串将改写缓冲以外的任何数据，那是你无权进行写操作的内存（不对吗？），你会毁换坏不属于你的内存。这是应用程序真正的死亡处方。</p>
<p>CString转化成char* 之二：使用 CString 对象的 GetBuffer 方法；</p>
<p>　　如果你需要修改 CString 中的内容，它有一个特殊的方法可以使用，那就是 GetBuffer，它的作用是返回一个可写的缓冲指针。 如果你只是打算修改字符或者截短字符串，你完全可以这样做：</p>
<p>CString s(_T("File.ext"));LPTSTR p = s.GetBuffer();LPTSTR dot = strchr(p, ''.''); // OK, should have used s.Find...if(p != NULL)*p = _T(''\0'');s.ReleaseBuffer();<br>　　这是 GetBuffer 的第一种用法，也是最简单的一种，不用给它传递参数，它使用默认值 0，意思是：&#8220;给我这个字符串的指针，我保证不加长它&#8221;。当你调用 ReleaseBuffer 时，字符串的实际长度会被重新计算，然后存入 CString 对象中。<br>　　必须强调一点，在 GetBuffer 和 ReleaseBuffer 之间这个范围，一定不能使用你要操作的这个缓冲的 CString 对象的任何方法。因为 ReleaseBuffer 被调用之前，该 CString 对象的完整性得不到保障。研究以下代码：</p>
<p>CString s(...);LPTSTR p = s.GetBuffer();//... 这个指针 p 发生了很多事情int n = s.GetLength(); // 很糟D!!!!! 有可能给出错误的答案!!!s.TrimRight(); // 很糟!!!!! 不能保证能正常工作!!!!s.ReleaseBuffer(); // 现在应该 OKint m = s.GetLength(); // 这个结果可以保证是正确的。s.TrimRight(); // 将正常工作。<br>　　假设你想增加字符串的长度，你首先要知道这个字符串可能会有多长，好比是声明字符串数组的时候用：</p>
<p>char buffer[1024];<br>表示 1024 个字符空间足以让你做任何想做得事情。在 CString 中与之意义相等的表示法：</p>
<p>LPTSTR p = s.GetBuffer(1024);<br>　　调用这个函数后，你不仅获得了字符串缓冲区的指针，而且同时还获得了长度至少为 1024 个字符的空间（注意，我说的是&#8220;字符&#8221;，而不是&#8220;字节&#8221;，因为 CString 是以隐含方式感知 Unicode 的）。<br>　　同时，还应该注意的是，如果你有一个常量串指针，这个串本身的值被存储在只读内存中，如果试图存储它，即使你已经调用了 GetBuffer ，并获得一个只读内存的指针，存入操作会失败，并报告存取错误。我没有在 CString 上证明这一点，但我看到过大把的 C 程序员经常犯这个错误。<br>　　C 程序员有一个通病是分配一个固定长度的缓冲，对它进行 sprintf 操作，然后将它赋值给一个 CString：</p>
<p>char buffer[256];sprintf(buffer, "%......", args, ...); // ... 部分省略许多细节CString s = buffer;<br>虽然更好的形式可以这么做：</p>
<p>CString s;s.Format(_T("%...."), args, ...);<br>如果你的字符串长度万一超过 256 个字符的时候，不会破坏堆栈。</p>
<p>　　另外一个常见的错误是：既然固定大小的内存不工作，那么就采用动态分配字节，这种做法弊端更大：</p>
<p>int len = lstrlen(parm1) + 13&nbsp; lstrlen(parm2) + 10 + 100;char * buffer = new char[len];sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);CString s = buffer;......delete [] buffer;<br>它可以能被简单地写成：</p>
<p>CString s;s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);<br>　　需要注意 sprintf 例子都不是 Unicode 就绪的，尽管你可以使用 tsprintf 以及用 _T() 来包围格式化字符串，但是基本 思路仍然是在走弯路，这这样很容易出错。</p>
<p>CString to char * 之三：和控件的接口；</p>
<p>　　我们经常需要把一个 CString 的值传递给一个控件，比如，CTreeCtrl。MFC为我们提供了很多便利来重载这个操作，但是 在大多数情况下，你使用&#8220;原始&#8221;形式的更新，因此需要将墨某个串指针存储到 TVINSERTITEMSTRUCT 结构的 TVITEM 成员中。如下：</p>
<p>TVINSERTITEMSTRUCT tvi;CString s;// ... 为s赋一些值。tvi.item.pszText = s; // Compiler yells at you here// ... 填写tvi的其他域HTREEITEM ti = c_MyTree.InsertItem(&amp;tvi);<br>　　为什么编译器会报错呢？明明看起来很完美的用法啊！但是事实上如果你看看 TVITEM 结构的定义你就会明白，在 TVITEM 结构中 pszText 成员的声明如下：</p>
<p>LPTSTR pszText;int cchTextMax;<br>　　因此，赋值不是赋给一个 LPCTSTR 类型的变量，而且编译器无法知道如何将赋值语句右边强制转换成 LPCTSTR。好吧，你说，那我就改成这样：</p>
<p>tvi.item.pszText = (LPCTSTR)s; //编译器依然会报错。<br>　　编译器之所以依然报错是因为你试图把一个 LPCTSTR 类型的变量赋值给一个 LPTSTR 类型的变量，这种操作在C或C++中是被禁止的。你不能用这种方法 来滥用常量指针与非常量指针概念，否则，会扰乱编译器的优化机制，使之不知如何优化你的程序。比如，如果你这么做：</p>
<p>const int i = ...;//... do lots of stuff... = a[i]; // usage 1// ... lots more stuff... = a[i]; // usage 2<br>　　那么，编译器会以为既然 i 是 const ，所以 usage1和usage2的值是相同的，并且它甚至能事先计算好 usage1 处的 a[i] 的地址，然后保留着在后面的 usage2 处使用，而不是重新计算。如果你按如下方式写的话：</p>
<p>const int i = ...;int * p = &amp;i;//... do lots of stuff... = a[i]; // usage 1// ... lots more stuff(*p)++; // mess over compiler''s assumption// ... and other stuff... = a[i]; // usage 2<br>　　编译器将认为 i 是常量，从而 a[i] 的位置也是常量，这样间接地破坏了先前的假设。因此，你的程序将会在 debug 编译模式（没有优化）和 release 编译模式（完全优化）中反映出不同的行为，这种情况可不好，所以当你试图把指向 i 的指针赋值给一个 可修改的引用时，会被编译器诊断为这是一种伪造。这就是为什么（LPCTSTR）强制类型转化不起作用的原因。<br>　　为什么不把该成员声明成 LPCTSTR 类型呢？因为这个结构被用于读写控件。当你向控件写数据时，文本指针实际上被当成 LPCTSTR，而当你从控件读数据 时，你必须有一个可写的字符串。这个结构无法区分它是用来读还是用来写。</p>
<p>因此，你会常常在我的代码中看到如下的用法：</p>
<p>tvi.item.pszText = (LPTSTR)(LPCTSTR)s;<br>　　它把 CString 强制类型转化成 LPCTSTR，也就是说先获得改字符串的地址，然后再强制类型转化成 LPTSTR，以便可以对之进行赋值操作。 注意这只有在使用 Set 或 Insert 之类的方法才有效！如果你试图获取数据，则不能这么做。<br>　　如果你打算获取存储在控件中的数据，则方法稍有不同，例如，对某个 CTreeCtrl 使用 GetItem 方法，我想获取项目的文本。我知道这些 文本的长度不会超过 MY_LIMIT，因此我可以这样写：</p>
<p>TVITEM tvi;// ... assorted initialization of other fields of tvitvi.pszText = s.GetBuffer(MY_LIMIT);tvi.cchTextMax = MY_LIMIT;c_MyTree.GetItem(&amp;tvi);s.ReleaseBuffer();<br>　　可以看出来，其实上面的代码对所有类型的 Set 方法都适用，但是并不需要这么做，因为所有的类 Set 方法（包括 Insert方法）不会改变字符串的内容。但是当你需要写 CString 对象时，必须保证缓冲是可写的，这正是 GetBuffer 所做的事情。再次强调： 一旦做了一次 GetBuffer 调用，那么在调用 ReleaseBuffer 之前不要对这个 CString 对象做任何操作。</p>
<p>&nbsp;5、CString 型转化成 BSTR 型</p>
<p>　　当我们使用 ActiveX 控件编程时，经常需要用到将某个值表示成 BSTR 类型。BSTR 是一种记数字符串，Intel平台上的宽字符串（Unicode），并且 可以包含嵌入的 NULL 字符。</p>
<p>你可以调用 CString 对象的 AllocSysString 方法将 CString 转化成 BSTR：</p>
<p>CString s;s = ... ; // whateverBSTR b = s.AllocSysString();<br>　　现在指针 b 指向的就是一个新分配的 BSTR 对象，该对象是 CString 的一个拷贝，包含终结 NULL字符。现在你可以将它传递给任何需要 BSTR 的接口。通常，BSTR 由接收它的组件来释放，如果你需要自己释放 BSTR 的话，可以这么做：</p>
<p>::SysFreeString(b);<br>　　对于如何表示传递给 ActiveX 控件的字符串，在微软内部曾一度争论不休，最后 Visual Basic 的人占了上风，BSTR（&#8220;Basic String&#8221;的首字母缩写）就是这场争论的结果。</p>
<p>&nbsp;6、BSTR 型转化成 CString 型</p>
<p>　　由于 BSTR 是记数 Unicode 字符串，你可以用标准转换方法来创建 8 位的 CString。实际上，这是 CString 内建的功能。在 CString 中 有特殊的构造函数可以把 ANSI 转化成 Unicode，也可以把Unicode 转化成 ANSI。你同样可以从 VARIANT 类型的变量中获得 BSTR 类型的字符串，VARIANT 类型是 由各种 COM 和 Automation (自动化)调用返回的类型。</p>
<p>例如，在一个ANSI程序中：</p>
<p>BSTR b;b = ...; // whateverCString s(b == NULL ? L"" : b)<br>　　对于单个的 BSTR 串来说，这种用法可以工作得很好，这是因为 CString 有一个特殊的构造函数以LPCWSTR（BSTR正是这种类型） 为参数，并将它转化成 ANSI 类型。专门检查是必须的，因为 BSTR 可能为空值，而 CString 的构造函数对于 NULL 值情况考虑的不是很周到，（感谢 Brian Ross 指出这一点!）。这种用法也只能处理包含 NUL 终结字符的单字符串；如果要转化含有多个 NULL 字符 串，你得额外做一些工作才行。在 CString 中内嵌的 NULL 字符通常表现不尽如人意，应该尽量避免。<br>　　根据 C/C++ 规则，如果你有一个 LPWSTR，那么它别无选择，只能和 LPCWSTR 参数匹配。</p>
<p>在 Unicode 模式下，它的构造函数是：</p>
<p>CString::CString(LPCTSTR);<br>正如上面所表示的，在 ANSI 模式下，它有一个特殊的构造函数：</p>
<p>CString::CString(LPCWSTR); <br>　　它会调用一个内部的函数将 Unicode 字符串转换成 ANSI 字符串。（在Unicode模式下，有一个专门的构造函数，该函数有一个参数是LPCSTR类型——一个8位 ANSI 字符串 指针，该函数将它加宽为 Unicode 的字符串！）再次强调：一定要检查 BSTR 的值是否为 NULL。<br>　　另外还有一个问题，正如上文提到的：BSTRs可以含有多个内嵌的NULL字符，但是 CString 的构造函数只能处理某个串中单个 NULL 字符。 也就是说，如果串中含有嵌入的 NUL字节，CString 将会计算出错误的串长度。你必须自己处理它。如果你看看 strcore.cpp 中的构造函数，你会发现 它们都调用了lstrlen，也就是计算字符串的长度。<br>　　注意从 Unicode 到 ANSI 的转换使用带专门参数的 ::WideCharToMultiByte，如果你不想使用这种默认的转换方式，则必须编写自己的转化代码。<br>　　如果你在 UNICODE 模式下编译代码，你可以简单地写成：</p>
<p><br>CString convert(BSTR b){&nbsp;&nbsp;&nbsp; if(b == NULL)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return CString(_T(""));&nbsp;&nbsp;&nbsp; CString s(b); // in UNICODE mode&nbsp;&nbsp;&nbsp; return s;}<br>　　如果是 ANSI 模式，则需要更复杂的过程来转换。注意这个代码使用与 ::WideCharToMultiByte 相同的参数值。所以你 只能在想要改变这些参数进行转换时使用该技术。例如，指定不同的默认字符，不同的标志集等。 <br>CString convert(BSTR b){&nbsp;&nbsp;&nbsp; CString s;&nbsp;&nbsp;&nbsp; if(b == NULL)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return s; // empty for NULL BSTR#ifdef UNICODE&nbsp;&nbsp;&nbsp; s = b;#else&nbsp;&nbsp;&nbsp; LPSTR p = s.GetBuffer(SysStringLen(b) + 1);&nbsp;&nbsp;&nbsp;&nbsp; ::WideCharToMultiByte(CP_ACP,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ANSI Code Page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // no flags&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // source widechar string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -1,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // assume NUL-terminated&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // target buffer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SysStringLen(b)+1, // target buffer length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NULL,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // use system default char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NULL);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // don''t care if default used&nbsp;&nbsp;&nbsp; s.ReleaseBuffer();#endif&nbsp;&nbsp;&nbsp; return s;}<br>　　我并不担心如果 BSTR 包含没有映射到 8 位字符集的 Unicode 字符时会发生什么，因为我指定了::WideCharToMultiByte 的最后两个参数为 NULL。这就是你可能需要改变的地方。 </p>
<p>&nbsp;7、VARIANT 型转化成 CString 型</p>
<p>　　事实上，我从来没有这么做过，因为我没有用 COM/OLE/ActiveX 编写过程序。但是我在microsoft.public.vc.mfc 新闻组上看到了 Robert Quirk 的一篇帖子谈到了这种转化，我觉得把他的文章包含在我的文章里是不太好的做法，所以在这里多做一些解释和演示。如果和他的文章有相孛的地方可能是我的疏忽。<br>　　VARIANT 类型经常用来给 COM 对象传递参数，或者接收从 COM 对象返回的值。你也能自己编写返回 VARIANT 类型的方法，函数返回什么类型 依赖可能（并且常常）方法的输入参数（比如，在自动化操作中，依赖与你调用哪个方法。IDispatch::Invoke 可能返回（通过其一个参数）一个 包含有BYTE、WORD、float、double、date、BSTR 等等 VARIANT 类型的结果，（详见 MSDN 上的 VARIANT 结构的定义）。在下面的例子中，假设 类型是一个BSTR的变体，也就是说在串中的值是通过 bsrtVal 来引用，其优点是在 ANSI 应用中，有一个构造函数会把 LPCWCHAR 引用的值转换为一个 CString（见 BSTR-to-CString 部分）。在 Unicode 模式中，将成为标准的 CString 构造函数，参见对缺省::WideCharToMultiByte 转换的告诫，以及你觉得是否可以接受（大多数情况下，你会满意的）。<br>VARIANT vaData;vaData = m_com.YourMethodHere();ASSERT(vaData.vt == VT_BSTR);CString strData(vaData.bstrVal);<br>你还可以根据 vt 域的不同来建立更通用的转换例程。为此你可能会考虑：</p>
<p><br>CString VariantToString(VARIANT * va){&nbsp;&nbsp;&nbsp; CString s;&nbsp;&nbsp;&nbsp; switch(va-&gt;vt)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { /* vt */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case VT_BSTR:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return CString(vaData-&gt;bstrVal);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case VT_BSTR | VT_BYREF:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return CString(*vaData-&gt;pbstrVal);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case VT_I4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.Format(_T("%d"), va-&gt;lVal);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return s;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case VT_I4 | VT_BYREF:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.Format(_T("%d"), *va-&gt;plVal);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case VT_R8:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.Format(_T("%f"), va-&gt;dblVal);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return s;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ... 剩下的类型转换由读者自己完成&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return CString("");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } /* vt */}<br>&nbsp;8、载入字符串表资源</p>
<p>　　如果你想创建一个容易进行语言版本移植的应用程序，你就不能在你的源代码中直接包含本土语言字符串 （下面这些例子我用的语言都是英语，因为我的本土语是英语），比如下面这种写法就很糟：<br>CString s = "There is an error";<br>　　你应该把你所有特定语言的字符串单独摆放（调试信息、在发布版本中不出现的信息除外）。这意味着向下面这样写比较好：</p>
<p>s.Format(_T("%d - %s"), code, text);<br>　　在你的程序中，文字字符串不是语言敏感的。不管怎样，你必须很小心，不要使用下面这样的串：</p>
<p>// fmt is "Error in %s file %s"// readorwrite is "reading" or "writing"s.Format(fmt, readorwrite, filename); <br>　　这是我的切身体会。在我的第一个国际化的应用程序中我犯了这个错误，尽管我懂德语，知道在德语的语法中动词放在句子的最后面，我们的德国方面的发行人还是苦苦的抱怨他们不得不提取那些不可思议的德语错误提示信息然后重新格式化以让它们能正常工作。比较好的办法（也是我现在使用的办法）是使用两个字符串，一个用 于读，一个用于写，在使用时加载合适的版本，使得它们对字符串参数是非敏感的。也就是说加载整个格式，而不是加载串&#8220;reading&#8221;，&#8220;writing&#8221;：</p>
<p>// fmt is "Error in reading file %s"// "Error in writing file %s"s.Format(fmt, filename);<br>　　一定要注意，如果你有好几个地方需要替换，你一定要保证替换后句子的结构不会出现问题，比如在英语中，可以是主语-宾语，主语-谓语，动词-宾语的结构等等。<br>　　在这里，我们并不讨论 FormatMessage，其实它比 sprintf/Format 还要有优势，但是不太容易和CString 结合使用。解决这种问题的办法就是我们按照参数出现在参数表中的位置给参数取名字，这样在你输出的时候就不会把他们的位置排错了。<br>　　接下来我们讨论我们这些独立的字符串放在什么地方。我们可以把字符串的值放入资源文件中的一个称为 STRINGTABLE 的段中。过程如下：首先使用 Visual Studio 的资源编辑器创建一个字符串，然后给每一个字符串取一个ID，一般我们给它取名字都以 IDS_开头。所以如果你有一个信息，你可以创建一个字符串资源然后取名为 IDS_READING_FILE，另外一个就取名为 IDS_WRITING_FILE。它们以下面的形式出现在你的 .rc 文件中：</p>
<p>STRINGTABLEIDS_READING_FILE "Reading file %s"IDS_WRITING_FILE "Writing file %s"END<br>注意：这些资源都以 Unicode 的格式保存，不管你是在什么环境下编译。他们在Win9x系统上也是以Unicode 的形式存在，虽然 Win9x 不能真正处理 Unicode。<br>然后你可以这样使用这些资源：<br>// 在使用资源串表之前，程序是这样写的：</p>
<p><br>&nbsp;&nbsp; CString fmt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(...)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt = "Reading file %s";&nbsp;&nbsp;&nbsp;&nbsp; else&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt = "Writing file %s";&nbsp; ...&nbsp;&nbsp;&nbsp; // much later&nbsp; CString s;&nbsp; s.Format(fmt, filename); <br>// 使用资源串表之后，程序这样写： <br>&nbsp;&nbsp;&nbsp; CString fmt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(...)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.LoadString(IDS_READING_FILE);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.LoadString(DS_WRITING_FILE);&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // much later&nbsp;&nbsp;&nbsp; CString s;&nbsp;&nbsp;&nbsp; s.Format(fmt, filename);<br>　　现在，你的代码可以移植到任何语言中去。LoadString 方法需要一个字符串资源的 ID 作为参数，然后它从 STRINGTABLE 中取出它对应的字符串，赋值给 CString 对象。 CString 对象的构造函数还有一个更加聪明的特征可以简化 STRINGTABLE 的使用。这个用法在 CString::CString 的文档中没有指出，但是在 构造函数的示例程序中使用了。（为什么这个特性没有成为正式文档的一部分，而是放在了一个例子中，我记不得了！）——【译者注：从这句话看，作者可能是CString的设计者。其实前面还有一句类似的话。说他没有对使用GetBuffer(0)获得的指针指向的地址是否可读做有效性检查 】。这个特征就是：如果你将一个字符串资源的ID强制类型转换为 LPCTSTR，将会隐含调用 LoadString。因此，下面两个构造字符串的例子具有相同的效果，而且其 ASSERT 在debug模式下不会被触发：<br>CString s;s.LoadString(IDS_WHATEVER);CString t( (LPCTSTR)IDS_WHATEVER );ASSERT(s == t);//不会被触发，说明s和t是相同的。<br>　　现在，你可能会想：这怎么可能工作呢？我们怎么能把 STRINGTABLE ID 转化成一个指针呢？很简单：所有的字符串 ID 都在1~65535这个范围内，也就是说，它所有的高位都是0，而我们在程序中所使用的指针是不可能小于65535的，因为程序的低 64K 内存永远也不可能存在的，如果你试图访问0x00000000到0x0000FFFF之间的内存，将会引发一个内存越界错误。所以说1~65535的值不可能是一个内存地址，所以我们可以用这些值来作为字符串资源的ID。<br>　　我倾向于使用 MAKEINTRESOURCE 宏显式地做这种转换。我认为这样可以让代码更加易于阅读。这是个只适合在 MFC 中使用的标准宏。你要记住，大多数的方法即可以接受一个 UINT 型的参数，也可以接受一个 LPCTSTR 型的参数，这是依赖 C++ 的重载功能做到的。C++重载函数带来的 弊端就是造成所有的强制类型转化都需要显示声明。同样，你也可以给很多种结构只传递一个资源名。</p>
<p>CString s;s.LoadString(IDS_WHATEVER);CString t( MAKEINTRESOURCE(IDS_WHATEVER));ASSERT(s == t);<br>　　告诉你吧：我不仅只是在这里鼓吹，事实上我也是这么做的。在我的代码中，你几乎不可能找到一个字符串，当然，那些只是偶然在调试中出现的或者和语言无关的字符串除外。</p>
<p>&nbsp;9、CString 和临时对象</p>
<p>　　这是出现在 microsoft.public.vc.mfc 新闻组中的一个小问题，我简单的提一下，这个问题是有个程序员需要往注册表中写入一个字符串，他写道：<br>　　我试着用 RegSetValueEx() 设置一个注册表键的值，但是它的结果总是令我困惑。当我用char[]声明一个变量时它能正常工作，但是当我用 CString 的时候，总是得到一些垃圾："&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;...&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;"为了确认是不是我的 CString 数据出了问题，我试着用 GetBuffer，然后强制转化成 char*，LPCSTR。GetBuffer 返回的值是正确的，但是当我把它赋值给 char* 时，它就变成垃圾了。以下是我的程序段：</p>
<p>char* szName = GetName().GetBuffer(20);RegSetValueEx(hKey, "Name", 0, REG_SZ,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (CONST BYTE *) szName,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strlen (szName + 1));<br>这个 Name 字符串的长度小于 20，所以我不认为是 GetBuffer 的参数的问题。</p>
<p>真让人困惑，请帮帮我。</p>
<p>亲爱的 Frustrated，</p>
<p>你犯了一个相当微妙的错误，聪明反被聪明误，正确的代码应该象下面这样：</p>
<p><br>CString Name = GetName();RegSetValueEx(hKey, _T("Name"), 0, REG_SZ,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (CONST BYTE *) (LPCTSTR)Name,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (Name.GetLength() + 1) * sizeof(TCHAR));<br>　　为什么我写的代码能行而你写的就有问题呢？主要是因为当你调用 GetName 时返回的 CString 对象是一个临时对象。参见：《C++ Reference manual》&#167;12.2<br>　　在一些环境中，编译器有必要创建一个临时对象，这样引入临时对象是依赖于实现的。如果编译器引入的这个临时对象所属的类有构造函数的话，编译器要确保这个类的构造函数被调用。同样的，如果这个类声明有析构函数的话，也要保证这个临时对象的析构函数被调用。<br>　　编译器必须保证这个临时对象被销毁了。被销毁的确切地点依赖于实现.....这个析构函数必须在退出创建该临时对象的范围之前被调用。<br>　　大部分的编译器是这样设计的：在临时对象被创建的代码的下一个执行步骤处隐含调用这个临时对象的析构函数，实现起来，一般都是在下一个分号处。因此，这个 CString 对象在 GetBuffer 调用之后就被析构了（顺便提一句，你没有理由给 GetBuffer 函数传递一个参数，而且没有使用ReleaseBuffer 也是不对的）。所以 GetBuffer 本来返回的是指向这个临时对象中字符串的地址的指针，但是当这个临时对象被析构后，这块内存就被释放了。然后 MFC 的调试内存分配器会重新为这块内存全部填上 0xDD，显示出来刚好就是&#8220;&amp;Yacute;&#8221;符号。在这个时候你向注册表中写数据，字符串的内容当然全被破坏了。<br>　　我们不应该立即把这个临时对象转化成 char* 类型，应该先把它保存到一个 CString 对象中，这意味着把临时对象复制了一份，所以当临时的 CString 对象被析构了之后，这个 CString 对象中的值依然保存着。这个时候再向注册表中写数据就没有问题了。<br>　　此外，我的代码是具有 Unicode 意识的。那个操作注册表的函数需要一个字节大小，使用lstrlen(Name+1) 得到的实际结果对于 Unicode 字符来说比 ANSI 字符要小一半，而且它也不能从这个字符串的第二个字符起开始计算，也许你的本意是 lstrlen(Name) + 1（OK，我承认，我也犯了同样的错误！）。不论如何，在 Unicode 模式下，所有的字符都是2个字节大小，我们需要处理这个问题。微软的文档令人惊讶地对此保持缄默：REG_SZ 的值究竟是以字节计算还是以字符计算呢？我们假设它指的是以字节为单位计算，你需要对你的代码做一些修改来计算这个字符串所含有的字节大小。</p>
<p>&nbsp;10、CString 的效率</p>
<p>　　CString 的一个问题是它确实掩藏了一些低效率的东西。从另外一个方面讲，它也确实可以被实现得更加高效，你可能会说下面的代码：<br>CString s = SomeCString1;s += SomeCString2;s += SomeCString3;s += ",";s += SomeCString4;<br>比起下面的代码来，效率要低多了：</p>
<p>char s[1024];lstrcpy(s, SomeString1);lstrcat(s, SomeString2);lstrcat(s, SomeString 3);lstrcat(s, ",");lstrcat(s, SomeString4);<br>　　总之，你可能会想，首先，它为 SomeCString1 分配一块内存，然后把 SomeCString1 复制到里面，然后发现它要做一个连接，则重新分配一块新的足够大的内存，大到能够放下当前的字符串加上SomeCString2，把内容复制到这块内存 ，然后把 SomeCString2 连接到后面，然后释放第一块内存，并把指针重新指向新内存。然后为每个字符串重复这个过程。把这 4 个字符串连接起来效率多低啊。事实上，在很多情况下根本就不需要复制源字符串（在 += 操作符左边的字符串）。<br>　　在 VC++6.0 中，Release 模式下，所有的 CString 中的缓存都是按预定义量子分配的。所谓量子，即确定为 64、128、256 或者 512 字节。这意味着除非字符串非常长，连接字符串的操作实际上就是 strcat 经过优化后的版本（因为它知道本地的字符串应该在什么地方结束，所以不需要寻找字符串的结尾；只需要把内存中的数据拷贝到指定的地方即可）加上重新计算字符串的长度。所以它的执行效率和纯 C 的代码是一样的，但是它更容易写、更容易维护和更容易理解。<br>　　如果你还是不能确定究竟发生了怎样的过程，请看看 CString 的源代码，strcore.cpp，在你 vc98的安装目录的 mfc\src 子目录中。看看 ConcatInPlace 方法，它被在所有的 += 操作符中调用。</p>
<p>啊哈！难道 CString 真的这么"高效"吗？比如，如果我创建</p>
<p>CString cat("Mew!");<br>　　然后我并不是得到了一个高效的、精简的5个字节大小的缓冲区（4个字符加一个结束字符），系统将给我分配64个字节，而其中59个字节都被浪费了。<br>　　如果你也是这么想的话，那么就请准备好接受再教育吧。可能在某个地方某个人给你讲过尽量使用少的空间是件好事情。不错，这种说法的确正确，但是他忽略了事实中一个很重要的方面。<br>　　如果你编写的是运行在16K EPROMs下的嵌入式程序的话，你有理由尽量少使用空间，在这种环境下，它能使你的程序更健壮。但是在 500MHz, 256MB的机器上写 Windows 程序，如果你还是这么做，它只会比你认为的&#8220;低效&#8221;的代码运行得更糟。<br>　　举例来说。字符串的大小被认为是影响效率的首要因素，使字符串尽可能小可以提高效率，反之则降低效率，这是大家一贯的想法。但是这种想法是不对的，精确的内存分配的后果要在程序运行了好几个小时后才能体现得出来，那时，程序的堆中将充满小片的内存，它们太小以至于不能用来做任何事，但是他们增加了你程序的内存用量，增加了内存页面交换的次数，当页面交换的次数增加到系统能够忍受的上限，系统则会为你的程序分配更多的页面，直到你的程序占用了所有的可用内存。由此可见，虽然内存碎片是决定效率的次要因素，但正是这些因素实际控制了系统的行为，最终，它损害了系统的可靠性，这是令人无法接受的。<br>　　记住，在 debug 模式下，内存往往是精确分配的，这是为了更好的排错。<br>　　假设你的应用程序通常需要连续工作好几个月。比如，我常打开 VC++，Word，PowerPoint，Frontpage，Outlook Express，Fort&#233; Agent，Internet Explorer和其它的一些程序，而且通常不关闭它们。我曾经夜以继日地连续用 PowerPoint 工作了好几天（反之，如果你不幸不得不使用像 Adobe FrameMaker 这样的程序的话，你将会体会到可靠性的重要；这个程序机会每天都要崩溃4~6次，每次都是因为用完了所有的空间并填满我所有的交换页面）。所以精确内存分配是不可取的，它会危及到系统的可靠性，并引起应用程序崩溃。<br>　　按量子的倍数为字符串分配内存，内存分配器就可以回收用过的内存块，通常这些回收的内存块马上就可以被其它的 CString 对象重新用到，这样就可以保证碎片最少。分配器的功能加强了，应用程序用到的内存就能尽可能保持最小，这样的程序就可以运行几个星期或几个月而不出现问题。<br>　　题外话：很多年以前，我们在 CMU 写一个交互式系统的时候，一些对内存分配器的研究显示出它往往产生很多内存碎片。Jim Mitchell，现在他在 Sun Microsystems 工作，那时侯他创造了一种内存分配器，它保留了一个内存分配状况的运行时统计表，这种技术和当时的主流分配器所用的技术都不同，且较为领先。当一个内存块需要被分割得比某一个值小的话，他并不分割它，因此可以避免产生太多小到什么事都干不了的内存碎片。事实上他在内存分配器中使用了一个浮动指针，他认为：与其让指令做长时间的存取内存操作，还不如简单的忽略那些太小的内存块而只做一些浮动指针的操作。（His observation was that the long-term saving in instructions by not having to ignore unusable small storage chunks far and away exceeded the additional cost of doing a few floating point operations on an allocation operation.）他是对的。<br>　　永远不要认为所谓的&#8220;最优化&#8221;是建立在每一行代码都高速且节省内存的基础上的，事实上，高速且节省内存应该是在一个应用程序的整体水平上考虑的。在软件的整体水平上，只使用最小内存的字符串分配策略可能是最糟糕的一种方法。<br>　　如果你认为优化是你在每一行代码上做的那些努力的话，你应该想一想：在每一行代码中做的优化很少能真正起作用。你可以看我的另一篇关于优化问题的文章《Your Worst Enemy for some thought-provoking ideas》。<br>　　记住，+= 运算符只是一种特例，如果你写成下面这样：</p>
<p>CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;<br>则每一个 + 的应用会造成一个新的字符串被创建和一次复制操作。</p>
<p>&nbsp;总结</p>
<p>　　以上是使用 CString 的一些技巧。我每天写程序的时候都会用到这些。CString 并不是一种很难使用的类，但是 MFC 没有很明显的指出这些特征，需要你自己去探索、去发现。 <br></p>
<img src ="http://www.cppblog.com/lilac/aggbug/41059.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lilac/" target="_blank">李亚</a> 2008-01-12 22:55 <a href="http://www.cppblog.com/lilac/archive/2008/01/12/41059.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] 深入探讨MFC消息循环和消息泵</title><link>http://www.cppblog.com/lilac/archive/2007/12/17/38782.html</link><dc:creator>李亚</dc:creator><author>李亚</author><pubDate>Mon, 17 Dec 2007 12:28:00 GMT</pubDate><guid>http://www.cppblog.com/lilac/archive/2007/12/17/38782.html</guid><wfw:comment>http://www.cppblog.com/lilac/comments/38782.html</wfw:comment><comments>http://www.cppblog.com/lilac/archive/2007/12/17/38782.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lilac/comments/commentRss/38782.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lilac/services/trackbacks/38782.html</trackback:ping><description><![CDATA[<h2 align=center>深入探讨MFC消息循环和消息泵</h2>
<p align=center><strong>作者：周焱</strong></p>
<p><strong><font color=#0909f7>首 先，应该清楚MFC的消息循环(::GetMessage,::PeekMessage)，消息泵(CWinThread::PumpMessage)和 MFC的消息在窗口之间的路由是两件不同的事情。在MFC的应用程序中(应用程序类基于CWinThread继承)，必须要有一个消息循环，他的作用是从 应用程序的消息队列中读取消息，并把它派送出去(::DispatchMessage)。而消息路由是指消息派送出去之后，系统(USER32.DLL) 把消息投递到哪个窗口，以及以后消息在窗口之间的传递是怎样的。</font></strong></p>
<p>消息分为队列消息(进入线程的消息队列) 和非队列消息(不进入线程的消息队列)。对于队列消息，最常见的是鼠标和键盘触发的消息，例如WM_MOUSERMOVE,WM_CHAR等消息；还有例 如：WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后，相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息，然后输 送到系统消息队列，由Windows系统负责把消息加入到相应线程的消息队列中，于是就有了消息循环(从消息队列中读取并派送消息)。还有一种是非队列消 息，他绕过系统队列和消息队列，直接将消息发送到窗口过程。例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。创建窗口时发送WM_CREATE消息。在后面你将看到，MS这么设计是很有道理的，以及他的整套实现机制。</p>
<p>这里讲述MFC的消息循环，消息泵。先看看程序启动时，怎么进入消息循环的：<br>_tWinMain -&gt;AfxWinMain -&gt;AfxWinInit -&gt;CWinThread::InitApplication -&gt;CWinThread::InitInstance -&gt;CWinThread::Run</p>
<p>非对话框程序的消息循环的事情都从这CWinThread的一Run开始...</p>
<p><font color=#ff3300><strong>第一部分：非对话框程序的消息循环机制。</strong></font></p>
<p><font color=#88777e>//thrdcore.cpp<br></font>// main running routine until thread exits<br>int CWinThread::Run()<br>{<br>ASSERT_VALID(this);</p>
<p>// for tracking the idle time state<br>BOOL bIdle = TRUE;<br>LONG lIdleCount = 0;</p>
<p>// acquire and dispatch messages until a WM_QUIT message is received.<br>for (;;)<br>{<br><font color=#3300ff>// phase1: check to see if we can do idle work</font><br>while (bIdle &amp;&amp;<br>&nbsp;&nbsp; !::PeekMessage(&amp;m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))<br>{<br>&nbsp;&nbsp; // call OnIdle while in bIdle state<br>&nbsp;&nbsp; if (!OnIdle(lIdleCount++))<br>&nbsp;&nbsp;&nbsp; bIdle = FALSE; // assume "no idle" state<br>}</p>
<p><font color=#3300ff>// phase2: pump messages while available<br></font>do<br>{<br>&nbsp;&nbsp; // pump message, but quit on WM_QUIT<br>&nbsp;&nbsp; if (!PumpMessage())<br>&nbsp;&nbsp;&nbsp; return ExitInstance();</p>
<p>&nbsp;&nbsp; // reset "no idle" state after pumping "normal" message<br>&nbsp;&nbsp; if (IsIdleMessage(&amp;m_msgCur))<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; bIdle = TRUE;<br>&nbsp;&nbsp;&nbsp; lIdleCount = 0;<br>&nbsp;&nbsp; }</p>
<p>} while (::PeekMessage(&amp;m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));<br>}&nbsp;&nbsp;&nbsp; <font color=#0033ff>//无限循环，退出条件是收到WM_QUIT消息。</font></p>
<p>ASSERT(FALSE); <font color=#0033ff>// not reachable<br></font>}</p>
<p>这是一个无限循环，他的退出条件是收到WM_QUIT消息：</p>
<p>if (!PumpMessage())<br>&nbsp;&nbsp;&nbsp; return ExitInstance();</p>
<p>在PumpMessage中，如果收到WM_QUIT消息，那么返回FALSE,所以ExitInstance()函数执行，跳出循环，返回程序的退出代码。所以，一个程序要退出，只用在代码中调用函数</p>
<p><font color=#3300ff>VOID PostQuitMessage( int nExitCode )。指定退出代码nExitCode就可以退出程序。</font></p>
<p>下面讨论一下这个函数Run的流程，分两步：</p>
<p>1, 第一个内循环phase1。bIdle代表程序是否空闲。他的意思就是，如果程序是空闲并且消息队列中没有要处理的消息，那么调用虚函数OnIdle进行 空闲处理。在这个处理中将更新UI界面(比如工具栏按钮的enable和disable状态)，删除临时对象(比如用FromHandle得到的对象指 针。由于这个原因，在函数之间传递由FromHandle得到的对象指针是不安全的，因为他没有持久性)。OnIdle是可以重载的，你可以重载他并返回 TRUE使消息循环继续处于空闲状态。</p>
<p><font color=#3300ff>NOTE：MS用临时对象是出于效率上的考虑，使内存 有效利用，并能够在空闲时自动撤销资源。关于由句柄转换成对象，可以有若干种方法。一般是先申明一个对象obj,然后使用obj.Attatch来和一个 句柄绑定。这样产生的对象是永久的，你必须用obj.Detach来释放对象。</font></p>
<p>2，第二个内循环phase2。在这个循环内先启动消息泵(PumpMessage),如果不是WM_QUIT消息，消息泵将消息发送出去(::DispatchMessage)。消息的目的地是消息结构中的hwnd字段所对应的窗口。<br><font color=#88777e>//thrdcore.cpp<br></font>BOOL CWinThread::PumpMessage()<br>{<br>ASSERT_VALID(this);<br><br><font color=#3300ff>//如果是WM_QUIT就退出函数(return FALSE)，这将导致程序结束.<br></font>if (!::GetMessage(&amp;m_msgCur, NULL, NULL, NULL)) {<br>#ifdef _DEBUG<br>&nbsp;&nbsp; if (afxTraceFlags &amp; traceAppMsg)<br>&nbsp;&nbsp;&nbsp; TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n");<br>&nbsp;&nbsp; m_nDisablePumpCount++; // application must die<br>&nbsp;&nbsp;&nbsp; // Note: prevents calling message loop things in 'ExitInstance'<br>&nbsp;&nbsp;&nbsp; // will never be decremented<br>#endif<br>&nbsp;&nbsp; return FALSE;<br>}</p>
<p>#ifdef _DEBUG<br>if (m_nDisablePumpCount != 0)<br>{<br>&nbsp;&nbsp; TRACE0("Error: CWinThread::PumpMessage called when not permitted.\n");<br>&nbsp;&nbsp; ASSERT(FALSE);<br>}<br>#endif</p>
<p>#ifdef _DEBUG<br>if (afxTraceFlags &amp; traceAppMsg)<br>&nbsp;&nbsp; _AfxTraceMsg(_T("PumpMessage"), &amp;m_msgCur);<br>#endif</p>
<p>// process this message</p>
<p>if (m_msgCur.message != WM_KICKIDLE &amp;&amp; !PreTranslateMessage(&amp;m_msgCur))<br>{<br>&nbsp;&nbsp; ::TranslateMessage(&amp;m_msgCur); <font color=#0033ff>//键转换</font><br>&nbsp;&nbsp; ::DispatchMessage(&amp;m_msgCur); <font color=#0033ff>//派送消息</font><br>}<br>return TRUE;<br>}</p>
<p>在 这一步有一个特别重要的函数大家一定认识：PreTranslateMessage。这个函数在::DispatchMessage发送消息到窗口之前， 进行对消息的预处理。PreTranslateMessage函数是CWinThread的成员函数，大家重载的时候都是在View类或者主窗口类中，那 么，它是怎么进入别的类的呢？代码如下：<br><font color=#88777e>//thrdcore.cpp</font><br>BOOL CWinThread::PreTranslateMessage(MSG* pMsg)<br>{<br>ASSERT_VALID(this);</p>
<p><font color=#0033ff>// 如果是线程消息，那么将会调用线程消息的处理函数</font><br>if (pMsg-&gt;hwnd == NULL &amp;&amp; DispatchThreadMessageEx(pMsg))<br>&nbsp;&nbsp; return TRUE;</p>
<p>// walk from target to main window<br>CWnd* pMainWnd = AfxGetMainWnd();<br>if (CWnd::WalkPreTranslateTree(pMainWnd-&gt;GetSafeHwnd(), pMsg))<br>&nbsp;&nbsp; return TRUE;</p>
<p>// in case of modeless dialogs, last chance route through main<br>//&nbsp;&nbsp; window's accelerator table<br>if (pMainWnd != NULL)<br>{<br>&nbsp;&nbsp; CWnd* pWnd = CWnd::FromHandle(pMsg-&gt;hwnd);<br>&nbsp;&nbsp; if (pWnd-&gt;GetTopLevelParent() != pMainWnd)<br>&nbsp;&nbsp;&nbsp; return pMainWnd-&gt;PreTranslateMessage(pMsg);<br>}</p>
<p>return FALSE;&nbsp;&nbsp; // no special processing<br>}</p>
<p>由上面这个函数可以看出：</p>
<p>第一，如果(pMsg-&gt;hwnd == NULL),说明这是一个线程消息。调用CWinThread::DispatchThreadMessageEx到消息映射表找到消息入口，然后调用消息处理函数。</p>
<p><font color=#3300ff>NOTE: 一般用PostThreadMessage函数发送线程之间的消息，他和窗口消息不同，需要指定线程id,消息激被系统放入到目标线程的消息队列中；用 ON_THREAD_MESSAGE( message, memberFxn )宏可以映射线程消息和他的处理函数。这个宏必须在应用程序类(从CWinThread继承)中，因为只有应用程序类才处理线程消息。如果你在别的类(比 如视图类)中用这个宏，线程消息的消息处理函数将得不到线程消息。</font></p>
<p>第二，消息的目标窗口的 PreTranslateMessage函数首先得到消息处理权，如果函数返回FALSE，那么他的父窗口将得到消息的处理权，直到主窗口；如果函数返回 TRUE(表示消息已经被处理了)，那么就不需要调用父类的PreTranslateMessage函数。这样，保证了消息的目标窗口以及他的父窗口都可 以有机会调用PreTranslateMessage--在消息发送到窗口之前进行预处理(如果自己处理完然后返回FALSE的话 -_-b),如果你想要消息不传递给父类进行处理的话，返回TRUE就行了。</p>
<p>第三，如果消息的目标窗口和主窗口没有父子关系，那么再调用主 窗口的PreTranslateMessage函数。为什么这样？由第二步知道，一个窗口的父窗口不是主窗口的话，尽管它的 PreTranslateMessage返回FALSE，主窗口也没有机会调用PreTranslateMessage函数。我们知道，加速键的转换一般 在框架窗口的PreTranslateMessage函数中。我找遍了MFC中关于加速键转换的处理，只有CFrameWnd, CMDIFrameWnd，CMDIChildWnd等窗口类有。所以，第三步的意思是，如果消息的目标窗口(他的父窗口不是主窗口，比如一个这样的非模 式对话框)使消息的预处理继续漫游的话(他的PreTranslateMessage返回FALSE)，那么给一次机会给主窗口调用 PreTranslateMessage(万一他是某个加速键消息呢？)，这样能够保证在有非模式对话框的情况下还能保证主窗口的加速键好使。<br>我做了一个小例子，在对话框类的PreTranslateMessage中，返回FALSE。在主窗口显示这个非模式对话框，在对话框拥有焦点的时候，仍然能够激活主窗口的快捷键。</p>
<p><font color=#0033ff>总之，整个框架就是让每个消息的目标窗口(包括他的父窗口)都有机会参与消息到来之前的处理。呵呵~</font></p>
<p>至 此，非对话框的消息循环和消息泵的机制就差不多了。这个机制在一个无限循环中，不断地从消息队列中获取消息，并且保证了程序的线程消息能够得到机会处理， 窗口消息在预处理之后被发送到相应的窗口处理过程。那么，还有一点疑问，为什么要一会儿调用::PeekMessage,一会儿调用:: GetMessage呢，他们有什么区别？</p>
<p><font color=#0033ff>NOTE：一般来说，GetMessage被设计用来高效地从消息队列获取消息。如果队列中没有消息，那么函数GetMessage将导致线程休眠(让出CPU时间)。而PeekMessage是判断消息队列中如果没有消息，它马上返回0，不会导致线程处于睡眠状态。</font></p>
<p>在 上面的phase1第一个内循环中用到了PeekMessage，它的参数PM_NOREMOVE表示并不从消息队列中移走消息，而是一个检测查询，如果 消息队列中没有消息他立刻返回0，如果这时线程空闲的话将会引起消息循环调用OnIdle处理过程(上面讲到了这个函数的重要性)。如果将:: PeekMessage改成::GetMessage(***),那么如果消息队列中没有消息，线程将休眠，直到线程下一次获得CPU时间并且有消息出现 才可能继续执行，这样，消息循环的空闲时间没有得到应用，OnIdle也将得不到执行。这就是为什么既要用::PeekMessage(查询),又要 用::GetMessage(做实际的工作)的缘故。</p>
<p><strong><font color=#ff6600>第二部分: 对话框程序的消息循环机制</font></strong></p>
<p><strong></strong></p>
<p>基于对话框的MFC工程和上面的消息循环机制不一样。实际上MFC的对话框工程程序就是模式对话框。他和上面讲到的非对话框程序的不同之处，主要在于应用程序对象的InitInstance()不一样。</p>
<p><font color=#88777e>//dlg_5Dlg.cpp</font><br>BOOL CDlg_5App::InitInstance()<br>{<br>AfxEnableControlContainer();<br>#ifdef _AFXDLL<br>Enable3dControls();&nbsp;&nbsp;&nbsp; // Call this when using MFC in a shared DLL<br>#else<br>Enable3dControlsStatic(); // Call this when linking to MFC statically<br>#endif</p>
<p>CDlg_5Dlg dlg; <font color=#0909f7>//定义一个对话框对象</font><br>m_pMainWnd = &amp;dlg;<br>int nResponse = dlg.DoModal(); <font color=#0033ff>//对话框的消息循环在这里面开始</font><br>if (nResponse == IDOK)<br>{<br>&nbsp;&nbsp; // TODO: Place code here to handle when the dialog is<br>&nbsp;&nbsp; // dismissed with OK<br>}<br>else if (nResponse == IDCANCEL)<br>{<br>&nbsp;&nbsp; // TODO: Place code here to handle when the dialog is<br>&nbsp;&nbsp; // dismissed with Cancel<br>}</p>
<p>// Since the dialog has been closed, return FALSE so that we exit the<br>// application, rather than start the application's message pump.<br>return FALSE;<br>}</p>
<p><font color=#0909f7>NOTE: InitInstance函数返回FALSE，由最上面程序启动流程可以看出，CWinThread::Run是不会得到执行的。也就是说，上面第一部分 说的消息循环在对话框中是不能执行的。实际上，对话框也有消息循环，她的消息循环在CDialog::DoModal()虚函数中的一个 RunModalLoop函数中。</font></p>
<p>这个函数的实现体在CWnd类中：<br>int CWnd::RunModalLoop(DWORD dwFlags)<br>{<br>ASSERT(::IsWindow(m_hWnd)); // window must be created<br>ASSERT(!(m_nFlags &amp; WF_MODALLOOP)); // window must not already be in modal state</p>
<p>// for tracking the idle time state<br>BOOL bIdle = TRUE;<br>LONG lIdleCount = 0;<br>BOOL bShowIdle = (dwFlags &amp; MLF_SHOWONIDLE) &amp;&amp; !(GetStyle() &amp; WS_VISIBLE);<br>HWND hWndParent = ::GetParent(m_hWnd);<br>m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);<br>MSG* pMsg = &amp;AfxGetThread()-&gt;m_msgCur;</p>
<p>// acquire and dispatch messages until the modal state is done<br>for (;;)<br>{<br>&nbsp;&nbsp; ASSERT(ContinueModal());</p>
<p><font color=#0033ff>&nbsp;&nbsp; // phase1: check to see if we can do idle work</font><br>&nbsp;&nbsp; while (bIdle &amp;&amp;<br>&nbsp;&nbsp;&nbsp; !::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; ASSERT(ContinueModal());</p>
<p>&nbsp;&nbsp;&nbsp; // show the dialog when the message queue goes idle<br>&nbsp;&nbsp;&nbsp; if (bShowIdle)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; ShowWindow(SW_SHOWNORMAL);<br>&nbsp;&nbsp;&nbsp;&nbsp; UpdateWindow();<br>&nbsp;&nbsp;&nbsp;&nbsp; bShowIdle = FALSE;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; // call OnIdle while in bIdle state<br>&nbsp;&nbsp;&nbsp; if (!(dwFlags &amp; MLF_NOIDLEMSG) &amp;&amp; hWndParent != NULL &amp;&amp; lIdleCount == 0)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; // send WM_ENTERIDLE to the parent<br>&nbsp;&nbsp;&nbsp;&nbsp; ::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_hWnd);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; if ((dwFlags &amp; MLF_NOKICKIDLE) ||<br>&nbsp;&nbsp;&nbsp;&nbsp; !SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; // stop idle processing next time<br>&nbsp;&nbsp;&nbsp;&nbsp; bIdle = FALSE;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp; }</p>
<p><font color=#0909f7>// phase2: pump messages while available<br></font>&nbsp;&nbsp; do<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; ASSERT(ContinueModal());</p>
<p>&nbsp;&nbsp;&nbsp; // pump message, but quit on WM_QUIT<br>&nbsp;&nbsp;&nbsp;<font color=#0909f7>//PumpMessage(消息泵)的实现和上面讲的差不多。都是派送消息到窗口。</font><br>&nbsp;&nbsp;&nbsp; if (!AfxGetThread()-&gt;PumpMessage())<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; AfxPostQuitMessage(0);<br>&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; // show the window when certain special messages rec'd<br>&nbsp;&nbsp;&nbsp; if (bShowIdle &amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp; (pMsg-&gt;message == 0x118 || pMsg-&gt;message == WM_SYSKEYDOWN))<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; ShowWindow(SW_SHOWNORMAL);<br>&nbsp;&nbsp;&nbsp;&nbsp; UpdateWindow();<br>&nbsp;&nbsp;&nbsp;&nbsp; bShowIdle = FALSE;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; if (!ContinueModal())<br>&nbsp;&nbsp;&nbsp;&nbsp; goto ExitModal;</p>
<p>&nbsp;&nbsp;&nbsp; // reset "no idle" state after pumping "normal" message<br>&nbsp;&nbsp;&nbsp; if (AfxGetThread()-&gt;IsIdleMessage(pMsg))<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; bIdle = TRUE;<br>&nbsp;&nbsp;&nbsp;&nbsp; lIdleCount = 0;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp; } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));<br>} <font color=#0909f7>//无限循环</font></p>
<p>ExitModal:<br>m_nFlags &amp;= ~(WF_MODALLOOP|WF_CONTINUEMODAL);<br>return m_nModalResult;<br>}</p>
<p>先说说怎么退出这个无限循环，在代码中：<br>if (!ContinueModal())<br>goto ExitModal;<br>决定是否退出循环，消息循环函数返回也就是快要结束结束程序了。<br>BOOL CWnd::ContinueModal()<br>{<br>return m_nFlags &amp; WF_CONTINUEMODAL;<br>}</p>
<p><font color=#0033ff>NOTE: CWnd::ContinueModal()函数检查对话框是否继续模式。返回TRUE,表示现在是模式的；返回FALSE，表示对话框已经不是模式(将要结束)。</font></p>
<p>如 果要结束对话框，在内部最终会调用函数CWnd::EndModalLoop，它取消m_nFlags的模式标志(消息循环中的 ContinueModal函数将返回FALSE，消息循环将结束，程序将退出)；然后激发消息循环读取消息。也就是说，结束模式对话框是一个标志，改变 这个标志就可以了。他的代码是：</p>
<p><font color=#88777e>//wincore.cpp<br></font>void CWnd::EndModalLoop(int nResult)<br>{<br>ASSERT(::IsWindow(m_hWnd));</p>
<p>// this result will be returned from CWnd::RunModalLoop<br>m_nModalResult = nResult;</p>
<p>// make sure a message goes through to exit the modal loop<br>if (m_nFlags &amp; WF_CONTINUEMODAL)<br>{<br>&nbsp;&nbsp; m_nFlags &amp;= ~WF_CONTINUEMODAL;<br>&nbsp;&nbsp; PostMessage(WM_NULL);<br>}<br>}</p>
<p><font color=#0909f7>NOTE: PostMessage(NULL)是有用的。如果消息队列中没有消息的话，可能消息循环中的ContinueModal()不会马上执行，发送一个空消息是激发消息循环马上工作。</font></p>
<p>下面说一下CWnd::RunModalLoop函数中的消息循环究竟干了些什么事情:<br>1, 第一个内循环。首先从消息队列中查询消息，如果对话框空闲，而且消息队列中没有消息，他做三件事情，大家应到都能从字面上明白什么意思。最重要的是发送 WM_KICKIDLE消息。为什么呢？第一部分讲到了，非对话框程序用OnIdle来更新用户界面(UI)，比如工具栏，状态栏。那么，如果对话框中也 有工具栏和状态栏呢，在哪里更新(网上有很多这样的程序)？可以处理WM_KICKIDLE消息：<br><br>LRESULT CDlg_5Dlg::OnKickIdle(WPARAM w,LPARAM l)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; //调用CWnd::UpdateDialogControls更新用户界面<br>&nbsp;&nbsp;&nbsp;&nbsp; UpdateDialogControls(this, TRUE);<br>&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>}</p>
<p><font color=#0033ff>NOTE: CWnd::UpdateDialog函数发送CN_UPDATE_COMMAND_UI消息给所有的用户界面对话框控件。</font> </p>
<p>2, 第二个内循环。最重要的还是PumpMessage派送消息到目标窗口。其他的，像第二个if语句，0x118消息好像是WM_SYSTIMER消息(系 统用来通知光标跳动的一个消息)。也就是说，如果消息为WM_SYSTIMER或者WM_SYSKEYDOWN,并且空闲显示标志为真的话，就显示窗口并 通知窗口立刻重绘。</p>
<p><font color=#0909f7>总之，对话框的消息循环机制和非对话框(比如SDI,MDI)还是类似 的，仅仅侧重点不同。模式对话框是模式显示，自然有他的特点。下面部分讨论一下模式对话框和非模式对话框的区别。因为模式对话框有自己的特殊消息循环；而 非模式对话框，共用程序的消息循环，和普通的窗口已经没有什么大的区别了。</font></p>
<p><font color=#ff3300><strong>第三部分：模式对话框和非模式对话框的区别</strong></font></p>
<p><strong></strong></p>
<p>这个话题已经有很多人讨论，我说说我所理解的意思。<br>在MFC 框架中，一个对话框对象DoModal一下就能产生一个模式对话框，Create一下就能产生一个非模式对话框。实际上，无论是模式对话框还是非模式对话 框，在MFC内部都是调用::CreateDialogIndirect(***)函数来创建非模式对话框。只是模式对话框作了更多的工作，包括使父窗口 无效，然后进入自己的消息循环等等。::CreateDialogIndirect(***)函数最终调用CreateWindowEx函数通知系统创建 窗体并返回句柄，他内部没有实现自己的消息循环。<br>非模式对话框创建之后立即返回，并且和主程序共用一个消息循环。非模式对话框要等对话框结束之后才返回，自己有消息循环。比如下面的代码：<br>CMyDlg* pdlg = new CMyDlg;<br>pdlg -&gt;Create(IDD_DIALOG1);<br>pdlg-&gt;ShowWindow(SW_SHOW);<br>MessageBox("abc");<br>非模式对话框和消息框MessageBox几乎是同时弹出来。而如果将Create改成DoModal，那么，只能弹出模式对话框，在关闭了对话框之后(模式对话框自己的消息循环结束)，消息框才弹出来。</p>
<p><font color=#0033ff>NOTE： 可以在模式对话框中调用GetParent()-&gt;EnableWindow(true);这样，主窗口的菜单，工具栏又激活了，能用了。MFC使 用非模式对话框来模拟模式对话框，而在win32 SDK程序中，模式对话框激发他的父窗口Enable操作是没有效果的。</font></p>
<p><font color=#ff3300><strong>关于消息循环总结：</strong></font></p>
<p><br>1， 我们站在一个什么高度看消息循环？消息循环其实没有什么深奥的道理。如果一个邮递员要不断在一个城市中送信，我们要求他做什么？要求他来回跑，但他一次只 能在一个地方出现。如果我们的应用程序只有一个线程的话，我们要他不断地为窗口传递消息，我们怎么做？在一个循环中不断的检测消息，并将他发送到适当的窗 口。窗口可以有很多个，但消息循环只有一个，而且每时每刻最多只有一个地方在执行代码。为什么？ 看第二点。</p>
<p>2，因为是单线程的(程序进程 启动的时候，只有而且有一个线程，我们称他为主线程),所以就像邮递员一样，每次只能在某一个地方干活。什么意思呢？举个例子，用:: DiapatchMessage派送消息，在窗口处理过程(WinProc,窗口函数)返回之前，他是阻塞的,不会立即返回，也就是消息循环此时不能再从 消息队列中读取消息，直到::DispatchMessage返回。如果你在窗口函数中执行一个死循环操作，就算你用PostQuitMessage函数 退出，程序也会down掉。<br>while(1)<br>{<br>&nbsp;&nbsp;&nbsp; PostQuitMessage(0); <font color=#3300ff>//程序照样down.</font><br>}<br>所 以，当窗口函数处理没有返回的时候，消息循环是不会从消息队列中读取消息的。这也是为什么在模式对话框中要自己用无限循环来继续消息循环，因为这个无限循 环阻塞了原来的消息循环，所以，在这个无限循环中要用GetMessage,PeekMessage,DispatchMessage来从消息队列中读取 消息并派送消息了。要不然程序就不会响应了，这不是我们所希望的。<br>所以说，消息循环放在程序的什么的地方都基本上是过的去的，比如放在DLL里 面。但是，最好在任何时候，只有一个消息循环在工作(其他的都被阻塞了)。然后，我们要作好的一件事情，就是怎么从消息循环中退出！当然用WM_QUIT 是可以拉~(PostThreadMessage也是个好主意)，这个消息循环退出后，可能程序退出，也可能会激活另外一个被阻塞的消息循环，程序继续运 行。这要看你怎么想，怎么去做。最后一个消息循环结束的时候，也许就是程序快结束的时候，因为主线程的执行代码也快要完了(除非BT的再作个死循环)。</p>
<p><font color=#0033ff>NOTE: 让windows系统知道创建一个线程的唯一方法是调用API CreatThread函数(__beginthreadex之类的都要在内部调用他创建新线程)。好像windows核心编程说，在win2000下， 系统用CreateRemoteThread函数来创建线程，CreateThread在内部调用CreateRemoteThread。不过这不是争论 的焦点，至少win98下CreateRemoteThread并不能正常工作，还是CreateThread主持大局。</font></p>
<p>3，在整个消息循环的机制中，还必须谈到窗口函数的可重入性。什么意思？就是窗口函数(他是个回调函数)的代码什么时候都可以被系统(调用者一般是user32模块)调用。比如在窗口过程中，向自己的窗口SendMessage(***);那么执行过程是怎样的？<br>我们知道，SendMessage是要等到消息发送并被目标窗口执行完之后才返回的。那么窗口在处理消息，然后又等待刚才发送到本窗口的消息被处理后之后(SendMessage返回)才继续往下执行，程序不就互相死锁了吗？ <br>其 实是不会的。windows设计一套适合SendMessage的算法，他判断如果发送的消息是属于本线程创建的窗口的，那么直接由user32模块调用 窗口函数(可能就有窗口重入)，并将消息的处理结果结果返回。这样做体现了窗口重入。上面的例子，我们调用SendMessage(***)发送消息到本 窗口，那么窗口过程再次被调用，处理完消息之后将结果返回，然后SendMessage之后的程序接着执行。对于非队列消息，如果没有窗口重入，不知道会 是什么样子。</p>
<p><font color=#0909f7>NOTE: 由于窗口的可重入性。在win32 SDK程序中应尽量少用全局变量和静态变量，因为在窗口函数执行过程中可能窗口重入，如果重入后将这些变量改了，但你的程序在窗口重入返回之后继续执行， 可能就是使用已经改变的全局或静态变量。在MFC中(所有窗口的窗口函数基本上都是AfxWndProc)，按照类的思想进行了组织，一般变量都是类中 的，好管理的多。</font></p>
<p>4,MFC中窗口类(比如C**View,CFrameWnd等)中的MessageBox函数，以及 AfxMessageBox函数都是阻塞原有的消息循环的。由消息框内部的一个消息循环来从消息队列中读取消息，并派送消息(和模式对话框类似)。实际 上，这些消息函数最终调用的是::MessageBox，它在消息框内部实现了一个消息循环(原有的主程序消息循环被阻塞了)。论坛中碰到过几次关于计时 器和消息框的问题，看下面的代码：<br>void CTest_recalclayoutView::OnTimer(UINT nIDEvent) <br>{<br>// TODO: Add your message handler code here and/or call default<br>MessageBox("abc");<br>while(1); <font color=#0909f7>//设计一个死循环<br></font>CView::OnTimer(nIDEvent);<br>}<br>咱 让OnTimer大约5秒钟弹出一个消息框。那么，消息框不断的被弹出来，只要消息框不被关闭，那么程序就不会进入死循环。实际上，每次弹出对话框，都是 最上层的那个消息框掌握着消息循环，其他的消息循环被阻塞了。只要不关闭最上面的消息框，while(1);就得不到执行。如果点了关闭，程序就进入了死 循环，只能用ctrl+alt+del来解决问题了。</p>
<p>5，消息循环在很多地方都有应用。比如应用在线程池中。一个线程的执行周期一般在线程 函数返回之后结束，那么怎么延长线程的生命周期呢？一种方法就是按照消息循环的思想，在线程中加入消息循环，不断地从线程队列读取消息，并处理消息，线程 的生命周期就保持着直到这个消息循环的退出。</p>
<p><font color=#3300ff>NOTE：只要线程有界面元素或者调用GetMessage,或者有线程消息发送过来，系统就会为线程创建一个消息队列。</font></p>
<p>&nbsp;</p>
<p>6, 在单线程程序中，如果要执行一个长时间的复杂操作而且界面要有相应的话，可以考虑用自己的消息泵。比如，可以将一个阻塞等待操作放在一个循环中，并将超时 值设置得比较小，然后每个等待的片段中用消息泵继续消息循环，使界面能够响应用户操作。等等之类，都可以应用消息泵(调用一个类似这样的函数)：<br>BOOL CChildView::PeekAndPump()<br>{<br>MSG msg;<br>while(::PeekMessage(&amp;msg,NULL,0,0,PM_NOREMOVE))<br>{<br>&nbsp;&nbsp; if(!AfxGetApp()-&gt;PumpMessage())<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; ::PostQuitMessage(0);<br>&nbsp;&nbsp;&nbsp; return false;<br>&nbsp;&nbsp; }<br>} <br>return true;<br>}<br>其实，用多线程也能解决复杂运算时的界面问题，但是没有这么方便，而且一般要加入线程通信和同步，考虑的事情更多一点。</p>
<p>&nbsp;</p>
<p>综上所述，MFC消息循环就那么回事，主要思想还是和SDK中差不多。这种思想主要的特点表现在迎合MFC整个框架上，为整个框架服务，为应用和功能服务。这是我的理解。呵呵~</p>
<img src ="http://www.cppblog.com/lilac/aggbug/38782.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lilac/" target="_blank">李亚</a> 2007-12-17 20:28 <a href="http://www.cppblog.com/lilac/archive/2007/12/17/38782.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>添加上下文菜单的方法 </title><link>http://www.cppblog.com/lilac/archive/2007/12/05/37878.html</link><dc:creator>李亚</dc:creator><author>李亚</author><pubDate>Wed, 05 Dec 2007 12:19:00 GMT</pubDate><guid>http://www.cppblog.com/lilac/archive/2007/12/05/37878.html</guid><wfw:comment>http://www.cppblog.com/lilac/comments/37878.html</wfw:comment><comments>http://www.cppblog.com/lilac/archive/2007/12/05/37878.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lilac/comments/commentRss/37878.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lilac/services/trackbacks/37878.html</trackback:ping><description><![CDATA[<div class=postText>首先要在在文件首定义菜单项：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">#define&nbsp;ID_MENU_EDIT&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">5001</span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>#define&nbsp;ID_MENU_DELETE&nbsp;</span><span style="COLOR: #000000">5002</span></div>
然后添加对话框的WM_CONTEXTMENU消息函数，函数内容为：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;CMenu&nbsp;menuPopup;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(menuPopup.CreatePopupMenu())<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;menuPopup.AppendMenu(MF_STRING,ID_MENU_EDIT,</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">修改(&amp;E)</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;menuPopup.AppendMenu(MF_STRING,ID_MENU_DELETE,</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">删除(&amp;D)</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;menuPopup.TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this);&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
然后定义菜单相应函数,
<p>1,在头文件中添加函数定义语句：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">//</span><span style="COLOR: #000000">&nbsp;Generated&nbsp;message&nbsp;map&nbsp;functions<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;</span><span style="COLOR: #000000">//</span><span style="COLOR: #000000">{{AFX_MSG(CAdo2Dlg)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;virtual&nbsp;BOOL&nbsp;OnInitDialog();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;afx_msg&nbsp;void&nbsp;onInfoEdit();&nbsp;</span><span style="COLOR: #000000">//</span><span style="COLOR: #000000">&nbsp;&nbsp;这个是编辑菜单的响应函数<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;afx_msg&nbsp;void&nbsp;onInfoDelete();&nbsp;&nbsp;</span><span style="COLOR: #000000">//</span><span style="COLOR: #000000">这个是删除菜单的响应函数<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;afx_msg&nbsp;void&nbsp;OnSysCommand(UINT&nbsp;nID,&nbsp;LPARAM&nbsp;lParam);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;afx_msg&nbsp;void&nbsp;OnPaint();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;afx_msg&nbsp;HCURSOR&nbsp;OnQueryDragIcon();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;afx_msg&nbsp;void&nbsp;OnButton1();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;afx_msg&nbsp;void&nbsp;OnButton2();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;afx_msg&nbsp;void&nbsp;OnRdblclkList1(NMHDR</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pNMHDR,&nbsp;LRESULT</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pResult);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;afx_msg&nbsp;void&nbsp;OnDblclkList1(NMHDR</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pNMHDR,&nbsp;LRESULT</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pResult);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;afx_msg&nbsp;void&nbsp;OnContextMenu(CWnd</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pWnd,&nbsp;CPoint&nbsp;point);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;</span><span style="COLOR: #000000">//</span><span style="COLOR: #000000">}}AFX_MSG<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;DECLARE_MESSAGE_MAP()</span></div>
<p>&nbsp;</p>
<p>2，在cpp文件中添加函数体：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">void&nbsp;CAdo2Dlg::OnInfoEdit()&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;AfxMessageBox(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">edit</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>}<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>void&nbsp;CAdo2Dlg::OnInfoDelete()&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;AfxMessageBox(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">delete</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>}</span></div>
<p>&nbsp;</p>
<p>3，然后在cpp文件中添加影射：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">BEGIN_MESSAGE_MAP(CAdo2Dlg,&nbsp;CDialog)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">//</span><span style="COLOR: #000000">{{AFX_MSG_MAP(CAdo2Dlg)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;ON_COMMAND(ID_MENU_EDIT,&nbsp;&nbsp;&nbsp;&nbsp;OnInfoEdit)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;ON_COMMAND(ID_MENU_DELETE,&nbsp;&nbsp;OnInfoDelete)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">//</span><span style="COLOR: #000000">}}AFX_MSG_MAP<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>END_MESSAGE_MAP()</span></div>
<p>&nbsp;</p>
<p>所有的工作完成了！</p>
</div>
<img src ="http://www.cppblog.com/lilac/aggbug/37878.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lilac/" target="_blank">李亚</a> 2007-12-05 20:19 <a href="http://www.cppblog.com/lilac/archive/2007/12/05/37878.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC动态链接库的创建和使用 </title><link>http://www.cppblog.com/lilac/archive/2007/12/05/37877.html</link><dc:creator>李亚</dc:creator><author>李亚</author><pubDate>Wed, 05 Dec 2007 12:18:00 GMT</pubDate><guid>http://www.cppblog.com/lilac/archive/2007/12/05/37877.html</guid><wfw:comment>http://www.cppblog.com/lilac/comments/37877.html</wfw:comment><comments>http://www.cppblog.com/lilac/archive/2007/12/05/37877.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lilac/comments/commentRss/37877.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lilac/services/trackbacks/37877.html</trackback:ping><description><![CDATA[<strong>一，MFC扩展DLL<br></strong>创建：<br>1，新建一个MFC扩展DLL ,名字为dll5,添加头文件，名为dll5<br>2，头文件中加入：<br>extern __declspec(dllexport) CString concatA(CString x,CString y);<br>3，在cpp文件中加入：<br>extern __declspec(dllexport) CString concatA(CString x,CString y)<br>{<br>&nbsp;return x + y;<br>}<br>4,在cpp文件中加入：<br>#include "dll5.h"
<p>5，编译，生成dll<br>使用：<br>1，新建一个单文档应用程序，名为Usedll5<br>2,将刚才生成的dll5.lib文件和dll5.h文件拷贝到当前应用程序路径下,<br>&nbsp; 将dll5.dll 文件拷贝到 当前应用程序下的debug下<br>3，在当前应用程序中用到该dll5的导出方法（concatA)的文件（或类）上添加如下语句：<br>#include "dll5.h"<br>假设将其加到 Usedll5View.cpp中。<br>4，在Usedll5View类中建立消息映射入口，在消息函数中添加如下语句：<br>CString a=concatA("中国北车集团","长春轨道客车股份有限公司");<br>MessageBox(a);<br>5，在 工程/设置/连接/对象库/模块 中加入：dll5.lib</p>
<p>6,编译执行该应用程序，并触发该消息，则输出：</p>
<p>中国北车集团长春轨道客车股份有限公司</p>
<p>之后只要定义不更改，函数体无论怎么更改。我们只要将编译好的dll拷贝过来即可。如果定义有了修改，则需要将h文件和lib 文件拷贝过来，并需要重新编译。</p>
<p>&nbsp; </p>
<p><strong><font size=4>二，动态链接库使用共享MFC DLL</font></strong><br>创建：<br>1，新建一个 DLL(选 动态链接库使用共享MFC DLL） <br>2，头文件中加入：<br>_declspec(dllexport) CString WINAPI concatA(CString x,CString y);<br>3，在cpp文件末尾加入：<br>_declspec(dllexport) CString WINAPI concatA(CString x,CString y)<br>{<br>&nbsp;return x + y;<br>}<br>4，编译，生成dll<br>使用：<br>1，新建一个单文档应用程序，名为Usedll8<br>2,将刚才生成的dll8.lib文件拷贝到当前应用程序路径下,<br>&nbsp; 将dll8.dll 文件拷贝到 c:\winnt\system32下<br>3，在当前应用程序中用到该dll5的导出方法（concatA)的 类的头文件上添加如下语句：<br>extern CString WINAPI concatA(CString x,CString y);<br>假设将其加到 Usedll8View.h中。<br>4，在Usedll8View类中建立消息映射入口，在消息函数中添加如下语句：<br>CString a=concatA("中国北车集团","长春轨道客车股份有限公司");<br>MessageBox(a);<br>5，在 工程/设置/连接/对象库/模块 中加入：dll8.lib<br>6,编译执行该应用程序，并触发该消息，则输出：</p>
<p>中国北车集团长春轨道客车股份有限公司</p>
<img src ="http://www.cppblog.com/lilac/aggbug/37877.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lilac/" target="_blank">李亚</a> 2007-12-05 20:18 <a href="http://www.cppblog.com/lilac/archive/2007/12/05/37877.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>知识点整理集合...</title><link>http://www.cppblog.com/lilac/archive/2007/11/25/37268.html</link><dc:creator>李亚</dc:creator><author>李亚</author><pubDate>Sun, 25 Nov 2007 03:55:00 GMT</pubDate><guid>http://www.cppblog.com/lilac/archive/2007/11/25/37268.html</guid><description><![CDATA[<strong>UpdateData()的使用方法</strong>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UpdateData（）只有一个BOOL类型的参数，UpdateData(FALSE)一般用于对话框控件连接的变量值刷新屏幕显示；比如你在一个文本框上绑定了一个m_member变量，用UpdateData(FALSE);即可把这个值在文本框里显示出来，反之，UpdateData(TRUE);能把填入文本框的 内容赋值给m_member.</p>
<img src ="http://www.cppblog.com/lilac/aggbug/37268.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lilac/" target="_blank">李亚</a> 2007-11-25 11:55 <a href="http://www.cppblog.com/lilac/archive/2007/11/25/37268.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>透明的CheckBox的代码</title><link>http://www.cppblog.com/lilac/archive/2007/11/23/37201.html</link><dc:creator>李亚</dc:creator><author>李亚</author><pubDate>Fri, 23 Nov 2007 05:21:00 GMT</pubDate><guid>http://www.cppblog.com/lilac/archive/2007/11/23/37201.html</guid><wfw:comment>http://www.cppblog.com/lilac/comments/37201.html</wfw:comment><comments>http://www.cppblog.com/lilac/archive/2007/11/23/37201.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lilac/comments/commentRss/37201.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lilac/services/trackbacks/37201.html</trackback:ping><description><![CDATA[<p>透明的CheckBox，基类是CButton...<br>主要代码如下<br>OnPaint() <br>{<br>&nbsp;CPaintDC dc(this); // device context for painting<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; // TODO: Add your message handler code here<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; CRect rect;<br>&nbsp;&nbsp;&nbsp; GetClientRect(&amp;rect);</p>
<p>&nbsp;&nbsp;&nbsp; CRect BoxRect;<br>&nbsp;&nbsp;&nbsp; BoxRect=rect;<br>&nbsp;&nbsp;&nbsp; BoxRect.right =BoxRect.left +15;<br>&nbsp;&nbsp;&nbsp; dc.DrawFrameControl(BoxRect,DFC_BUTTON,DFCS_BUTTONCHECK|GetCheck()?DFCS_CHECKED :0);</p>
<p>&nbsp;&nbsp;&nbsp; CFont&nbsp;&nbsp; myFont;&nbsp; <br>&nbsp;&nbsp;&nbsp; myFont.CreatePointFont (120,_T("宋体"));<br>&nbsp;&nbsp;&nbsp; CFont&nbsp;&nbsp; *pOldFont=(CFont&nbsp;&nbsp; *)dc.SelectObject&nbsp;&nbsp; (&amp;myFont);&nbsp; <br>&nbsp;&nbsp;&nbsp; dc.SetBkMode(TRANSPARENT);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; CString StrWndText;<br>&nbsp;&nbsp;&nbsp; GetWindowText(StrWndText);</p>
<p>&nbsp;&nbsp;&nbsp; rect.OffsetRect (20,0);<br>&nbsp;&nbsp;&nbsp; dc.DrawText(StrWndText,&nbsp;&nbsp; rect,&nbsp;&nbsp; DT_LEFT|DT_VCENTER|DT_SINGLELINE);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; dc.SelectObject&nbsp;&nbsp; (pOldFont);&nbsp; <br>&nbsp;&nbsp;&nbsp; myFont.DeleteObject&nbsp;&nbsp; ();&nbsp; <br>&nbsp;&nbsp;&nbsp; // Do not call CButton::OnPaint() for painting messages<br>}</p>
<img src ="http://www.cppblog.com/lilac/aggbug/37201.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lilac/" target="_blank">李亚</a> 2007-11-23 13:21 <a href="http://www.cppblog.com/lilac/archive/2007/11/23/37201.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>