??xml version="1.0" encoding="utf-8" standalone="yes"?>无码人妻久久一区二区三区免费丨 ,成人亚洲欧美久久久久,一本久久a久久精品综合夜夜http://www.shnenglu.com/lilac/category/5636.html?成ؓ.拥有.zh-cnWed, 21 May 2008 02:39:10 GMTWed, 21 May 2008 02:39:10 GMT60[转]常用数据cd的用与转换http://www.shnenglu.com/lilac/archive/2008/01/12/41062.html李亚李亚Sat, 12 Jan 2008 15:07:00 GMThttp://www.shnenglu.com/lilac/archive/2008/01/12/41062.htmlhttp://www.shnenglu.com/lilac/comments/41062.htmlhttp://www.shnenglu.com/lilac/archive/2008/01/12/41062.html#Feedback0http://www.shnenglu.com/lilac/comments/commentRss/41062.htmlhttp://www.shnenglu.com/lilac/services/trackbacks/41062.html我们先定义一些常见类型变量借以说明

int i = 100;
long l = 2001;
float f=300.2;
double d=12345.119;
char username[]="E佩?;
char temp[200];
char *buf;
CString str;
_variant_t v1;
_bstr_t v2;

一、其它数据类型{换ؓ字符?/strong>

  • 短整?int)
    itoa(i,temp,10);///i转换为字W串攑օtemp?最后一个数字表C十q制
    itoa(i,temp,2); ///按二q制方式转换
  • 长整?long)
    ltoa(l,temp,10);
  • 点?float,double)
    用fcvt可以完成转换,q是MSDN中的例子:
    int decimal, sign;
    char *buffer;
    double source = 3.1415926535;
    buffer = _fcvt( source, 7, &decimal, &sign );
    q行l果:source: 3.1415926535 buffer: '31415927' decimal: 1 sign: 0
    decimal表示数点的位置,sign表示W号:0为正敎ͼ1?
  • CString变量
    str = "2008北京奥运";
    buf = (LPSTR)(LPCTSTR)str;
  • BSTR变量
    BSTR bstrValue = ::SysAllocString(L"E序?);
    char * buf = _com_util::ConvertBSTRToString(bstrValue);
    SysFreeString(bstrValue);
    AfxMessageBox(buf);
    delete(buf);
  • CComBSTR变量
    CComBSTR bstrVar("test");
    char *buf = _com_util::ConvertBSTRToString(bstrVar.m_str);
    AfxMessageBox(buf);
    delete(buf);
  • _bstr_t变量
    _bstr_tcd是对BSTR的封装,因ؓ已经重蝲?操作W,所以很Ҏ使用
    _bstr_t bstrVar("test");
    const char *buf = bstrVar;///不要修改buf中的内容
    AfxMessageBox(buf);

  • 通用Ҏ(针对非COM数据cd)
    用sprintf完成转换
    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);

二、字W串转换为其它数据类?/font>
strcpy(temp,"123");

  • 短整?int)
    i = atoi(temp);
  • 长整?long)
    l = atol(temp);
  • 点(double)
    d = atof(temp);
  • CString变量
    CString name = temp;
  • BSTR变量
    BSTR bstrValue = ::SysAllocString(L"E序?);
    ...///完成对bstrValue的?br>SysFreeString(bstrValue);
  • CComBSTR变量
    CComBSTRcd变量可以直接赋?br>CComBSTR bstrVar1("test");
    CComBSTR bstrVar2(temp);
  • _bstr_t变量
    _bstr_tcd的变量可以直接赋?br>_bstr_t bstrVar1("test");
    _bstr_t bstrVar2(temp);

三、其它数据类型{换到CString
使用CString的成员函数Format来{?例如:

  • 整数(int)
    str.Format("%d",i);
  • 点?float)
    str.Format("%f",i);
  • 字符串指?char *){已l被CString构造函数支持的数据cd可以直接赋?br>str = username;
  • 对于Format所不支持的数据cdQ可以通过上面所说的关于其它数据cd转化到char *的方法先转到char *Q然后赋值给CString变量?br>

四、BSTR、_bstr_t与CComBSTR

  • CComBSTR 是ATL对BSTR的封装,_bstr_t是C++对BSTR的封?BSTR?2位指?但ƈ不直接指向字串的~冲区?br>char *转换到BSTR可以q样:
    BSTR b=_com_util::ConvertStringToBSTR("数据");///使用前需要加上comutil.h和comsupp.lib
    SysFreeString(bstrValue);
    反之可以使用
    char *p=_com_util::ConvertBSTRToString(b);
    delete p;
    具体可以参考一Q二D落里的具体说明?br>
    CComBSTR与_bstr_t对大量的操作W进行了重蝲Q可以直接进?,!=,=={操作,所以用非常方ѝ?br>特别是_bstr_t,大家使用它?br>

五、VARIANT 、_variant_t ?COleVariant

  • VARIANT的结构可以参考头文gVC98\Include\OAIDL.H中关于结构体tagVARIANT的定义?br>对于VARIANT变量的赋|首先lvt成员赋|指明数据cdQ再对联合结构中相同数据cd的变量赋|举个例子Q?br>VARIANT va;
    int a=2001;
    va.vt=VT_I4;///指明整型数据
    va.lVal=a; ///赋?br>
    对于不马上赋值的VARIANTQ最好先用Void VariantInit(VARIANTARG FAR* pvarg);q行初始?其本质是vt讄为VT_EMPTY,下表我们列Dvt与常用数据的对应关系:

    Byte bVal; // VT_UI1.
    Short iVal; // VT_I2.
    long lVal; // VT_I4.
    float fltVal; // VT_R4.
    double dblVal; // VT_R8.
    VARIANT_BOOL boolVal; // VT_BOOL.
    SCODE scode; // VT_ERROR.
    CY cyVal; // VT_CY.
    DATE date; // VT_DATE.
    BSTR bstrVal; // VT_BSTR.
    DECIMAL FAR* pdecVal // VT_BYREF|VT_DECIMAL.
    IUnknown FAR* punkVal; // VT_UNKNOWN.
    IDispatch FAR* pdispVal; // VT_DISPATCH.
    SAFEARRAY FAR* parray; // VT_ARRAY|*.
    Byte FAR* pbVal; // VT_BYREF|VT_UI1.
    short FAR* piVal; // VT_BYREF|VT_I2.
    long FAR* plVal; // VT_BYREF|VT_I4.
    float FAR* pfltVal; // VT_BYREF|VT_R4.
    double FAR* pdblVal; // VT_BYREF|VT_R8.
    VARIANT_BOOL FAR* pboolVal; // VT_BYREF|VT_BOOL.
    SCODE FAR* pscode; // VT_BYREF|VT_ERROR.
    CY FAR* pcyVal; // VT_BYREF|VT_CY.
    DATE FAR* pdate; // VT_BYREF|VT_DATE.
    BSTR FAR* pbstrVal; // VT_BYREF|VT_BSTR.
    IUnknown FAR* FAR* ppunkVal; // VT_BYREF|VT_UNKNOWN.
    IDispatch FAR* FAR* ppdispVal; // VT_BYREF|VT_DISPATCH.
    SAFEARRAY FAR* FAR* pparray; // VT_ARRAY|*.
    VARIANT FAR* pvarVal; // VT_BYREF|VT_VARIANT.
    void FAR* byref; // Generic ByRef.
    char cVal; // VT_I1.
    unsigned short uiVal; // VT_UI2.
    unsigned long ulVal; // VT_UI4.
    int intVal; // VT_INT.
    unsigned int uintVal; // VT_UINT.
    char FAR * pcVal; // VT_BYREF|VT_I1.
    unsigned short FAR * puiVal; // VT_BYREF|VT_UI2.
    unsigned long FAR * pulVal; // VT_BYREF|VT_UI4.
    int FAR * pintVal; // VT_BYREF|VT_INT.
    unsigned int FAR * puintVal; //VT_BYREF|VT_UINT.

  • _variant_t是VARIANT的封装类Q其赋值可以用强制类型{换,其构造函C自动处理q些数据cd?br>使用旉加上#include <comdef.h>
    例如Q?br>long l=222;
    ing i=100;
    _variant_t lVal(l);
    lVal = (long)i;

  • COleVariant的用与_variant_t的方法基本一P请参考如下例子:
    COleVariant v3 = "字符?, v4 = (long)1999;
    CString str =(BSTR)v3.pbstrVal;
    long i = v4.lVal;

六、其它一些COM数据cd

  • ҎProgID得到CLSID
    HRESULT CLSIDFromProgID( LPCOLESTR lpszProgID,LPCLSID pclsid);
    CLSID clsid;
    CLSIDFromProgID( L"MAPI.Folder",&clsid);
  • ҎCLSID得到ProgID
    WINOLEAPI ProgIDFromCLSID( REFCLSID clsid,LPOLESTR * lplpszProgID);
    例如我们已经定义?CLSID_IApplication,下面的代码得到ProgID
    LPOLESTR pProgID = 0;
    ProgIDFromCLSID( CLSID_IApplication,&pProgID);
    ...///可以使用pProgID
    CoTaskMemFree(pProgID);//不要忘记释放

七、ANSI与Unicode
UnicodeUCؓ宽字W型字串,COM里用的都是Unicode字符丌Ӏ?/font>

  • ANSI转换到Unicode
    (1)通过Lq个宏来实现Q例? CLSIDFromProgID( L"MAPI.Folder",&clsid);
    (2)通过MultiByteToWideChar函数实现转换,例如:
    char *szProgID = "MAPI.Folder";
    WCHAR szWideProgID[128];
    CLSID clsid;
    long lLen = MultiByteToWideChar(CP_ACP,0,szProgID,strlen(szProgID),szWideProgID,sizeof(szWideProgID));
    szWideProgID[lLen] = '\0';
    (3)通过A2W宏来实现,例如:
    USES_CONVERSION;
    CLSIDFromProgID( A2W(szProgID),&clsid);
  • Unicode转换到ANSI
    (1)使用WideCharToMultiByte,例如:
    // 假设已经有了一个Unicode ?wszSomeString...
    char szANSIString [MAX_PATH];
    WideCharToMultiByte ( CP_ACP, WC_COMPOSITECHECK, wszSomeString, -1, szANSIString, sizeof(szANSIString), NULL, NULL );
    (2)使用W2A宏来实现,例如:
    USES_CONVERSION;
    pTemp=W2A(wszSomeString);

八、其?/strong>

  • Ҏ息的处理中我们经帔R要将WPARAM或LPARAM{?2位数据(DWORD)分解成两?6位数据(WORD),例如Q?br>LPARAM lParam;
    WORD loValue = LOWORD(lParam);///取低16?br>WORD hiValue = HIWORD(lParam);///取高16?br>
  • 对于16位的数据(WORD)我们可以用同LҎ分解成高低两?位数?BYTE),例如:
    WORD wValue;
    BYTE loValue = LOBYTE(wValue);///取低8?br>BYTE hiValue = HIBYTE(wValue);///取高8?br>
  • 两个16位数据(WORDQ合?2位数?DWORD,LRESULT,LPARAM,或WPARAM)
    LONG MAKELONG( WORD wLow, WORD wHigh );
    WPARAM MAKEWPARAM( WORD wLow, WORD wHigh );
    LPARAM MAKELPARAM( WORD wLow, WORD wHigh );
    LRESULT MAKELRESULT( WORD wLow, WORD wHigh );

  • 两个8位的数据(BYTE)合成16位的数据(WORD)
    WORD MAKEWORD( BYTE bLow, BYTE bHigh );

  • 从R(red),G(green),B(blue)三色得到COLORREFcd的颜色?br>COLORREF RGB( BYTE byRed,BYTE byGreen,BYTE byBlue );
    例如COLORREF bkcolor = RGB(0x22,0x98,0x34);


  • 从COLORREFcd的颜色值得到RGB三个颜色?br>BYTE Red = GetRValue(bkcolor); ///得到U颜?br>BYTE Green = GetGValue(bkcolor); ///得到lK?br>BYTE Blue = GetBValue(bkcolor); ///得到兰颜?/span>

九、注意事?/strong>
假如需要用到ConvertBSTRToString此类函数,需要加上头文gcomutil.h,q在setting中加入comsupp.lib或者直接加?pragma comment( lib, "comsupp.lib" )



李亚 2008-01-12 23:07 发表评论
]]>
CString与其他类型的转换----CString字符串{换箋 http://www.shnenglu.com/lilac/archive/2008/01/12/41060.html李亚李亚Sat, 12 Jan 2008 15:00:00 GMThttp://www.shnenglu.com/lilac/archive/2008/01/12/41060.htmlhttp://www.shnenglu.com/lilac/comments/41060.htmlhttp://www.shnenglu.com/lilac/archive/2008/01/12/41060.html#Feedback0http://www.shnenglu.com/lilac/comments/commentRss/41060.htmlhttp://www.shnenglu.com/lilac/services/trackbacks/41060.htmlq里只是介绍了很常见Ҏ,q有其他Ҏ也可以实现相关的功能!
1、字W串与数的{换:
atof(字符?>double,int,long),itoa(int->字符?,ltoa(long int->字符?
double->CString的方法:CString::Format("%d", &dX);
 
2、CString to char*
//l过cd强制转换Q可以将CStringcd转换成char*Q例如:
CString cStr = "Hello,world!";
char *zStr = (char*)(LPCTSTR)cStr;
当然Q这只是最快捷的一U方法。因为CString自带一个函数可以将CString对象转换成const char*Q也是LPCTSTR?br> 
3、char* to CString
//char*cd可以直接lCStringQ完成自动{换,例如Q?br>char *zStr = "Hello,world!";
CString cStr = zStr;
 
4、CString to LPCSTR 
//CString转换成LPCSTRQ需要获得CString的长度CString cStr=_T("Hello,world!");
int nLen = cStr.GetLength();
LPCSTR lpszBuf = cStr.GetBuffer(nLen);
 
5、CString to LPSTR
//q个和第4个技巧是一LQ例如:
CString cStr = _T("Hello,world!");
int nLen = str.GetLength();
LPSTR lpszBuf = str.GetBuffer(nLen);  

李亚 2008-01-12 23:00 发表评论
]]>
[转]CString 操作指南http://www.shnenglu.com/lilac/archive/2008/01/12/41059.html李亚李亚Sat, 12 Jan 2008 14:55:00 GMThttp://www.shnenglu.com/lilac/archive/2008/01/12/41059.htmlhttp://www.shnenglu.com/lilac/comments/41059.htmlhttp://www.shnenglu.com/lilac/archive/2008/01/12/41059.html#Feedback0http://www.shnenglu.com/lilac/comments/commentRss/41059.htmlhttp://www.shnenglu.com/lilac/services/trackbacks/41059.htmlCString 是一U很有用的数据类型。它们很大程度上化了MFC中的许多操作Q得MFC在做字符串操作的时候方便了很多。不怎样Q用CString有很多特D的技巧,特别是对于纯C背景下走出来的程序员来说有点难以学习。这文章就来讨些技巧?br>  使用CString可以让你对字W串的操作更加直截了当。这文章不是CString的完全手册,但囊括了大部分常见基本问题?/p>

q篇文章包括以下内容Q?
CString 对象的连?/span>
格式化字W串Q包?int 型{化ؓ CString Q?/span>
CString 型{化成 int ?/span>
CString 型和 char* cd的相互{?/span>
  1.char* 转化?CString
  2.CString 转化?char* 之一Q用LPCTSTR强制转化
  3.CString 转化?char* 之二Q用CString对象的GetBufferҎ
  4.CString 转化?char* 之三: 和控件的接口
  5.CString 型{化成 BSTR 型;

BSTR 型{化成 CString 型;
VARIANT 型{化成 CString 型;
载入字符串表资源Q?
CString 和时对象;

CString 的效率;
ȝ
下面我分别讨论?/p>

 1、CString 对象的连?/p>

  能体现出 CString cd方便性特点的一个方面就字符串的q接Q?CString cdQ你能很方便地连接两个字W串Q正如下面的例子Q?/p>

CString gray("Gray");CString cat("Cat");CString graycat = gray + cat;
要比用下面的Ҏ好得多:

char gray[] = "Gray";char cat[] = "Cat";char * graycat = malloc(strlen(gray) + strlen(cat) + 1);strcpy(graycat, gray);strcat(graycat, cat);
 2、格式化字符?/p>

  与其?sprintf() 函数?wsprintf() 函数来格式化一个字W串Q还不如?CString 对象的Format()ҎQ?/p>

CString s;s.Format(_T("The total is %d"), total);
  用这U方法的好处是你不用担心用来存放格式化后数据的缓冲区是否_大,q些工作由CStringcL你完成?br>  格式化是一U把其它不是字符串类型的数据转化为CStringcd的最常用技巧,比如Q把一个整数{化成CStringcdQ可用如下方法:

CString s;s.Format(_T("%d"), total);
  我LҎ的字W串使用_T()宏,q是Z让我的代码至有Unicode的意识,当然Q关于Unicode的话题不在这文章的讨论范围。_T()宏在8位字W环境下是如下定义的Q?/p>

#define _T(x) x // 非Unicode版本Qnon-Unicode versionQ?br>而在Unicode环境下是如下定义的:

#define _T(x) L##x // Unicode版本QUnicode versionQ?br>所以在Unicode环境下,它的效果q当于Q?/p>

s.Format(L"%d", total);
  如果你认Z的程序可能在Unicode的环境下q行Q那么开始在意用 Unicode ~码。比如说Q不要用 sizeof() 操作W来获得字符串的长度Q因为在Unicode环境下就会有2倍的误差。我们可以用一些方法来隐藏Unicode的一些细节,比如在我需要获得字W长度的时候,我会用一个叫做DIM的宏Q这个宏是在我的dim.h文g中定义的Q我会在我写的所有程序中都包含这个文Ӟ

#define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )
  q个宏不仅可以用来解决Unicode的字W串长度的问题,也可以用在编译时定义的表gQ它可以获得表格的项敎ͼ如下Q?/p>

class Whatever { ... };Whatever data[] = {   { ... },    ...   { ... },};for(int i = 0; i < DIM(data); i++) // 扫描表格L匚wV?br>  q里要提醒你的就是一定要注意那些在参C需要真实字节数的API函数调用Q如果你传递字W个数给它,它将不能正常工作。如下:
TCHAR data[20];lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!lstrcpyn(data, longstring, DIM(data) - 1); // RIGHTWriteFile(f, data, DIM(data), &bytesWritten, NULL); // WRONG!WriteFile(f, data, sizeof(data), &bytesWritten, NULL); // RIGHT
造成以上原因是因为lstrcpyn需要一个字W个C为参敎ͼ但是WriteFile却需要字节数作ؓ参数?br>同样需要注意的是有时候需要写出数据的所有内宏V如果你仅仅只想写出数据的真实长度,你可能会认ؓ你应该这样做Q?/p>

WriteFile(f, data, lstrlen(data), &bytesWritten, NULL); // WRONG
但是在Unicode环境下,它不会正常工作。正的做法应该是这P

WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &bytesWritten, NULL); // RIGHT
  因ؓWriteFile需要的是一个以字节为单位的长度。(可能有些Z?#8220;在非Unicode的环境下q行q行代码Q就意味着L在做一个多余的?操作Q这样不会降低程序的效率吗?”q种x是多余的Q你必须要了解编译器实际上做了什么,没有哪一个C或C++~译器会把这U无聊的?操作留在代码中。在Unicode环境下运行的时候,你也不必担心那个?操作会降低程序的效率Q记住,q只是一个左UM位的操作而已Q编译器也很乐意Z做这U替换。)
  使用_T宏ƈ不是意味着你已l创Z一个Unicode的程序,你只是创Z一个有Unicode意识的程序而已。如果你在默认的8-bit模式下编译你的程序的话,得到的将是一个普通的8-bit的应用程序(q里?-bit指的只是8位的字符~码Qƈ不是?位的计算机系l)Q当你在Unicode环境下编译你的程序时Q你才会得到一个Unicode的程序。记住,CString ?Unicode 环境下,里面包含的可都是16位的字符哦?/p>

 3、CString 型{化成 int ?/p>

  ?CString cd的数据{化成整数cd最单的Ҏ是使用标准的字W串到整数{换例E?br>  虽然通常你怀疑用_atoi()函数是一个好的选择Q它也很会是一个正的选择。如果你准备使用 Unicode 字符Q你应该用_ttoi()Q它?ANSI ~码pȝ中被~译成_atoi()Q而在 Unicode ~码pȝ中编译成_wtoi()。你也可以考虑使用_tcstoul()或者_tcstol()Q它们都能把字符串{化成Lq制的长整数Q如二进制、八q制、十q制或十六进ӞQ不同点在于前者{化后的数据是无符LQunsignedQ,而后者相反。看下面的例子:

CString hex = _T("FAB");CString decimal = _T("4011");ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));
 4、CString 型和 char* cd的相互{?/p>

  q是初学者?CString 时最常见的问题。有?C++ 的帮助,很多问题你不需要深入的去考虑它,直接拿来用就行了Q但是如果你不能深入了解它的q行机制Q又会有很多问题让你qhQ特别是有些看v来没有问题的代码Q却偏偏不能正常工作?br>比如Q你会奇怪ؓ什么不能写向下面这L代码呢:

CString graycat = "Gray" + "Cat";
或者这P

CString graycat("Gray" + "Cat");
  事实上,~译器将抱怨上面的q些试。ؓ什么呢Q因为针对CString ?LPCTSTR数据cd的各U各Ll合Q?#8220; +” q算W?被定义成一个重载操作符。而不是两?LPCTSTR 数据cdQ它是底层数据类型。你不能对基本数据(?int、char 或?char*Q类型重?C++ 的运符。你可以象下面这样做Q?/p>

CString graycat = CString("Gray") + CString("Cat");
或者这P

CString graycat = CString("Gray") + "Cat";
研究一番就会发玎ͼ“ +”L使用在至有一?CString 对象和一?LPCSTR 的场合?/p>

注意Q编写有 Unicode 意识的代码L一件好事,比如Q?/p>

CString graycat = CString(_T("Gray")) + _T("Cat");
q将使得你的代码可以直接UL?/p>

char* 转化?CString

  现在你有一?char* cd的数据,或者说一个字W串。怎么样创?CString 对象呢?q里有一些例子:

char * p = "This is a test";
或者象下面q样更具?Unicode 意识Q?/p>

TCHAR * p = _T("This is a test")
?/p>

LPTSTR p = _T("This is a test");
你可以用下面Q意一U写法:

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);
  用这些方法可以轻村ְ帔R字符串或指针转换?CString。需要注意的是,字符的赋值L被拷贝到 CString 对象中去的,所以你可以象下面这h作:

TCHAR * p = _T("Gray");CString s(p);p = _T("Cat");s += p;
l果字符串肯定是“GrayCat”?/p>

CString c还有几个其它的构造函敎ͼ但是q里我们不考虑它,如果你有兴趣可以自己查看相关文?/p>

事实上,CString cȝ构造函数比我展C的要复杂,比如Q?/p>

CString s = "This is a test";
  q是很草率的~码Q但是实际上它在 Unicode 环境下能~译通过。它在运行时调用构造函数的 MultiByteToWideChar 操作?8 位字W串转换?16 位字W串。不怎样Q如?char * 指针是网l上传输?8 位数据,q种转换是很有用的?/p>

CString 转化?char* 之一Q强制类型{换ؓ LPCTSTRQ?/p>

  q是一U略微硬性的转换Q有?#8220;正确”的做法,Z在认识上q存在许多؜乱,正确的用方法有很多Q但错误的用方法可能与正确的用方法一样多?br>  我们首先要了?CString 是一U很Ҏ?C++ 对象Q它里面包含了三个|一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字W记C及一个缓冲区长度?有效字符数的大小可以是从0到该~冲最大长度值减1之间的Q何数Q因为字W串l尾有一个NULL字符Q。字W记数和~冲区长度被巧妙隐藏?br>  除非你做一些特D的操作Q否则你不可能知道给CString对象分配的缓冲区的长度。这P即你获得了?~冲的地址Q你也无法更改其中的内容Q不能截短字W串Q也 l对没有办法加长它的内容Q否则第一旉׃看到溢出?br>  LPCTSTR 操作W(或者更明确地说是 TCHAR * 操作W)?CString cM被重载了Q该操作W的定义是返回缓冲区的地址Q因此,如果你需要一个指?CString ?字符串指针的话,可以q样做:


CString s("GrayCat");LPCTSTR p = s;
  它可以正地q行。这是由C语言的强制类型{化规则实现的。当需要强制类型{化时QC++规测容许q种选择。比如,你可以将QQҎQ定义ؓ某个复?Q有一ҎQҎQ进行强制类型{换后只返回该复数的第一个QҎQ也是其实部)。可以象下面q样Q?/p>

Complex c(1.2f, 4.8f);float realpart = c;
如果(float)操作W定义正的话,那么实部的的值应该是1.2?br>  q种强制转化适合所有这U情况,例如QQ何带?LPCTSTR cd参数的函数都会强制执行这U{换?于是Q你可能有这样一个函敎ͼ也许在某个你买来的DLL中)Q?/p>

BOOL DoSomethingCool(LPCTSTR s);
你象下面q样调用它:

CString file("c:\\myfiles\\coolstuff")BOOL result = DoSomethingCool(file);
  它能正确q行。因?DoSomethingCool 函数已经说明了需要一?LPCTSTR cd的参敎ͼ因此 LPCTSTR 被应用于该参敎ͼ?MFC 中就是返回的串地址?/p>

如果你要格式化字W串怎么办呢Q?/p>

CString graycat("GrayCat");CString s;s.Format("Mew! I love %s", graycat);
  注意׃在可变参数列表中的|在函数说明中是以“...”表示的)q没有隐含一个强制类型{换操作符。你会得C么结果呢Q?br>  一个o人惊讶的l果Q我们得到的实际l果串是Q?/p>

"Mew! I love GrayCat"?br>  因ؓ MFC 的设计者们在设?CString 数据cd旉常小心, CString cd表达式求值后指向了字W串Q所以这里看不到M?Format ?sprintf 中的强制cd转换Q你仍然可以得到正确的行为。描q?CString 的附加数据实际上?CString 名义地址之后?br>  有一件事情你是不能做的,那就是修改字W串。比如,你可能会试?#8220;,”代替“.”Q不要做q样的,如果你在乎国际化问题Q你应该使用十进制{换的 National Language Support Ҏ,Q,下面是个单的例子Q?/p>

CString v("1.00"); // 货币金额Q两位小数LPCTSTR p = v;p[lstrlen(p) - 3] = '','';
  q时~译器会报错Q因Z赋g一个常量串。如果你做如下尝试,~译器也会错Q?/p>

strcat(p, "each");
  因ؓ strcat 的第一个参数应该是 LPTSTR cd的数据,而你却给了一?LPCTSTR?/p>

  不要试图钻这个错误消息的牛角,q只会你自己陷入麻烦!

  原因是缓冲有一个计敎ͼ它是不可存取的(它位?CString 地址之下的一个隐藏区域)Q如果你改变q个Ԍ~冲中的字符计数不会反映所做的修改。此外,如果字符串长度恰好是该字W串物理限制的长度(梢后q会讲到q个问题Q,那么扩展该字W串改写缓冲以外的M数据Q那是你无权q行写操作的内存Q不对吗Q)Q你会毁换坏不属于你的内存。这是应用程序真正的M处方?/p>

CString转化成char* 之二Q?CString 对象?GetBuffer ҎQ?/p>

  如果你需要修?CString 中的内容Q它有一个特D的Ҏ可以使用Q那是 GetBufferQ它的作用是q回一个可写的~冲指针?如果你只是打修改字W或者截短字W串Q你完全可以q样做:

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();
  q是 GetBuffer 的第一U用法,也是最单的一U,不用l它传递参敎ͼ它用默认?0Q意思是Q?#8220;l我q个字符串的指针Q我保证不加长它”。当你调?ReleaseBuffer Ӟ字符串的实际长度会被重新计算Q然后存?CString 对象中?br>  必须一点,?GetBuffer ?ReleaseBuffer 之间q个范围Q一定不能用你要操作的q个~冲?CString 对象的Q何方法。因?ReleaseBuffer 被调用之前,?CString 对象的完整性得不到保障。研I以下代码:

CString s(...);LPTSTR p = s.GetBuffer();//... q个指针 p 发生了很多事情int n = s.GetLength(); // 很糟D!!!!! 有可能给出错误的{案!!!s.TrimRight(); // 很糟!!!!! 不能保证能正常工?!!!s.ReleaseBuffer(); // 现在应该 OKint m = s.GetLength(); // q个l果可以保证是正的。s.TrimRight(); // 正常工作?br>  假设你想增加字符串的长度Q你首先要知道这个字W串可能会有多长Q好比是声明字符串数l的时候用Q?/p>

char buffer[1024];
表示 1024 个字W空间以让你做M惛_得事情。在 CString 中与之意义相{的表示法:

LPTSTR p = s.GetBuffer(1024);
  调用q个函数后,你不仅获得了字符串缓冲区的指针,而且同时q获得了长度臛_?1024 个字W的I间Q注意,我说的是“字符”Q而不?#8220;字节”Q因?CString 是以隐含方式感知 Unicode 的)?br>  同时Q还应该注意的是Q如果你有一个常量串指针Q这个串本n的D存储在只d存中Q如果试囑֭储它Q即使你已经调用?GetBuffer Qƈ获得一个只d存的指针Q存入操作会p|Qƈ报告存取错误。我没有?CString 上证明这一点,但我看到q大把的 C E序员经常犯q个错误?br>  C E序员有一个通病是分配一个固定长度的~冲Q对它进?sprintf 操作Q然后将它赋值给一?CStringQ?/p>

char buffer[256];sprintf(buffer, "%......", args, ...); // ... 部分省略许多l节CString s = buffer;
虽然更好的Ş式可以这么做Q?/p>

CString s;s.Format(_T("%...."), args, ...);
如果你的字符串长度万一过 256 个字W的时候,不会破坏堆栈?/p>

  另外一个常见的错误是:既然固定大小的内存不工作Q那么就采用动态分配字节,q种做法弊端更大Q?/p>

int len = lstrlen(parm1) + 13  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;
它可以能被简单地写成Q?/p>

CString s;s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);
  需要注?sprintf 例子都不?Unicode qA的,管你可以?tsprintf 以及?_T() 来包围格式化字符Ԍ但是基本 思\仍然是在走弯路,q这样很Ҏ出错?/p>

CString to char * 之三Q和控g的接口;

  我们l常需要把一?CString 的g递给一个控Ӟ比如QCTreeCtrl。MFC为我们提供了很多便利来重载这个操作,但是 在大多数情况下,你?#8220;原始”形式的更斎ͼ因此需要将墨某个串指针存储?TVINSERTITEMSTRUCT l构?TVITEM 成员中。如下:

TVINSERTITEMSTRUCT tvi;CString s;// ... 为s赋一些倹{tvi.item.pszText = s; // Compiler yells at you here// ... 填写tvi的其他域HTREEITEM ti = c_MyTree.InsertItem(&tvi);
  Z么编译器会报错呢Q明明看h很完的用法啊!但是事实上如果你看看 TVITEM l构的定义你׃明白Q在 TVITEM l构?pszText 成员的声明如下:

LPTSTR pszText;int cchTextMax;
  因此Q赋g是赋l一?LPCTSTR cd的变量,而且~译器无法知道如何将赋D句右边强制{换成 LPCTSTR。好吧,你说Q那我就Ҏq样Q?/p>

tvi.item.pszText = (LPCTSTR)s; //~译器依然会报错?br>  ~译器之所以依然报错是因ؓ你试图把一?LPCTSTR cd的变量赋值给一?LPTSTR cd的变量,q种操作在C或C++中是被禁止的。你不能用这U方?来滥用常量指针与非常量指针概念,否则Q会Cؕ~译器的优化机制Q之不知如何优化你的程序。比如,如果你这么做Q?/p>

const int i = ...;//... do lots of stuff... = a[i]; // usage 1// ... lots more stuff... = a[i]; // usage 2
  那么Q编译器会以为既?i ?const Q所?usage1和usage2的值是相同的,q且它甚臌事先计算?usage1 处的 a[i] 的地址Q然后保留着在后面的 usage2 处用,而不是重新计。如果你按如下方式写的话Q?/p>

const int i = ...;int * p = &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
  ~译器将认ؓ i 是常量,从?a[i] 的位|也是常量,q样间接地破坏了先前的假设。因此,你的E序会?debug ~译模式Q没有优化)?release ~译模式Q完全优化)中反映出不同的行为,q种情况可不好,所以当你试图把指向 i 的指针赋值给一?可修改的引用Ӟ会被~译器诊断ؓq是一U伪造。这是Z么(LPCTSTRQ强制类型{化不起作用的原因?br>  Z么不把该成员声明?LPCTSTR cd呢?因ؓq个l构被用于读写控件。当你向控g写数据时Q文本指针实际上被当?LPCTSTRQ而当你从控gL?Ӟ你必L一个可写的字符丌Ӏ这个结构无法区分它是用来读q是用来写?/p>

因此Q你会常常在我的代码中看到如下的用法Q?/p>

tvi.item.pszText = (LPTSTR)(LPCTSTR)s;
  它把 CString 强制cd转化?LPCTSTRQ也是说先获得改字W串的地址Q然后再强制cd转化?LPTSTRQ以便可以对之进行赋值操作?注意q只有在使用 Set ?Insert 之类的方法才有效Q如果你试图获取数据Q则不能q么做?br>  如果你打获取存储在控g中的数据Q则ҎE有不同Q例如,Ҏ?CTreeCtrl 使用 GetItem ҎQ我惌取项目的文本。我知道q些 文本的长度不会超q?MY_LIMITQ因此我可以q样写:

TVITEM tvi;// ... assorted initialization of other fields of tvitvi.pszText = s.GetBuffer(MY_LIMIT);tvi.cchTextMax = MY_LIMIT;c_MyTree.GetItem(&tvi);s.ReleaseBuffer();
  可以看出来,其实上面的代码对所有类型的 Set Ҏ都适用Q但是ƈ不需要这么做Q因为所有的c?Set ҎQ包?InsertҎQ不会改变字W串的内宏V但是当你需要写 CString 对象Ӟ必须保证~冲是可写的Q这正是 GetBuffer 所做的事情。再ơ强调: 一旦做了一?GetBuffer 调用Q那么在调用 ReleaseBuffer 之前不要对这?CString 对象做Q何操作?/p>

 5、CString 型{化成 BSTR ?/p>

  当我们?ActiveX 控g~程Ӟl常需要用到将某个DC成 BSTR cd。BSTR 是一U记数字W串QIntelq_上的宽字W串QUnicodeQ,q且 可以包含嵌入?NULL 字符?/p>

你可以调?CString 对象?AllocSysString Ҏ?CString 转化?BSTRQ?/p>

CString s;s = ... ; // whateverBSTR b = s.AllocSysString();
  现在指针 b 指向的就是一个新分配?BSTR 对象Q该对象?CString 的一个拷贝,包含l结 NULL字符。现在你可以它传递给M需?BSTR 的接口。通常QBSTR 由接收它的组件来释放Q如果你需要自己释?BSTR 的话Q可以这么做Q?/p>

::SysFreeString(b);
  对于如何表示传递给 ActiveX 控g的字W串Q在微Y内部曾一度争Z休,最?Visual Basic 的h占了上风QBSTRQ?#8220;Basic String”的首字母~写Q就是这Z论的l果?/p>

 6、BSTR 型{化成 CString ?/p>

  ׃ BSTR 是记?Unicode 字符Ԍ你可以用标准转换Ҏ来创?8 位的 CString。实际上Q这?CString 内徏的功能。在 CString ?有特D的构造函数可以把 ANSI 转化?UnicodeQ也可以把Unicode 转化?ANSI。你同样可以?VARIANT cd的变量中获得 BSTR cd的字W串QVARIANT cd?由各U?COM ?Automation (自动?调用q回的类型?/p>

例如Q在一个ANSIE序中:

BSTR b;b = ...; // whateverCString s(b == NULL ? L"" : b)
  对于单个?BSTR 串来_q种用法可以工作得很好,q是因ؓ CString 有一个特D的构造函CLPCWSTRQBSTR正是q种cdQ?为参敎ͼq将它{化成 ANSI cd。专门检查是必须的,因ؓ BSTR 可能为空|?CString 的构造函数对?NULL 值情况考虑的不是很周到Q(感谢 Brian Ross 指出q一?Q。这U用法也只能处理包含 NUL l结字符的单字符Ԍ如果要{化含有多?NULL 字符 Ԍ你得额外做一些工作才行。在 CString 中内嵌的 NULL 字符通常表现不尽如h意,应该量避免?br>  Ҏ C/C++ 规则Q如果你有一?LPWSTRQ那么它别无选择Q只能和 LPCWSTR 参数匚w?/p>

?Unicode 模式下,它的构造函数是Q?/p>

CString::CString(LPCTSTR);
正如上面所表示的,?ANSI 模式下,它有一个特D的构造函敎ͼ

CString::CString(LPCWSTR);
  它会调用一个内部的函数?Unicode 字符串{换成 ANSI 字符丌Ӏ(在Unicode模式下,有一个专门的构造函敎ͼ该函数有一个参数是LPCSTRcd——一??ANSI 字符?指针Q该函数它加宽?Unicode 的字W串Q)再次Q一定要?BSTR 的值是否ؓ NULL?br>  另外q有一个问题,正如上文提到的:BSTRs可以含有多个内嵌的NULL字符Q但?CString 的构造函数只能处理某个串中单?NULL 字符?也就是说Q如果串中含有嵌入的 NUL字节QCString 会计算出错误的串长度。你必须自己处理它。如果你看看 strcore.cpp 中的构造函敎ͼ你会发现 它们都调用了lstrlenQ也是计算字符串的长度?br>  注意?Unicode ?ANSI 的{换用带专门参数?::WideCharToMultiByteQ如果你不想使用q种默认的{换方式,则必ȝ写自q转化代码?br>  如果你在 UNICODE 模式下编译代码,你可以简单地写成Q?/p>


CString convert(BSTR b){    if(b == NULL)        return CString(_T(""));    CString s(b); // in UNICODE mode    return s;}
  如果?ANSI 模式Q则需要更复杂的过E来转换。注意这个代码用与 ::WideCharToMultiByte 相同的参数倹{所以你 只能在想要改变这些参数进行{换时使用该技术。例如,指定不同的默认字W,不同的标志集{?
CString convert(BSTR b){    CString s;    if(b == NULL)       return s; // empty for NULL BSTR#ifdef UNICODE    s = b;#else    LPSTR p = s.GetBuffer(SysStringLen(b) + 1);     ::WideCharToMultiByte(CP_ACP,            // ANSI Code Page                          0,                 // no flags                          b,                 // source widechar string                          -1,                // assume NUL-terminated                          p,                 // target buffer                          SysStringLen(b)+1, // target buffer length                          NULL,              // use system default char                          NULL);             // don''t care if default used    s.ReleaseBuffer();#endif    return s;}
  我ƈ不担心如?BSTR 包含没有映射?8 位字W集?Unicode 字符时会发生什么,因ؓ我指定了::WideCharToMultiByte 的最后两个参Cؓ NULL。这是你可能需要改变的地方?

 7、VARIANT 型{化成 CString ?/p>

  事实上,我从来没有这么做q,因ؓ我没有用 COM/OLE/ActiveX ~写q程序。但是我在microsoft.public.vc.mfc 新闻l上看到?Robert Quirk 的一帖子谈Cq种转化Q我觉得把他的文章包含在我的文章里是不太好的做法Q所以在q里多做一些解释和演示。如果和他的文章有相孛的地方可能是我的疏忽?br>  VARIANT cdl常用来l?COM 对象传递参敎ͼ或者接收从 COM 对象q回的倹{你也能自己~写q回 VARIANT cd的方法,函数q回什么类?依赖可能Qƈ且常常)Ҏ的输入参敎ͼ比如Q在自动化操作中Q依赖与你调用哪个方法。IDispatch::Invoke 可能q回Q通过其一个参敎ͼ一?包含有BYTE、WORD、float、double、date、BSTR {等 VARIANT cd的结果,Q详?MSDN 上的 VARIANT l构的定义)。在下面的例子中Q假?cd是一个BSTR的变体,也就是说在串中的值是通过 bsrtVal 来引用,其优Ҏ?ANSI 应用中,有一个构造函C?LPCWCHAR 引用的D{换ؓ一?CStringQ见 BSTR-to-CString 部分Q。在 Unicode 模式中,成为标准的 CString 构造函敎ͼ参见对缺?:WideCharToMultiByte 转换的告诫,以及你觉得是否可以接受(大多数情况下Q你会满意的Q?br>VARIANT vaData;vaData = m_com.YourMethodHere();ASSERT(vaData.vt == VT_BSTR);CString strData(vaData.bstrVal);
你还可以Ҏ vt 域的不同来徏立更通用的{换例E。ؓ此你可能会考虑Q?/p>


CString VariantToString(VARIANT * va){    CString s;    switch(va->vt)      { /* vt */       case VT_BSTR:          return CString(vaData->bstrVal);       case VT_BSTR | VT_BYREF:          return CString(*vaData->pbstrVal);       case VT_I4:          s.Format(_T("%d"), va->lVal);          return s;       case VT_I4 | VT_BYREF:          s.Format(_T("%d"), *va->plVal);       case VT_R8:          s.Format(_T("%f"), va->dblVal);          return s;       ... 剩下的类型{换由读者自己完?nbsp;      default:          ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)          return CString("");      } /* vt */}
 8、蝲入字W串表资?/p>

  如果你想创徏一个容易进行语a版本UL的应用程序,你就不能在你的源代码中直接包含本土语a字符?Q下面这些例子我用的语言都是pQ因为我的本土语是英语)Q比如下面这U写法就很糟Q?br>CString s = "There is an error";
  你应该把你所有特定语a的字W串单独摆放Q调试信息、在发布版本中不出现的信息除外)。这意味着向下面这样写比较好:

s.Format(_T("%d - %s"), code, text);
  在你的程序中Q文字字W串不是语言敏感的。不怎样Q你必须很小心,不要使用下面q样的串Q?/p>

// fmt is "Error in %s file %s"http:// readorwrite is "reading" or "writing"s.Format(fmt, readorwrite, filename);
  q是我的切n体会。在我的W一个国际化的应用程序中我犯了这个错误,管我懂徯Q知道在徯的语法中动词攑֜句子的最后面Q我们的德国斚w的发行hq是苦苦的抱怨他们不得不提取那些不可思议的d语错误提CZ息然后重新格式化以让它们能正常工作。比较好的办法(也是我现在用的办法Q是使用两个字符Ԍ一个用 于读Q一个用于写Q在使用时加载合适的版本Q得它们对字符串参数是非敏感的。也是说加载整个格式,而不是加载串“reading”Q?#8220;writing”Q?/p>

// fmt is "Error in reading file %s"http:// "Error in writing file %s"s.Format(fmt, filename);
  一定要注意Q如果你有好几个地方需要替换,你一定要保证替换后句子的l构不会出现问题Q比如在p中,可以是主?宾语Q主?谓语Q动?宾语的结构等{?br>  在这里,我们q不讨论 FormatMessageQ其实它?sprintf/Format q要有优势,但是不太Ҏ和CString l合使用。解册U问题的办法是我们按照参数出现在参数表中的位置l参数取名字Q这样在你输出的时候就不会把他们的位置排错了?br>  接下来我们讨论我们这些独立的字符串放在什么地斏V我们可以把字符串的值放入资源文件中的一个称?STRINGTABLE 的段中。过E如下:首先使用 Visual Studio 的资源编辑器创徏一个字W串Q然后给每一个字W串取一个IDQ一般我们给它取名字都以 IDS_开头。所以如果你有一个信息,你可以创Z个字W串资源然后取名?IDS_READING_FILEQ另外一个就取名?IDS_WRITING_FILE。它们以下面的Ş式出现在你的 .rc 文g中:

STRINGTABLEIDS_READING_FILE "Reading file %s"IDS_WRITING_FILE "Writing file %s"END
注意Q这些资源都?Unicode 的格式保存,不管你是在什么环境下~译。他们在Win9xpȝ上也是以Unicode 的Ş式存在,虽然 Win9x 不能真正处理 Unicode?br>然后你可以这样用这些资源:
// 在用资源串表之前,E序是这样写的:


   CString fmt;      if(...)        fmt = "Reading file %s";     else       fmt = "Writing file %s";  ...    // much later  CString s;  s.Format(fmt, filename);
// 使用资源串表之后Q程序这样写Q?
    CString fmt;        if(...)           fmt.LoadString(IDS_READING_FILE);        else           fmt.LoadString(DS_WRITING_FILE);    ...      // much later    CString s;    s.Format(fmt, filename);
  现在Q你的代码可以移植到M语言中去。LoadString Ҏ需要一个字W串资源?ID 作ؓ参数Q然后它?STRINGTABLE 中取出它对应的字W串Q赋值给 CString 对象?CString 对象的构造函数还有一个更加聪明的特征可以?STRINGTABLE 的用。这个用法在 CString::CString 的文中没有指出Q但是在 构造函数的CZE序中用了。(Z么这个特性没有成为正式文的一部分Q而是攑֜了一个例子中Q我C得了Q)——【译者注Q从q句话看Q作者可能是CString的设计者。其实前面还有一句类似的话。说他没有对使用GetBuffer(0)获得的指针指向的地址是否可读做有效性检?】。这个特征就是:如果你将一个字W串资源的ID强制cd转换?LPCTSTRQ将会隐含调?LoadString。因此,下面两个构造字W串的例子具有相同的效果Q而且?ASSERT 在debug模式下不会被触发Q?br>CString s;s.LoadString(IDS_WHATEVER);CString t( (LPCTSTR)IDS_WHATEVER );ASSERT(s == t);//不会被触发,说明s和t是相同的?br>  现在Q你可能会想Q这怎么可能工作呢?我们怎么能把 STRINGTABLE ID 转化成一个指针呢Q很单:所有的字符?ID 都在1~65535q个范围内,也就是说Q它所有的高位都是0Q而我们在E序中所使用的指针是不可能小?5535的,因ؓE序的低 64K 内存永远也不可能存在的,如果你试图访?x00000000?x0000FFFF之间的内存,会引发一个内存越界错误。所以说1~65535的g可能是一个内存地址Q所以我们可以用q些值来作ؓ字符串资源的ID?br>  我們֐于?MAKEINTRESOURCE 宏显式地做这U{换。我认ؓq样可以让代码更加易于阅诅R这是个只适合?MFC 中用的标准宏。你要记住,大多数的Ҏ卛_以接受一?UINT 型的参数Q也可以接受一?LPCTSTR 型的参数Q这是依?C++ 的重载功能做到的。C++重蝲函数带来?弊端是造成所有的强制cd转化都需要显C声明。同P你也可以l很多种l构只传递一个资源名?/p>

CString s;s.LoadString(IDS_WHATEVER);CString t( MAKEINTRESOURCE(IDS_WHATEVER));ASSERT(s == t);
  告诉你吧Q我不仅只是在这里鼓吹,事实上我也是q么做的。在我的代码中,你几乎不可能扑ֈ一个字W串Q当Ӟ那些只是偶然在调试中出现的或者和语言无关的字W串除外?/p>

 9、CString 和时对?/p>

  q是出现?microsoft.public.vc.mfc 新闻l中的一个小问题Q我单的提一下,q个问题是有个程序员需要往注册表中写入一个字W串Q他写道Q?br>  我试着?RegSetValueEx() 讄一个注册表键的|但是它的l果L令我困惑。当我用char[]声明一个变量时它能正常工作Q但是当我用 CString 的时候,L得到一些垃圾:"&Yacute;&Yacute;&Yacute;&Yacute;...&Yacute;&Yacute;&Yacute;&Yacute;&Yacute;&Yacute;"Z认是不是我?CString 数据Z问题Q我试着?GetBufferQ然后强制{化成 char*QLPCSTR。GetBuffer q回的值是正确的,但是当我把它赋值给 char* Ӟ它就变成垃圾了。以下是我的E序D:

char* szName = GetName().GetBuffer(20);RegSetValueEx(hKey, "Name", 0, REG_SZ,              (CONST BYTE *) szName,             strlen (szName + 1));
q个 Name 字符串的长度于 20Q所以我不认为是 GetBuffer 的参数的问题?/p>

真让人困惑,请帮帮我?/p>

亲爱?FrustratedQ?/p>

你犯了一个相当微妙的错误Q聪明反被聪明误Q正的代码应该象下面这P


CString Name = GetName();RegSetValueEx(hKey, _T("Name"), 0, REG_SZ,                     (CONST BYTE *) (LPCTSTR)Name,                    (Name.GetLength() + 1) * sizeof(TCHAR));
  Z么我写的代码能行而你写的有问题呢?主要是因为当你调?GetName 时返回的 CString 对象是一个时对象。参见:《C++ Reference manual?#167;12.2
  在一些环境中Q编译器有必要创Z个时对象,q样引入临时对象是依赖于实现的。如果编译器引入的这个时对象所属的cL构造函数的话,~译器要保q个cȝ构造函数被调用。同LQ如果这个类声明有析构函数的话,也要保证q个临时对象的析构函数被调用?br>  ~译器必M证这个时对象被销毁了。被销毁的切地点依赖于实?....q个析构函数必须在退出创临时对象的范围之前被调用?br>  大部分的~译器是q样设计的:在时对象被创徏的代码的下一个执行步骤处隐含调用q个临时对象的析构函敎ͼ实现hQ一般都是在下一个分号处。因此,q个 CString 对象?GetBuffer 调用之后p析构了(Z提一句,你没有理q GetBuffer 函数传递一个参敎ͼ而且没有使用ReleaseBuffer 也是不对的)。所?GetBuffer 本来q回的是指向q个临时对象中字W串的地址的指针,但是当这个时对象被析构后,q块内存p释放了。然?MFC 的调试内存分配器会重Cؓq块内存全部填上 0xDDQ显C出来刚好就?#8220;&Yacute;”W号。在q个时候你向注册表中写数据Q字W串的内容当然全被破坏了?br>  我们不应该立xq个临时对象转化?char* cdQ应该先把它保存C?CString 对象中,q意味着把时对象复制了一份,所以当临时?CString 对象被析构了之后Q这?CString 对象中的g然保存着。这个时候再向注册表中写数据没有问题了?br>  此外Q我的代码是h Unicode 意识的。那个操作注册表的函数需要一个字节大,使用lstrlen(Name+1) 得到的实际结果对?Unicode 字符来说?ANSI 字符要小一半,而且它也不能从这个字W串的第二个字符起开始计,也许你的本意?lstrlen(Name) + 1QOKQ我承认Q我也犯了同L错误Q)。不论如何,?Unicode 模式下,所有的字符都是2个字节大,我们需要处理这个问题。微软的文档令h惊讶地对此保持缄默:REG_SZ 的值究竟是以字节计还是以字符计算呢?我们假设它指的是以字节ؓ单位计算Q你需要对你的代码做一些修Ҏ计算q个字符串所含有的字节大?/p>

 10、CString 的效?/p>

  CString 的一个问题是它确实掩藏了一些低效率的东ѝ从另外一个方面讲Q它也确实可以被实现得更加高效,你可能会说下面的代码Q?br>CString s = SomeCString1;s += SomeCString2;s += SomeCString3;s += ",";s += SomeCString4;
比v下面的代码来Q效率要低多了:

char s[1024];lstrcpy(s, SomeString1);lstrcat(s, SomeString2);lstrcat(s, SomeString 3);lstrcat(s, ",");lstrcat(s, SomeString4);
  MQ你可能会想Q首先,它ؓ SomeCString1 分配一块内存,然后?SomeCString1 复制到里面,然后发现它要做一个连接,则重新分配一块新的够大的内存,大到能够放下当前的字W串加上SomeCString2Q把内容复制到这块内?Q然后把 SomeCString2 q接到后面,然后释放W一块内存,q把指针重新指向新内存。然后ؓ每个字符串重复这个过E。把q?4 个字W串q接h效率多低啊。事实上Q在很多情况下根本就不需要复制源字符Ԍ?+= 操作W左边的字符Ԍ?br>  ?VC++6.0 中,Release 模式下,所有的 CString 中的~存都是按预定义量子分配的。所谓量子,即确定ؓ 64?28?56 或?512 字节。这意味着除非字符串非帔RQ连接字W串的操作实际上是 strcat l过优化后的版本Q因为它知道本地的字W串应该在什么地方结束,所以不需要寻扑֭W串的结;只需要把内存中的数据拯到指定的地方卛_Q加上重新计字W串的长度。所以它的执行效率和U?C 的代码是一LQ但是它更容易写、更Ҏl护和更Ҏ理解?br>  如果你还是不能确定究竟发生了怎样的过E,L?CString 的源代码Qstrcore.cppQ在?vc98的安装目录的 mfc\src 子目录中。看?ConcatInPlace ҎQ它被在所有的 += 操作W中调用?/p>

啊哈Q难?CString 真的q么"高效"吗?比如Q如果我创徏

CString cat("Mew!");
  然后我ƈ不是得到了一个高效的、精?个字节大的~冲区(4个字W加一个结束字W)Q系l将l我分配64个字节,而其?9个字节都被浪费了?br>  如果你也是这么想的话Q那么就请准备好接受再教育吧。可能在某个地方某个人给你讲q尽量用少的空间是件好事情。不错,q种说法的确正确Q但是他忽略了事实中一个很重要的方面?br>  如果你编写的是运行在16K EPROMs下的嵌入式程序的话,你有理由量用空_在这U环境下Q它能你的E序更健壮。但是在 500MHz, 256MB的机器上?Windows E序Q如果你q是q么做,它只会比你认为的“低效”的代码运行得更糟?br>  举例来说。字W串的大被认ؓ是媄响效率的首要因素Q字符串尽可能可以提高效率,反之则降低效率,q是大家一贯的x。但是这U想法是不对的,_的内存分配的后果要在E序q行了好几个时后才能体现得出来Q那ӞE序的堆中将充满片的内存,它们太小以至于不能用来做M事,但是他们增加了你E序的内存用量,增加了内存页面交换的ơ数Q当面交换的次数增加到pȝ能够忍受的上限,pȝ则会Z的程序分配更多的面Q直C的程序占用了所有的可用内存。由此可见,虽然内存片是决定效率的ơ要因素Q但正是q些因素实际控制了系l的行ؓQ最l,它损害了pȝ的可靠性,q是令h无法接受的?br>  CQ在 debug 模式下,内存往往是精分配的Q这是ؓ了更好的排错?br>  假设你的应用E序通常需要连l工作好几个月。比如,我常打开 VC++QWordQPowerPointQFrontpageQOutlook ExpressQForté AgentQInternet Explorer和其它的一些程序,而且通常不关闭它们。我曄夜以l日地连l用 PowerPoint 工作了好几天Q反之,如果你不q怸得不使用?Adobe FrameMaker q样的程序的话,你将会体会到可靠性的重要Q这个程序机会每天都要崩?~6ơ,每次都是因ؓ用完了所有的I间q填满我所有的交换面Q。所以精内存分配是不可取的Q它会危及到pȝ的可靠性,q引起应用程序崩溃?br>  按量子的倍数为字W串分配内存Q内存分配器可以回收用q的内存块,通常q些回收的内存块马上可以被其它?CString 对象重新用到Q这样就可以保证片最。分配器的功能加ZQ应用程序用到的内存p可能保持最,q样的程序就可以q行几个星期或几个月而不出现问题?br>  题外话:很多q以前,我们?CMU 写一个交互式pȝ的时候,一些对内存分配器的研究昄出它往往产生很多内存片。Jim MitchellQ现在他?Sun Microsystems 工作Q那时侯他创造了一U内存分配器Q它保留了一个内存分配状늚q行时统计表Q这U技术和当时的主分配器所用的技术都不同Q且较ؓ领先。当一个内存块需要被分割得比某一个值小的话Q他q不分割它,因此可以避免产生太多到什么事都干不了的内存碎片。事实上他在内存分配器中使用了一个Q动指针,他认为:与其让指令做长时间的存取内存操作Q还不如单的忽略那些太小的内存块而只做一些Q动指针的操作。(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.Q他是对的?br>  永远不要认ؓ所谓的“最优化”是徏立在每一行代码都高速且节省内存的基上的Q事实上Q高速且节省内存应该是在一个应用程序的整体水^上考虑的。在软g的整体水q上Q只使用最内存的字符串分配策略可能是最p糕的一U方法?br>  如果你认Z化是你在每一行代码上做的那些努力的话Q你应该想一惻I在每一行代码中做的优化很少能真正v作用。你可以看我的另一关于优化问题的文章《Your Worst Enemy for some thought-provoking ideas》?br>  CQ?= q算W只是一U特例,如果你写成下面这P

CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;
则每一?+ 的应用会造成一个新的字W串被创建和一ơ复制操作?/p>

 ȝ

  以上是?CString 的一些技巧。我每天写程序的时候都会用到这些。CString q不是一U很难用的c,但是 MFC 没有很明昄指出q些特征Q需要你自己L索、去发现?



李亚 2008-01-12 22:55 发表评论
]]>
[转] 深入探讨MFC消息循环和消息܇http://www.shnenglu.com/lilac/archive/2007/12/17/38782.html李亚李亚Mon, 17 Dec 2007 12:28:00 GMThttp://www.shnenglu.com/lilac/archive/2007/12/17/38782.htmlhttp://www.shnenglu.com/lilac/comments/38782.htmlhttp://www.shnenglu.com/lilac/archive/2007/12/17/38782.html#Feedback0http://www.shnenglu.com/lilac/comments/commentRss/38782.htmlhttp://www.shnenglu.com/lilac/services/trackbacks/38782.html深入探讨MFC消息循环和消息܇

作者:周焱

?先,应该清楚MFC的消息@?::GetMessage,::PeekMessage)Q消息܇(CWinThread::PumpMessage)?MFC的消息在H口之间的\由是两g不同的事情。在MFC的应用程序中(应用E序cd于CWinThreadl承)Q必要有一个消息@环,他的作用是从 应用E序的消息队列中d消息Qƈ把它zN出?::DispatchMessage)。而消息\由是指消息派送出M后,pȝ(USER32.DLL) 把消息投递到哪个H口Q以及以后消息在H口之间的传递是怎样的?/font>

消息分ؓ队列消息(q入U程的消息队? 和非队列消息(不进入线E的消息队列)。对于队列消息,最常见的是鼠标和键盘触发的消息Q例如WM_MOUSERMOVE,WM_CHAR{消息;q有?如:WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动E序׃把这些事件{换成相应的消息,然后?送到pȝ消息队列Q由Windowspȝ负责把消息加入到相应U程的消息队列中Q于是就有了消息循环(从消息队列中dq派送消?。还有一U是非队列消 息,他绕q系l队列和消息队列Q直接将消息发送到H口q程。例?当用hzM个窗口系l发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。创建窗口时发送WM_CREATE消息。在后面你将看到QMSq么设计是很有道理的Q以及他的整套实现机制?/p>

q里讲述MFC的消息@环,消息c先看看E序启动Ӟ怎么q入消息循环的:
_tWinMain ->AfxWinMain ->AfxWinInit ->CWinThread::InitApplication ->CWinThread::InitInstance ->CWinThread::Run

非对话框E序的消息@环的事情都从qCWinThread的一Run开?..

W一部分Q非对话框程序的消息循环机制?/strong>

//thrdcore.cpp
// main running routine until thread exits
int CWinThread::Run()
{
ASSERT_VALID(this);

// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;

// acquire and dispatch messages until a WM_QUIT message is received.
for (;;)
{
// phase1: check to see if we can do idle work
while (bIdle &&
   !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
   // call OnIdle while in bIdle state
   if (!OnIdle(lIdleCount++))
    bIdle = FALSE; // assume "no idle" state
}

// phase2: pump messages while available
do
{
   // pump message, but quit on WM_QUIT
   if (!PumpMessage())
    return ExitInstance();

   // reset "no idle" state after pumping "normal" message
   if (IsIdleMessage(&m_msgCur))
   {
    bIdle = TRUE;
    lIdleCount = 0;
   }

} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}    //无限循环Q退出条件是收到WM_QUIT消息?/font>

ASSERT(FALSE); // not reachable
}

q是一个无限@环,他的退出条件是收到WM_QUIT消息Q?/p>

if (!PumpMessage())
    return ExitInstance();

在PumpMessage中,如果收到WM_QUIT消息Q那么返回FALSE,所以ExitInstance()函数执行Q蟩出@环,q回E序的退Z码。所以,一个程序要退出,只用在代码中调用函数

VOID PostQuitMessage( int nExitCode )。指定退Z码nExitCode可以退出程序?/font>

下面讨论一下这个函数Run的流E,分两步:

1, W一个内循环phase1。bIdle代表E序是否I闲。他的意思就是,如果E序是空闲ƈ且消息队列中没有要处理的消息Q那么调用虚函数OnIdleq行 I闲处理。在q个处理中将更新UI界面(比如工具栏按钮的enable和disable状?Q删除时对?比如用FromHandle得到的对象指 针。由于这个原因,在函C间传递由FromHandle得到的对象指针是不安全的Q因Z没有持久?。OnIdle是可以重载的Q你可以重蝲他ƈq回 TRUE使消息@环l处于空闲状态?/p>

NOTEQMS用时对象是Z效率上的考虑Q内存 有效利用Qƈ能够在空闲时自动撤销资源。关于由句柄转换成对象,可以有若q种Ҏ。一般是先申明一个对象obj,然后使用obj.Attatch来和一?句柄l定。这样生的对象是永久的Q你必须用obj.Detach来释攑֯象?/font>

2Q第二个内@环phase2。在q个循环内先启动消息?PumpMessage),如果不是WM_QUIT消息Q消息܇消息发送出?::DispatchMessage)。消息的目的地是消息l构中的hwnd字段所对应的窗口?br>//thrdcore.cpp
BOOL CWinThread::PumpMessage()
{
ASSERT_VALID(this);

//如果是WM_QUIT退出函?return FALSE)Q这导致程序结?
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) {
#ifdef _DEBUG
   if (afxTraceFlags & traceAppMsg)
    TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n");
   m_nDisablePumpCount++; // application must die
    // Note: prevents calling message loop things in 'ExitInstance'
    // will never be decremented
#endif
   return FALSE;
}

#ifdef _DEBUG
if (m_nDisablePumpCount != 0)
{
   TRACE0("Error: CWinThread::PumpMessage called when not permitted.\n");
   ASSERT(FALSE);
}
#endif

#ifdef _DEBUG
if (afxTraceFlags & traceAppMsg)
   _AfxTraceMsg(_T("PumpMessage"), &m_msgCur);
#endif

// process this message

if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
   ::TranslateMessage(&m_msgCur); //键{?/font>
   ::DispatchMessage(&m_msgCur); //zN消?/font>
}
return TRUE;
}

?q一步有一个特别重要的函数大家一定认识:PreTranslateMessage。这个函数在::DispatchMessage发送消息到H口之前Q?q行Ҏ息的预处理。PreTranslateMessage函数是CWinThread的成员函敎ͼ大家重蝲的时候都是在ViewcL者主H口cMQ那 么,它是怎么q入别的cȝ呢?代码如下Q?br>//thrdcore.cpp
BOOL CWinThread::PreTranslateMessage(MSG* pMsg)
{
ASSERT_VALID(this);

// 如果是线E消息,那么会调用U程消息的处理函?/font>
if (pMsg->hwnd == NULL && DispatchThreadMessageEx(pMsg))
   return TRUE;

// walk from target to main window
CWnd* pMainWnd = AfxGetMainWnd();
if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))
   return TRUE;

// in case of modeless dialogs, last chance route through main
//   window's accelerator table
if (pMainWnd != NULL)
{
   CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);
   if (pWnd->GetTopLevelParent() != pMainWnd)
    return pMainWnd->PreTranslateMessage(pMsg);
}

return FALSE;   // no special processing
}

׃面这个函数可以看出:

W一Q如?pMsg->hwnd == NULL),说明q是一个线E消息。调用CWinThread::DispatchThreadMessageEx到消息映表扑ֈ消息入口Q然后调用消息处理函数?/p>

NOTE: 一般用PostThreadMessage函数发送线E之间的消息Q他和窗口消息不同,需要指定线Eid,消息Ȁ被系l放入到目标U程的消息队列中Q用 ON_THREAD_MESSAGE( message, memberFxn )宏可以映线E消息和他的处理函数。这个宏必须在应用程序类(从CWinThreadl承)中,因ؓ只有应用E序cL处理U程消息。如果你在别的类(?如视囄)中用q个宏,U程消息的消息处理函数将得不到线E消息?/font>

W二Q消息的目标H口?PreTranslateMessage函数首先得到消息处理权,如果函数q回FALSEQ那么他的父H口得到消息的处理权,直到ȝ口;如果函数q回 TRUE(表示消息已经被处理了)Q那么就不需要调用父cȝPreTranslateMessage函数。这P保证了消息的目标H口以及他的父窗口都?以有Z调用PreTranslateMessage--在消息发送到H口之前q行预处?如果自己处理完然后返回FALSE的话 -_-b),如果你想要消息不传递给父类q行处理的话Q返回TRUEp了?/p>

W三Q如果消息的目标H口和主H口没有父子关系Q那么再调用?H口的PreTranslateMessage函数。ؓ什么这Pq二步知道Q一个窗口的父窗口不是主H口的话Q尽它?PreTranslateMessageq回FALSEQ主H口也没有机会调用PreTranslateMessage函数。我们知道,加速键的{换一?在框架窗口的PreTranslateMessage函数中。我N了MFC中关于加速键转换的处理,只有CFrameWnd, CMDIFrameWndQCMDIChildWnd{窗口类有。所以,W三步的意思是Q如果消息的目标H口(他的父窗口不是主H口Q比如一个这L非模 式对话框)使消息的预处理lO游的?他的PreTranslateMessageq回FALSE)Q那么给一ơ机会给ȝ口调?PreTranslateMessage(万一他是某个加速键消息呢?)Q这栯够保证在有非模式对话框的情况下还能保证主H口的加速键好?br>我做了一个小例子Q在对话框类的PreTranslateMessage中,q回FALSE。在ȝ口显C个非模式对话框,在对话框拥有焦点的时候,仍然能够ȀzMH口的快捷键?/p>

MQ整个框架就是让每个消息的目标窗?包括他的父窗?都有Z参与消息到来之前的处理。呵呵~

?此,非对话框的消息@环和消息늚机制差不多了。这个机制在一个无限@环中Q不断地从消息队列中获取消息Qƈ且保证了E序的线E消息能够得到机会处理, H口消息在预处理之后被发送到相应的窗口处理过E。那么,q有一点疑问,Z么要一会儿调用::PeekMessage,一会儿调用:: GetMessage呢,他们有什么区别?

NOTEQ一般来_GetMessage被设计用来高效地从消息队列获取消息。如果队列中没有消息Q那么函数GetMessage导致线E休?让出CPU旉)。而PeekMessage是判断消息队列中如果没有消息Q它马上q回0Q不会导致线E处于睡眠状态?/font>

?上面的phase1W一个内循环中用CPeekMessageQ它的参数PM_NOREMOVE表示q不从消息队列中U走消息Q而是一个检查询,如果 消息队列中没有消息他立刻q回0Q如果这时线E空闲的话将会引h息@环调用OnIdle处理q程(上面讲到了这个函数的重要?。如果将:: PeekMessageҎ::GetMessage(***),那么如果消息队列中没有消息,U程休眠,直到U程下一ơ获得CPU旉q且有消息出?才可能l执行,q样Q消息@环的I闲旉没有得到应用QOnIdle也将得不到执行。这是Z么既要用::PeekMessage(查询),又要 ?:GetMessage(做实际的工作)的缘故?/p>

W二部分: 对话框程序的消息循环机制

Z对话框的MFC工程和上面的消息循环机制不一栗实际上MFC的对话框工程E序是模式对话框。他和上面讲到的非对话框E序的不同之处,主要在于应用E序对象的InitInstance()不一栗?/p>

//dlg_5Dlg.cpp
BOOL CDlg_5App::InitInstance()
{
AfxEnableControlContainer();
#ifdef _AFXDLL
Enable3dControls();    // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

CDlg_5Dlg dlg; //定义一个对话框对象
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal(); //对话框的消息循环在这里面开?/font>
if (nResponse == IDOK)
{
   // TODO: Place code here to handle when the dialog is
   // dismissed with OK
}
else if (nResponse == IDCANCEL)
{
   // TODO: Place code here to handle when the dialog is
   // dismissed with Cancel
}

// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}

NOTE: InitInstance函数q回FALSEQ由最上面E序启动程可以看出QCWinThread::Run是不会得到执行的。也是_上面W一部分 说的消息循环在对话框中是不能执行的。实际上Q对话框也有消息循环Q她的消息@环在CDialog::DoModal()虚函C的一?RunModalLoop函数中?/font>

q个函数的实C在CWndcMQ?br>int CWnd::RunModalLoop(DWORD dwFlags)
{
ASSERT(::IsWindow(m_hWnd)); // window must be created
ASSERT(!(m_nFlags & WF_MODALLOOP)); // window must not already be in modal state

// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle() & WS_VISIBLE);
HWND hWndParent = ::GetParent(m_hWnd);
m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);
MSG* pMsg = &AfxGetThread()->m_msgCur;

// acquire and dispatch messages until the modal state is done
for (;;)
{
   ASSERT(ContinueModal());

   // phase1: check to see if we can do idle work
   while (bIdle &&
    !::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))
   {
    ASSERT(ContinueModal());

    // show the dialog when the message queue goes idle
    if (bShowIdle)
    {
     ShowWindow(SW_SHOWNORMAL);
     UpdateWindow();
     bShowIdle = FALSE;
    }

    // call OnIdle while in bIdle state
    if (!(dwFlags & MLF_NOIDLEMSG) && hWndParent != NULL && lIdleCount == 0)
    {
     // send WM_ENTERIDLE to the parent
     ::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_hWnd);
    }
    if ((dwFlags & MLF_NOKICKIDLE) ||
     !SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))
    {
     // stop idle processing next time
     bIdle = FALSE;
    }
   }

// phase2: pump messages while available
   do
   {
    ASSERT(ContinueModal());

    // pump message, but quit on WM_QUIT
   //PumpMessage(消息?的实现和上面讲的差不多。都是派送消息到H口?/font>
    if (!AfxGetThread()->PumpMessage())
    {
     AfxPostQuitMessage(0);
     return -1;
    }

    // show the window when certain special messages rec'd
    if (bShowIdle &&
     (pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))
    {
     ShowWindow(SW_SHOWNORMAL);
     UpdateWindow();
     bShowIdle = FALSE;
    }

    if (!ContinueModal())
     goto ExitModal;

    // reset "no idle" state after pumping "normal" message
    if (AfxGetThread()->IsIdleMessage(pMsg))
    {
     bIdle = TRUE;
     lIdleCount = 0;
    }

   } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));
} //无限循环

ExitModal:
m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);
return m_nModalResult;
}

先说说怎么退个无限@环,在代码中Q?br>if (!ContinueModal())
goto ExitModal;
军_是否退出@环,消息循环函数q回也就是快要结束结束程序了?br>BOOL CWnd::ContinueModal()
{
return m_nFlags & WF_CONTINUEMODAL;
}

NOTE: CWnd::ContinueModal()函数查对话框是否l箋模式。返回TRUE,表示现在是模式的Q返回FALSEQ表C对话框已经不是模式(要l束)?/font>

?果要l束对话框,在内部最l会调用函数CWnd::EndModalLoopQ它取消m_nFlags的模式标?消息循环中的 ContinueModal函数返回FALSEQ消息@环将l束Q程序将退?Q然后激发消息@环读取消息。也是_l束模式对话框是一个标志,改变 q个标志可以了。他的代码是Q?/p>

//wincore.cpp
void CWnd::EndModalLoop(int nResult)
{
ASSERT(::IsWindow(m_hWnd));

// this result will be returned from CWnd::RunModalLoop
m_nModalResult = nResult;

// make sure a message goes through to exit the modal loop
if (m_nFlags & WF_CONTINUEMODAL)
{
   m_nFlags &= ~WF_CONTINUEMODAL;
   PostMessage(WM_NULL);
}
}

NOTE: PostMessage(NULL)是有用的。如果消息队列中没有消息的话Q可能消息@环中的ContinueModal()不会马上执行Q发送一个空消息是激发消息@环马上工作?/font>

下面说一下CWnd::RunModalLoop函数中的消息循环I竟q了些什么事?
1, W一个内循环。首先从消息队列中查询消息,如果对话框空Ԍ而且消息队列中没有消息,他做三g事情Q大家应到都能从字面上明白什么意思。最重要的是发?WM_KICKIDLE消息。ؓ什么呢Q第一部分讲到了,非对话框E序用OnIdle来更新用L?UI)Q比如工hQ状态栏。那么,如果对话框中?有工h和状态栏呢,在哪里更?|上有很多这LE序)Q可以处理WM_KICKIDLE消息Q?br>
LRESULT CDlg_5Dlg::OnKickIdle(WPARAM w,LPARAM l)
{
     //调用CWnd::UpdateDialogControls更新用户界面
     UpdateDialogControls(this, TRUE);
     return 0;
}

NOTE: CWnd::UpdateDialog函数发送CN_UPDATE_COMMAND_UI消息l所有的用户界面对话框控件?/font>

2, W二个内循环。最重要的还是PumpMessagezN消息到目标H口。其他的Q像W二个if语句Q?x118消息好像是WM_SYSTIMER消息(p?l用来通知光标跛_的一个消?。也是_如果消息为WM_SYSTIMER或者WM_SYSKEYDOWN,q且I闲昄标志为真的话Q就昄H口q?通知H口立刻重绘?/p>

MQ对话框的消息@环机制和非对话框(比如SDI,MDI)q是cM 的,仅仅侧重点不同。模式对话框是模式显C,自然有他的特炏V下面部分讨Z下模式对话框和非模式对话框的区别。因为模式对话框有自qҎ消息循环Q?非模式对话框Q共用程序的消息循环Q和普通的H口已经没有什么大的区别了?/font>

W三部分Q模式对话框和非模式对话框的区别

q个话题已经有很多h讨论Q我说说我所理解的意思?br>在MFC 框架中,一个对话框对象DoModal一下就能生一个模式对话框QCreate一下就能生一个非模式对话框。实际上Q无论是模式对话框还是非模式对话 框,在MFC内部都是调用::CreateDialogIndirect(***)函数来创建非模式对话框。只是模式对话框作了更多的工作,包括使父H口 无效Q然后进入自q消息循环{等?:CreateDialogIndirect(***)函数最l调用CreateWindowEx函数通知pȝ创徏 H体q返回句柄,他内部没有实现自q消息循环?br>非模式对话框创徏之后立即q回Qƈ且和ȝ序共用一个消息@环。非模式对话框要{对话框l束之后才返回,自己有消息@环。比如下面的代码Q?br>CMyDlg* pdlg = new CMyDlg;
pdlg ->Create(IDD_DIALOG1);
pdlg->ShowWindow(SW_SHOW);
MessageBox("abc");
非模式对话框和消息框MessageBox几乎是同时弹出来。而如果将CreateҎDoModalQ那么,只能弹出模式对话框,在关闭了对话框之?模式对话框自q消息循环l束)Q消息框才弹出来?/p>

NOTEQ?可以在模式对话框中调用GetParent()->EnableWindow(true);q样Q主H口的菜单,工具栏又ȀzMQ能用了。MFC?用非模式对话框来模拟模式对话框,而在win32 SDKE序中,模式对话框激发他的父H口Enable操作是没有效果的?/font>

关于消息循环ȝQ?/strong>


1Q?我们站在一个什么高度看消息循环Q消息@环其实没有什么深奥的道理。如果一个邮递员要不断在一个城市中送信Q我们要求他做什么?要求他来回跑Q但他一ơ只 能在一个地方出现。如果我们的应用E序只有一个线E的话,我们要他不断CؓH口传递消息,我们怎么做?在一个@环中不断的检消息,q将他发送到适当的窗 口。窗口可以有很多个,但消息@环只有一个,而且每时每刻最多只有一个地方在执行代码。ؓ什么? 看第二点?/p>

2Q因为是单线E的(E序q程 启动的时候,只有而且有一个线E,我们UCZU程),所以就像邮递员一P每次只能在某一个地方干zR什么意思呢QD个例子,?: DiapatchMessagezN消息,在窗口处理过E?WinProc,H口函数)q回之前Q他是阻塞的,不会立即q回Q也是消息循环此时不能再从 消息队列中读取消息,直到::DispatchMessageq回。如果你在窗口函C执行一个死循环操作Q就你用PostQuitMessage函数 退出,E序也会down掉?br>while(1)
{
    PostQuitMessage(0); //E序照样down.
}
所 以,当窗口函数处理没有返回的时候,消息循环是不会从消息队列中读取消息的。这也是Z么在模式对话框中要自q无限循环来l消息@环,因ؓq个无限?环阻塞了原来的消息@环,所以,在这个无限@环中要用GetMessage,PeekMessage,DispatchMessage来从消息队列中读?消息q派送消息了。要不然E序׃会响应了Q这不是我们所希望的?br>所以说Q消息@环放在程序的什么的地方都基本上是过的去的,比如攑֜DLL?面。但是,最好在M时候,只有一个消息@环在工作(其他的都被阻塞了)。然后,我们要作好的一件事情,是怎么从消息@环中退出!当然用WM_QUIT 是可以拉~(PostThreadMessage也是个好L)Q这个消息@环退出后Q可能程序退出,也可能会Ȁzd外一个被d的消息@环,E序l箋q?行。这要看你怎么惻I怎么d。最后一个消息@环结束的时候,也许是E序快结束的时候,因ؓȝE的执行代码也快要完?除非BT的再作个d@??/p>

NOTE: 让windowspȝ知道创徏一个线E的唯一Ҏ是调用API CreatThread函数(__beginthreadex之类的都要在内部调用他创建新U程)。好像windows核心~程_在win2000下, pȝ用CreateRemoteThread函数来创建线E,CreateThread在内部调用CreateRemoteThread。不q这不是争论 的焦点,臛_win98下CreateRemoteThreadq不能正常工作,q是CreateThreadL大局?/font>

3Q在整个消息循环的机制中Q还必须谈到H口函数的可重入性。什么意思?是H口函数(他是个回调函?的代码什么时候都可以被系l?调用者一般是user32模块)调用。比如在H口q程中,向自qH口SendMessage(***);那么执行q程是怎样的?
我们知道QSendMessage是要{到消息发送ƈ被目标窗口执行完之后才返回的。那么窗口在处理消息Q然后又{待刚才发送到本窗口的消息被处理后之后(SendMessageq回)才l往下执行,E序不就互相死锁了吗Q?
?实是不会的。windows设计一套适合SendMessage的算法,他判断如果发送的消息是属于本U程创徏的窗口的Q那么直接由user32模块调用 H口函数(可能有H口重入)Qƈ消息的处理l果l果q回。这样做体现了窗口重入。上面的例子Q我们调用SendMessage(***)发送消息到?H口Q那么窗口过E再ơ被调用Q处理完消息之后结果返回,然后SendMessage之后的程序接着执行。对于非队列消息Q如果没有窗口重入,不知道会 是什么样子?/p>

NOTE: ׃H口的可重入性。在win32 SDKE序中应量用全局变量和静态变量,因ؓ在窗口函数执行过E中可能H口重入Q如果重入后这些变量改了,但你的程序在H口重入q回之后l箋执行Q?可能是使用已经改变的全局或静态变量。在MFC?所有窗口的H口函数基本上都是AfxWndProc)Q按照类的思想q行了组l,一般变量都是类?的,好管理的多?/font>

4,MFC中窗口类(比如C**View,CFrameWnd{?中的MessageBox函数Q以?AfxMessageBox函数都是d原有的消息@环的。由消息框内部的一个消息@环来从消息队列中d消息QƈzN消?和模式对话框cM)。实?上,q些消息函数最l调用的?:MessageBoxQ它在消息框内部实现了一个消息@?原有的主E序消息循环被阻塞了)。论坛中到q几ơ关于计?器和消息框的问题Q看下面的代码:
void CTest_recalclayoutView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
MessageBox("abc");
while(1); //设计一个死循环
CView::OnTimer(nIDEvent);
}
?让OnTimer大约5U钟弹出一个消息框。那么,消息框不断的被弹出来Q只要消息框不被关闭Q那么程序就不会q入d@环。实际上Q每ơ弹出对话框Q都?最上层的那个消息框掌握着消息循环Q其他的消息循环被阻塞了。只要不关闭最上面的消息框Qwhile(1);得不到执行。如果点了关闭,E序p入了?循环Q只能用ctrl+alt+del来解决问题了?/p>

5Q消息@环在很多地方都有应用。比如应用在U程池中。一个线E的执行周期一般在U程 函数q回之后l束Q那么怎么廉U程的生命周期呢Q一U方法就是按照消息@环的思想Q在U程中加入消息@环,不断CU程队列d消息Qƈ处理消息Q线E?的生命周期就保持着直到q个消息循环的退出?/p>

NOTEQ只要线E有界面元素或者调用GetMessage,或者有U程消息发送过来,pȝ׃为线E创Z个消息队列?/font>

 

6, 在单U程E序中,如果要执行一个长旉的复杂操作而且界面要有相应的话Q可以考虑用自q消息c比如,可以一个阻塞等待操作放在一个@环中Qƈ超?D|得比较,然后每个{待的片D中用消息܇l箋消息循环Q界面能够响应用户操作。等{之c,都可以应用消息܇(调用一个类DL函数)Q?br>BOOL CChildView::PeekAndPump()
{
MSG msg;
while(::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
{
   if(!AfxGetApp()->PumpMessage())
   {
    ::PostQuitMessage(0);
    return false;
   }
}
return true;
}
其实Q用多线E也能解军_杂运时的界面问题,但是没有q么方便Q而且一般要加入U程通信和同步,考虑的事情更多一炏V?/p>

 

lg所qͼMFC消息循环那么回事,主要思想q是和SDK中差不多。这U思想主要的特点表现在q合MFC整个框架上,为整个框架服务,为应用和功能服务。这是我的理解。呵呵~



李亚 2007-12-17 20:28 发表评论
]]>
d上下文菜单的Ҏ http://www.shnenglu.com/lilac/archive/2007/12/05/37878.html李亚李亚Wed, 05 Dec 2007 12:19:00 GMThttp://www.shnenglu.com/lilac/archive/2007/12/05/37878.htmlhttp://www.shnenglu.com/lilac/comments/37878.htmlhttp://www.shnenglu.com/lilac/archive/2007/12/05/37878.html#Feedback0http://www.shnenglu.com/lilac/comments/commentRss/37878.htmlhttp://www.shnenglu.com/lilac/services/trackbacks/37878.html首先要在在文仉定义菜单:
#define ID_MENU_EDIT   5001 
#define ID_MENU_DELETE 
5002
然后d对话框的WM_CONTEXTMENU消息函数Q函数内容ؓQ?br>
    CMenu menuPopup;
    
if(menuPopup.CreatePopupMenu())
    {
         menuPopup.AppendMenu(MF_STRING,ID_MENU_EDIT,
"修改(&E)");
         menuPopup.AppendMenu(MF_STRING,ID_MENU_DELETE,
"删除(&D)");
         menuPopup.TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this);    
    }
然后定义菜单相应函数,

1,在头文g中添加函数定义语句:

// Generated message map functions
 
//{{AFX_MSG(CAdo2Dlg)
 virtual BOOL OnInitDialog();
 afx_msg void onInfoEdit(); 
//  q个是编辑菜单的响应函数
 afx_msg void onInfoDelete();  
//q个是删除菜单的响应函数
 afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
 afx_msg void OnPaint();
 afx_msg HCURSOR OnQueryDragIcon();
 afx_msg void OnButton1();
 afx_msg void OnButton2();
 afx_msg void OnRdblclkList1(NMHDR
* pNMHDR, LRESULT* pResult);
 afx_msg void OnDblclkList1(NMHDR
* pNMHDR, LRESULT* pResult);
 afx_msg void OnContextMenu(CWnd
* pWnd, CPoint point);
 
//}}AFX_MSG
 DECLARE_MESSAGE_MAP()

 

2Q在cpp文g中添加函CQ?br>

void CAdo2Dlg::OnInfoEdit() 
{
    AfxMessageBox(
"edit");
}

void CAdo2Dlg::OnInfoDelete() 
{
    AfxMessageBox(
"delete");
}

 

3Q然后在cpp文g中添加媄:

BEGIN_MESSAGE_MAP(CAdo2Dlg, CDialog)
    
//{{AFX_MSG_MAP(CAdo2Dlg)
    ON_COMMAND(ID_MENU_EDIT,    OnInfoEdit)
    ON_COMMAND(ID_MENU_DELETE,  OnInfoDelete)
    
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

 

所有的工作完成了!



李亚 2007-12-05 20:19 发表评论
]]>
VC动态链接库的创建和使用 http://www.shnenglu.com/lilac/archive/2007/12/05/37877.html李亚李亚Wed, 05 Dec 2007 12:18:00 GMThttp://www.shnenglu.com/lilac/archive/2007/12/05/37877.htmlhttp://www.shnenglu.com/lilac/comments/37877.htmlhttp://www.shnenglu.com/lilac/archive/2007/12/05/37877.html#Feedback0http://www.shnenglu.com/lilac/comments/commentRss/37877.htmlhttp://www.shnenglu.com/lilac/services/trackbacks/37877.html一QMFC扩展DLL
创徏Q?br>1Q新Z个MFC扩展DLL ,名字为dll5,d头文Ӟ名ؓdll5
2Q头文g中加入:
extern __declspec(dllexport) CString concatA(CString x,CString y);
3Q在cpp文g中加入:
extern __declspec(dllexport) CString concatA(CString x,CString y)
{
 return x + y;
}
4,在cpp文g中加入:
#include "dll5.h"

5Q编译,生成dll
使用Q?br>1Q新Z个单文应用E序Q名为Usedll5
2,刚才生成的dll5.lib文g和dll5.h文g拯到当前应用程序\径下,
  dll5.dll 文g拯?当前应用E序下的debug?br>3Q在当前应用E序中用到该dll5的导出方法(concatA)的文Ӟ或类Q上d如下语句Q?br>#include "dll5.h"
假设其加到 Usedll5View.cpp中?br>4Q在Usedll5ViewcM建立消息映射入口Q在消息函数中添加如下语句:
CString a=concatA("中国北R集团","长春轨道客R股䆾有限公司");
MessageBox(a);
5Q在 工程/讄/q接/对象?模块 中加入:dll5.lib

6,~译执行该应用程序,q触发该消息Q则输出Q?/p>

中国北R集团长春轨道客R股䆾有限公司

之后只要定义不更改,函数体无论怎么更改。我们只要将~译好的dll拯q来卛_。如果定义有了修改,则需要将h文g和lib 文g拯q来Qƈ需要重新编译?/p>

 

二,动态链接库使用׃nMFC DLL
创徏Q?br>1Q新Z?DLL(?动态链接库使用׃nMFC DLLQ?
2Q头文g中加入:
_declspec(dllexport) CString WINAPI concatA(CString x,CString y);
3Q在cpp文g末尾加入Q?br>_declspec(dllexport) CString WINAPI concatA(CString x,CString y)
{
 return x + y;
}
4Q编译,生成dll
使用Q?br>1Q新Z个单文应用E序Q名为Usedll8
2,刚才生成的dll8.lib文g拯到当前应用程序\径下,
  dll8.dll 文g拯?c:\winnt\system32?br>3Q在当前应用E序中用到该dll5的导出方法(concatA)?cȝ头文件上d如下语句Q?br>extern CString WINAPI concatA(CString x,CString y);
假设其加到 Usedll8View.h中?br>4Q在Usedll8ViewcM建立消息映射入口Q在消息函数中添加如下语句:
CString a=concatA("中国北R集团","长春轨道客R股䆾有限公司");
MessageBox(a);
5Q在 工程/讄/q接/对象?模块 中加入:dll8.lib
6,~译执行该应用程序,q触发该消息Q则输出Q?/p>

中国北R集团长春轨道客R股䆾有限公司



李亚 2007-12-05 20:18 发表评论
]]>知识Ҏ理集?..http://www.shnenglu.com/lilac/archive/2007/11/25/37268.html李亚李亚Sun, 25 Nov 2007 03:55:00 GMThttp://www.shnenglu.com/lilac/archive/2007/11/25/37268.htmlUpdateData()的用方?/strong>

         UpdateDataQ)只有一个BOOLcd的参敎ͼUpdateData(FALSE)一般用于对话框控gq接的变量值刷新屏q显C;比如你在一个文本框上绑定了一个m_member变量Q用UpdateData(FALSE);卛_把这个值在文本框里昄出来Q反之,UpdateData(TRUE);能把填入文本框的 内容赋值给m_member.



李亚 2007-11-25 11:55 发表评论
]]>
透明的CheckBox的代?/title><link>http://www.shnenglu.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.shnenglu.com/lilac/archive/2007/11/23/37201.html</guid><wfw:comment>http://www.shnenglu.com/lilac/comments/37201.html</wfw:comment><comments>http://www.shnenglu.com/lilac/archive/2007/11/23/37201.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/lilac/comments/commentRss/37201.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/lilac/services/trackbacks/37201.html</trackback:ping><description><![CDATA[<p>透明的CheckBoxQ基cLCButton...<br>主要代码如下<br>OnPaint() <br>{<br> CPaintDC dc(this); // device context for painting<br>    <br>    // TODO: Add your message handler code here<br>    <br>    CRect rect;<br>    GetClientRect(&rect);</p> <p>    CRect BoxRect;<br>    BoxRect=rect;<br>    BoxRect.right =BoxRect.left +15;<br>    dc.DrawFrameControl(BoxRect,DFC_BUTTON,DFCS_BUTTONCHECK|GetCheck()?DFCS_CHECKED :0);</p> <p>    CFont   myFont;  <br>    myFont.CreatePointFont (120,_T("宋体"));<br>    CFont   *pOldFont=(CFont   *)dc.SelectObject   (&myFont);  <br>    dc.SetBkMode(TRANSPARENT);   <br>    <br>    CString StrWndText;<br>    GetWindowText(StrWndText);</p> <p>    rect.OffsetRect (20,0);<br>    dc.DrawText(StrWndText,   rect,   DT_LEFT|DT_VCENTER|DT_SINGLELINE);   <br>    dc.SelectObject   (pOldFont);  <br>    myFont.DeleteObject   ();  <br>    // Do not call CButton::OnPaint() for painting messages<br>}</p> <img src ="http://www.shnenglu.com/lilac/aggbug/37201.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/lilac/" target="_blank">李亚</a> 2007-11-23 13:21 <a href="http://www.shnenglu.com/lilac/archive/2007/11/23/37201.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.wyj405.cn" target="_blank">޹һɾþþƷۺ</a>| <a href="http://www.xldgdq.cn" target="_blank">AVþ</a>| <a href="http://www.37eee.cn" target="_blank">þۺۺϾþ97ɫ</a>| <a href="http://www.jxlbw.cn" target="_blank">Ʒþþþþ</a>| <a href="http://www.00225858.cn" target="_blank">avպƷþþþþa </a>| <a href="http://www.51yueda.cn" target="_blank">Ʒһþ</a>| <a href="http://www.amgtm.cn" target="_blank">þҹɫƷ鶹</a>| <a href="http://www.uqbg.cn" target="_blank">˾ƷۺϾþþ</a>| <a href="http://www.91chd.cn" target="_blank">99REþþƷﶼǾƷ </a>| <a href="http://www.vtql.cn" target="_blank">ݺɫۺþö</a>| <a href="http://www.whchangxin.com.cn" target="_blank">þþƷ99Ʒ </a>| <a href="http://www.wsjpt.cn" target="_blank">˾þþƷ鶹һ</a>| <a href="http://www.sixmen.com.cn" target="_blank">18պҹþó</a>| <a href="http://www.nicemom.cn" target="_blank">þۺϾþۺϾɫ</a>| <a href="http://www.hunxiaodansang.cn" target="_blank">þþƷ</a>| <a href="http://www.rydtw.cn" target="_blank">þþĻձ</a>| <a href="http://www.2008qb.cn" target="_blank">þþһƷ99þþƷ88</a>| <a href="http://www.yes365cc.cn" target="_blank">޾ƷþþþþҲ</a>| <a href="http://www.shenghuigd.com.cn" target="_blank">þþþѾƷ</a>| <a href="http://www.yrdfund.com.cn" target="_blank">avԾþþþa </a>| <a href="http://www.vbnj.cn" target="_blank">þѾƷav </a>| <a href="http://www.lampserver.cn" target="_blank">ճˮþ޾Ʒtv</a>| <a href="http://www.3q168.net.cn" target="_blank">þˬˬAV</a>| <a href="http://www.176zfblp.cn" target="_blank">þþþùƷŮӰԺ</a>| <a href="http://www.z2023.cn" target="_blank">99þùѸ</a>| <a href="http://www.heizhuai.cn" target="_blank">˳ɵӰվþ</a>| <a href="http://www.mayishenggou.cn" target="_blank">ھƷþ޻</a>| <a href="http://www.6654sf.cn" target="_blank">޹˾ƷŮ˾þþ </a>| <a href="http://www.green-products.cn" target="_blank">þۺϾþþ</a>| <a href="http://www.hzust.cn" target="_blank">þۺɫһ</a>| <a href="http://www.hycv.cn" target="_blank">Ʒݾþþþ</a>| <a href="http://www.fc117.cn" target="_blank">þ99žŹѿС˵</a>| <a href="http://www.ahlgw.cn" target="_blank">91ƷɫۺϾþ</a>| <a href="http://www.i33b.cn" target="_blank">ɫۺϾþ</a>| <a href="http://www.aann8.cn" target="_blank">91ƷۺϾþþƷ</a>| <a href="http://www.fyhd.net.cn" target="_blank">þþۺϾɫۺ̾</a>| <a href="http://www.sz5111.cn" target="_blank">þ99Ʒþ99</a>| <a href="http://www.0731car.com.cn" target="_blank">þþƷԴվ</a>| <a href="http://www.mmexse.cn" target="_blank">AVӰƬþþþþ</a>| <a href="http://www.gjvthsj.cn" target="_blank">þþþAVר</a>| <a href="http://www.xhqb2000.com.cn" target="_blank">þþƷվ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>