??xml version="1.0" encoding="utf-8" standalone="yes"?> 当头文g中声明了一个函敎ͼ但是在相应的源文件中却没有对该函数进行定义,则会出现?#8220;解决的外部符?#8221;Qunresolved external symbol Q错误。另外,当一个函数调用了外部的一个库文g中的函数Q但是在当前project的properties中ƈ没有所依赖的(dependentQ库文g包含q来Ӟ也会出现q种错误?/p>
lgQ当一个solution在linking时找不到所涉及到的函数的定义时׃出现“unresolved external symbol ”错误?/p>
例如Q下面是调用rapi的库文grapi.lib中的函数Ӟ׃没有rapi.lib包含q来而导致的链接错误?/p>
1>Linking... 【解x案?/span> 1. Project -> ** Properties... -> Configuration Properties -> Linker -> Input -> Additional Dependencies -> rapi.lib 另外Q在此之前,q需drapi库的相应目录QTools -> Options -> Projects and Solutions -> VC++ Directories -> Show Directories for -> 在Include files中添加C:\Program Files\Matrox Imaging\rapi\Include 以及 Tools -> Options -> Projects and Solutions -> VC++ Directories -> Show Directories for -> 在Library files中添加C:\Program Files\Matrox Imaging\rapi\LIB。添加目录的目的是ؓ了VC在调用相应库文g时不必L使用l对地址QVC可以通过文g名在所包含的目录中q行搜烦。这P前面的rapi.lib׃必用绝对地址了?/p>
【缺炏V可UL性差Q如果将原工E中的源文g和头文g中的内容copy下来重新建立该工E时Q例如:在电(sh)子书或网l上扑ֈ的程序)Q仍需自己在工E中d目录及所需的库文g?/p>
2. 在程序前加入#pragma commet(lib, "rapi.lib")也可以达到相同的效果?/span>
]]>
WMFileSync.obj : error LNK2019: unresolved external symbol _CeFindClose@4 referenced in function "public: virtual long __stdcall CWMFileSync::IsFileExists(wchar_t *)" (?IsFileExists@CWMFileSync@@UAGJPA_W@Z)
]]>
PreTranslateMessage只能处理消息队列中的消息,也就是由PostMessage发出的消?鼠标键盘消息{?
SendMessage发送的消息q不攑֜消息队列?而是直接调用处理函数q行处理,所以这U消息用PreTranslateMessage的捕获不到的,如WM_KIllFOCUS和WM_SETFOCUS.
SendMessage的消息是直接执行处理函数?它要{消息执行完才返?执行完SendMessage函数);而PostMessage则把消息发送后立即q回,把消息挂到消息队列中{待执行,所以以下的E序是错?!
#include <windows.h>
int main()
{
PostMessage(HWND_BROADCAST,WM_QUIT,0,0);
return 0;
}
PostMessage消息WM_QUIT发送到消息队列?׃PostMessage的立卌?即结?执行下一句语句结束整个程?消息q挂在消息队列中未处理故出错!!
在此Q我个h认ؓQCString装得确实很完美Q它有许多优点,?#8220;Ҏ(gu)使用 Q功能强Q动态分配内存,大量q行拯时它很能节省内存资源q且执行效率高,与标准E完全兼容Q同时支持多字节与宽字节Q由于有异常机制所以用它安全方便” 其实Q用过E中之所以容易出错,那是因ؓ我们对它了解得还不够Q特别是它的实现机制。因为我们中的大多数人,在工作中q不那么爱深入地ȝ关于它的文档Q何况它q是英文的?
׃前几天我在工作中遇到了一个本不是问题但却特别手、特别难解决而且莫名惊诧的问题。好来最后发现是׃CString引发的。所以没办法Q我把整个CString的实现全部看了一遍,才慌然大(zhn),q彻底弄清了问题的原?q个问题Q我已在csdn上开?。在此,我想把我的一些关于CString的知识ȝ一番,以供他(她)人借鉴Q也许其中有我理解上的错误,望发现者能通知我,不胜感谢?/p>
1Q?CString实现的机?
CString是通过“引用”来管理串的,“引用”q个词我怿大家q不陌生Q象Window内核对象、COM对象{都是通过引用来实现的。而CString也是通过q样的机制来理分配的内存块。实际上CString对象只有一个指针成员变?所以Q何CString实例的长度只?字节.
? int len = sizeof(CString);//len{于4
q个指针指向一个相关的引用内存块,如图: CString str("abcd");
‘A’
‘B’
‘C’
‘D’
0
0x04040404 head部,为引用内存块相关信息
str 0x40404040
正因为如此,一个这L内存块可被多个CString所引用Q例如下列代码:
CString str("abcd");
CString a = str;
CString b(str);
CString c;
c = b;
上面代码的结果是Q上面四个对?str,a,b,c)中的成员变量指针有相同的|都ؓ0x40404040.而这块内存块怎么知道有多个CString引用它呢Q同P它也会记录一些信息。如被引用数Q串长度Q分配内存长度?/p>
q块引用内存块的l构定义如下Q?/p>
struct CStringData
{
long nRefs; //表示有多个CString 引用? 4
int nDataLength; //串实际长? 4
int nAllocLength; //d分配的内存长度(不计q头部的12字节Q? 4
};
׃有了q些信息QCStringp正确地分配、管理、释攑ּ用内存块?/p>
如果你想在调试程序的时候获得这些信息。可以在WatchH口键入下列表达式:
(CStringData*)((CStringData*)(this->m_pchData)-1)?/p>
(CStringData*)((CStringData*)(str.m_pchData)-1)//str为指CString实例
正因为采用了q样的好机制Q得CString在大量拷贝时Q不仅效率高Q而且分配内存?/p>
2QLPCTSTR ?GetBuffer(int nMinBufLength)
q两个函数提供了与标准C的兼容{换。在实际中用频率很高,但却是最Ҏ(gu)出错的地斏V这两个函数实际上返回的都是指针Q但它们有何区别呢?以及调用它们后,q后是做了怎样的处理过E呢Q?/p>
(1) LPCTSTR 它的执行q程其实很简单,只是q回引用内存块的串地址?它是作ؓ操作W重载提供的Q所以在代码中有时可以隐式{换,而有时却需强制转制。如Q?/span>
CString str;
const char* p = (LPCTSTR)str;
//假设有这L一个函敎ͼTest(const char* p)Q?你就可以q样调用
Test(str);//q里会隐式{换ؓLPCTSTR
(2) GetBuffer(int nMinBufLength) 它类|也会q回一个指针,不过它有点差?q回的是LPTSTR
(3) q两者到底有何不同呢Q我惛_诉大Ӟ其本质上完全不一P一般说LPCTSTR转换后只应该当常量用,或者做函数的入参;而GetBuffer(...)取出指针后,可以通过q个指针来修攚w面的内容Q或者做函数的出参。ؓ什么呢Q也许经常有q样的代码:
CString str("abcd");
char* p = (char*)(const char*)str;
p[2] = 'z';
其实Q也许有q样的代码后Q你的程序ƈ没有错,而且E序也运行得挺好。但它却是非常危险的。再?/p>
CString str("abcd");
CString test = str;
....
char* p = (char*)(const char*)str;
p[2] = 'z';
strcpy(p, "akfjaksjfakfakfakj");//q下完蛋?
你知道此Ӟtest中的值是多少吗?{案?abzd"。它也跟着改变了,q不是你所期望发生的。但Z么会q样呢?你稍微想惛_会明白,前面说过Q因为CString是指向引用块的,str与test指向同一块地?当你p[2]='z'后,当然test也会随着改变。所以用它做LPCTSTR做{换后Q你只能去读q块数据Q千万别L变它的内宏V?/p>
假如我想直接通过指针MҎ(gu)据的话,那怎样办呢Q就是用GetBuffer(...).看下qC码:
CString str("abcd");
CString test = str;
....
char* p = str.GetBuffer(20);
p[2] = 'z'; // 执行到此Q现在test中值却仍是"abcd"
strcpy(p, "akfjaksjfakfakfakj"); // 执行到此Q现在test中D?abcd"
Z么会q样Q其实GetBuffer(20)调用Ӟ它实际上另外建立了一块新内块存,q分?0字节长度的bufferQ而原来的内存块引用计C相应?. 所以执行代码后str与test是指向了两块不同的地方,所以相安无事?/p>
(4) 不过q里q有一Ҏ(gu)意事:是str.GetBuffer(20)后,str的分配长度ؓ20Q即指针p它所指向的buffer只有20字节长,l它赋值时Q切不可过Q否则灾隄你不q了Q如果指定长度小于原来串长度Q如GetBuffer(1),实际上它会分?个字节长度(卛_来串长度Q;另外Q当调用GetBuffer(...)后ƈ改变其内容,一定要记得调用ReleaseBuffer(),q个函数会根据串内容来更新引用内存块的头部信息?/span>
(5) 最后还有一注意事项Q看下述代码Q?/p>
char* p = NULL;
const char* q = NULL;
{
CString str = "abcd";
q = (LPCTSTR)str;
p = str.GetBuffer(20);
AfxMessageBox(q);// 合法?/p>
strcpy(p, "this is test");//合法的,
}
AfxMessageBox(q);// 非法的,可能完蛋
strcpy(p, "this is test");//非法的,可能完蛋
q里要说的就是,当返回这些指针后Q?如果CString对象生命l束Q这些指针也相应无效?/p>
3Q拷?& 赋?& "引用内存? 什么时候释放?
下面演示一D代码执行过E?/span>
void Test()
{
CString str("abcd");
//str指向一引用内存块(引用内存块的引用计数?,长度?,分配长度?Q?/span>
CString a;
//a指向一初始数据状态,
a = str;
//a与str指向同一引用内存块(引用内存块的引用计数?,长度?,分配长度?Q?/span>
CString b(a);
//a、b与str指向同一引用内存块(引用内存块的引用计数?,长度?,分配长度?Q?/span>
{
LPCTSTR temp = (LPCTSTR)a;
//temp指向引用内存块的串首地址。(引用内存块的引用计数?,长度?,分配长度?Q?/span>
CString d = a;
//a、b、d与str指向同一引用内存块(引用内存块的引用计数?, 长度?,分配长度?Q?/span>
b = "testa";
//q条语句实际是调用CString::operator=(CString&)函数?b指向一新分配的引用内存块。(新分配的引用内存块的 引用计数?, 长度?, 分配长度?Q?/span>
//同时原引用内存块引用计数?. a、d与str仍指向原 引用内存块(引用内存块的引用计数?,长度?,分配长度?Q?
}
//׃d生命l束Q调用析构函敎ͼD引用计数?Q引用内存块的引用计Cؓ2,长度?,分配长度?Q?/span>
LPTSTR temp = a.GetBuffer(10);
//此语句也会导致重新分配新内存块。temp指向新分配引用内存块的串首地址Q新 分配的引用内存块的引用计Cؓ1,长度?,分配长度?0Q?/span>
//同时原引用内存块引用计数?. 只有str?指向原引用内存块 Q引用内存块的引用计Cؓ1, 长度?, 分配长度?Q?
strcpy(temp, "temp");
//a指向的引用内存块的引用计Cؓ1,长度?,分配长度?0 a.ReleaseBuffer();//注意:a指向的引用内存块的引用计Cؓ1,长度?,分配长度?0
}
//执行到此Q所有的局部变量生命周期都已结束。对象str a b 各自调用自己的析构构
//函数Q所指向的引用内存块也相应减1
//注意Qstr a b 所分别指向的引用内存块的计数均?,q导致所分配的内存块释放
//CString分配的内存在堆里Q只有这?nbsp;引用内存块的计数均ؓ0Q才释放内存
通过观察上面执行q程Q我们会发现CString虽然可以多个对象指向同一引用内块存,但是它们在进行各U拷贝、赋值及改变串内Ҏ(gu)Q它的处理是很智能ƈ且非常安全的Q完全做C互不q涉、互不媄响。当然必要求你的代码用正恰当,特别是实际用中会有更复杂的情况Q如做函数参数、引用、及有时需保存到CStringList当中Q如果哪怕有一块地方使用不当Q其l果也会D发生不可预知的错?/p>
5 FreeExtra()的作?/strong>
看这D代?/p>
(1) CString str("test");
(2) LPTSTR temp = str.GetBuffer(50);
(3) strcpy(temp, "there are 22 character");
(4) str.ReleaseBuffer();
(5) str.FreeExtra();
上面代码执行到第(4)行时Q大安知道str指向的引用内存块计数?,长度?2,分配长度?0. 那么执行str.FreeExtra()Ӟ它会释放所分配的多余的内存?引用内存块计Cؓ1,长度?2,分配长度?2)
6 Format(...) ?FormatV(...)
q条语句在用中是最Ҏ(gu)出错的。因为它最富有技巧性,也相当灵zR在q里Q我没打对它细l分析,实际上sprintf(...)怎么用,它就怎么用。我只提醒用时需注意一点:是它的参数的特D性,׃~译器在~译时ƈ不能L验格式串参数与对应的变元的类型及长度。所以你必须要注意,两者一定要对应上,
否则׃出错。如Q?/p>
CString str;
int a = 12;
str.Format("first:%l, second: %s", a, "error");//result?试试
7 LockBuffer() ?UnlockBuffer()
思议Q这两个函数的作用就是对引用内存块进行加锁及解锁。但使用它有什么作用及执行q它后对CString串有什么实质上的媄响。其实挺单,看下面代?
(1) CString str("test");
(2) str.LockBuffer();
(3) CString temp = str;
(4) str.UnlockBuffer();
(5) str.LockBuffer();
(6) str = "error";
(7) str.ReleaseBuffer();
执行?3)后,与通常情况下不同,temp与strq不指向同一引用内存块。你可以在watchH口用这个表辑ּ(CStringData*)((CStringData*)(str.m_pchData)-1)看看?/p>
其实在msdn中有说明Q?/p>
While in a locked state, the string is protected in two ways:
No other string can get a reference to the data in the locked string, even if that string is assigned to the locked string.
The locked string will never reference another string, even if that other string is copied to the locked string.
8 CString 只是处理串吗Q?/strong>
不对QCString不只是能操作Ԍ而且q能处理内存块数据。功能完善吧Q看q段代码
char p[20];
for(int loop=0; loop<sizeof(p); loop++)
{
p[loop] = 10-loop;
}
CString str((LPCTSTR)p, 20);
char temp[20];
memcpy(temp, str, str.GetLength());
str完全能够转蝲内存块p到内存块temp中。所以能用CString来处理二q制数据
8 AllocSysString()与SetSysString(BSTR*)
q两个函数提供了串与BSTR的{换。用时L意一点:当调用AllocSysString()后,调用它SysFreeString(...)
9 参数的安全检?/strong>
在MFC中提供了多个宏来q行参数的安全检查,如:ASSERT. 其中在CString中也不例外,有许多这L参数验,其实q也说明了代码的安全性高Q可有时我们会发现这很烦Q也DDebug与Release版本不一P如有时程序Debug通正常,而Release则程序崩溃;而有时恰相反QDebug不行QRelease行。其实我个h认ؓQ我们对CString的用过E中Q应力求代码质量高,不能在Debug版本中出CQ何断a框,哪怕releaseq行g看v来一切正常。但很不安全。如下代码:
(1) CString str("test");
(2) str.LockBuffer();
(3) LPTSTR temp = str.GetBuffer(10);
(4) strcpy(temp, "error");
(5) str.ReleaseBuffer();
(6) str.ReleaseBuffer();//执行到此ӞDebug版本会弹出错?/p>
10 CString的异常处?/p>
我只惛_调一点:只有分配内存Ӟ才有可能D抛出CMemoryException.
同样Q在msdn中的函数声明中,注有throw( CMemoryException)的函数都有重新分配或调整内存的可能?/p>
11 跨模块时的Cstring。即一个DLL的接口函C的参CؓCString&Ӟ它会发生怎样的现象。解{我遇到的问题。我的问题原来已l发_地址为:
http://www.csdn.net/expert/topic/741/741921.xml?temp=.2283136
构造一个这样CString对象Ӟ如CString strQ你可知道此时的str所指向的引用内存块吗?也许你会认ؓ它指向NULL。其实不对,如果q样的话QCString所采用的引用机制管理内存块׃有麻烦了Q所以CString在构造一个空串的对象Ӟ它会指向一个固定的初始化地址Q这块数据的声明如下Q?/p>
AFX_STATIC_DATA int _afxInitData[] = {-1,0,0,0};
要描q概括一下:当某个CString对象串置I的话,如Empty(),CString a{,它的成员变量m_pchData׃指向_afxInitDataq个变量的地址。当q个CString对象生命周期l束Ӟ正常情况下它会去Ҏ(gu)指向的引用内存块计数?Q如果引用计Cؓ0(x有Q何CString引用它时)Q则释放q块引用内存。而现在的情况是如果CString所指向的引用内存块是初始化内存块时Q则不会释放M内存?/p>
说了q么多,q与我遇到的问题有什么关pdQ其实关pd着呢?其真正原因就是如果exe模块与dll模块有一个是static~译q接的话。那么这个CString初始化数据在exe模块与dll模块中有不同的地址Q因为staticq接则会在本模块中有一份源代码的拷贝。另外一U情况,如果两个模块都是shareq接的,CString的实C码则在另一个单独的dll中实玎ͼ而AFX_STATIC_DATA指定变量只装一ơ,所以两个模块中_afxInitData有相同的地址?/p>
现在问题完全明白了吧Q你可以自己LCZ下?/p>
__declspec (dllexport) void test(CString& str)
{
str = "abdefakdfj";//如果是staticq接Qƈ且传入的str为空串的话,q里出错?/p>
}
最后一Ҏ(gu)法:写得q里Q其实CString中还有许多技巧性的好东东,我ƈ没去解释。如很多重蝲的操作符、查扄。我认ؓq是详细看看msdnQ这样也怼比我讲的好多了。我只侧重那些可能会出错的情c当Ӟ如我上面叙述中有错误Q敬请高手指点,不胜感谢Q?br />
msdnQ?a >http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmfc98/html/_mfc_cstring_class_members.asp
1.AfxGetStaticModuleState()指向当前模块状?
2.当前函数调用l束后原模块的状态自动被恢复Q?
3.用于DLL中所调用MFC函数、类、资源时的模块状态切?
I其原因Q就debug版中的堆栈中的局部变量(包括指针Q在明确初始化之前都?x0ccq行初始化,因此Q未初始化时候的指针是指向地址0x0cccccccc的,而这D地址一来是处于内核地址I间Q一般的应用E序是无权访问的Q上面的报错是q样产生的。因此,一旦遇Cq报错,基本可以认定E序中出C野指针?/p>
另外一斚wcc对应着int 3调试中断Q堆栈中的存攄局部数据一般情况下是只ȝQ当发生意外执行堆栈里面的数据就会引发该调试中断?/p>
可以认ؓ0x0cc是有特D含义的占位W,对于指针而言Q它跟NULL是一个意思,其它hҎ(gu)意义的占位符q有Q?/p>
0xcdcdcdcd - Created but not initialized
0xdddddddd - Deleted
0xfeeefeee - Freed memory set by NT's heap manager
0xcccccccc - Uninitialized locals in VC6 when you compile w/ /GZ
0xabababab - Memory following a block allocated by LocalAlloc()
在VC~译器下Q如果?zhn)用最高别进行编译,~译器就会很苛刻地指出?zhn)的非常细的警告。当你生命了一个变量,而没有用时Q编译器׃报警告:
“warning C4100: ''QQ'' : unreferenced formal parameter.”
所以,Z让编译器不必你的警告,׃用UNREFERENCED_PARAMETER语句。比如:
int SomeFunction(int arg1, int arg2)
{
UNREFERENCED_PARAMETER(arg2)
...
}
===============================================================================
[ 译文档 本文适合中读?已阅?439?] 文档 代码 工具
C++ At Work 专栏...
未引用参敎ͼdd栏命令及其它...
原著QPaul DiLascia
译QNorthTibet
下蝲源代码:CAtWork0505.exe (171KB)
原文出处QUnreferenced Parameters, Adding Task Bar Commands, and More
未引用参?br>dd栏命?/p>
我看到过一?C++ 代码针对没有使用q的参数?UNREFERENCED_PARAMETERQ例如:
int SomeFunction(int arg1, int arg2)
{
UNREFERENCED_PARAMETER(arg2)
...
}
我还看到q这L代码Q?
int SomeFunction(int arg1, int /* arg2 */)
{
...
}
你能解释它们的差别吗Q哪一U用法更好?
Judy McGeough
是啊Qؓ什么呢Q让我们?UNREFERENCED_PARAMETER 开始吧。这个宏?winnt.h 中定义如下:
#define UNREFERENCED_PARAMETER(P) (P) 换句话说 UNREFERENCED_PARAMETER 展开传递的参数或表辑ּ。其目的是避免编译器关于未引用参数的警告。许多程序员Q包括我在内Q喜Ƣ用最高别的警告 Level 4Q?W4Q进行编译。Level 4 属于“能被安全忽略的事?#8221;的范畴。虽然它们可能你难堪,但很破坏你的代码。例如,在你的程序中可能会有q样一些代码行Q?/p>
int x=1; 但你从没用到q?x。也许这一行是你以前?x 时留下来的,只删除了使用它的代码Q而忘了删除这个变量。Warning Level 4 能找到这些小ȝ。所以,Z么不让编译器帮助你完成可能是最高别的专业化呢Q用Level 4 ~译是展CZ工作态度的一U方式。如果你为公众用者编写库QLevel 4 则是C交CD上需要的。你不想你的开发h员用低U选项清洁地编译他们的代码?br> 问题是,Level 4 实在是太q于注意l节Q在 Level 4 上,~译器连未引用参数这h伤大雅的事情也要抱怨(当然Q除非你真的有意使用q个参数Q这时便相安无事Q。假设你有一个函数带来两个参敎ͼ但你只用其中一个:
int SomeFunction(int arg1, int arg2)
{
return arg1+5;
}使用 /W4Q编译器抱怨:
“warning C4100: ''arg2'' : unreferenced formal parameter.”Z骗过~译器,你可以加?UNREFERENCED_PARAMETER(arg2)。现在编译器在编译你的引?arg2 的函数时便会住口。ƈ且由于语句:
arg2;实际上不做Q何事情,~译器不会ؓ之生Q何代码,所以在I间和性能上不会有M损失?/p>
l心的h可能会问Q既然你不?arg2Q那当初Z要声明它呢?通常是因Z实现某个函数以满x些API固有的v名需要,例如QMFC?OnSize 处理例程的v名必要像下面这P
void OnSize(UINT nType, int cx, int cy); q里 cx/cy 是窗口新的宽/高,nType 是一个类?SIZE_MAXIMIZED ?SIZE_RESTORED q样的编码,表示H口是否最大化或是常规大小。一般你不会在意 nTypeQ只会关?cx ?xy。所以如果你想用 /W4Q则必须使用 UNREFERENCED_PARAMETER(nType)。OnSize 只是上千?MFC ?Windows 函数之一。编写一个基?Windows 的程序,几乎不可能不到未引用参数?br> 说了q么多关?UNREFERENCED_PARAMETER 内容。Judy 在她的问题中q提C另一?C++ E序员常用的q且其作用与 UNREFERENCED_PARAMETER 相同的诀H,那就是注释函数v名中的参数名Q?/p>
void CMyWnd::OnSize(UINT /* nType */,
int cx, int cy)
{
} 现在 nType 是未命名参数Q其效果像你敲?OnSize(UINT, int cx, int cy)一栗那么现在的关键问题是:你应该用哪U方法——未命名参数Q还?UNREFERENCED_PARAMETERQ?br> 大多数情况下Q两者没什么区别,使用哪一个纯_Ҏ(gu)风格问题。(你喜Ƣ你?java 咖啡是黑色还是奶油的颜色Q)但我认ؓ臛_有一U情况必M?UNREFERENCED_PARAMETER。假设你军_H口不允许最大化。那么你便禁?Maximize 按钮Q从pȝ菜单中删除,同时L每一个用戯够最大化H口的操作。因Z是偏执狂Q大多数好的E序员都是偏执狂Q,你添加一?ASSERT Q断aQ以保代码按照你的意图q行Q?/p>
void CMyWnd::OnSize(UINT nType, int cx, int cy)
{
ASSERT(nType != SIZE_MAXIMIZE);
... // use cx, cy
} 质检团队竭尽所能以各种方式q行你的E序QASSERT 从没有弹Q于是你认ؓ~译生成 Release 版本是安全的。但是此?_DEBUG 定义没有了,ASSERT(nType != SIZE_MAXIMIZE)展开?((void)0)Qƈ?nType 一下子成了一个未引用参数Q这栯入你q净的编译。你无法注释掉参数表中的 nTypeQ因Z要在 ASSERT 中用它。于是在q种情况下——你唯一使用参数的地Ҏ(gu)?ASSERT 中或其它 _DEBUG 条g代码中——只?UNREFERENCED_PARAMETER 会保持编译器?Debug ?Release 生成模式下都没有问题。知道了吗?
l束讨论之前Q我惌有一个问题我没有提及Q就是你可以象下面这L pragma 指o抑制单一的编译器警告Q?/p>
#pragma warning( disable : 4100 )4100 是未引用参数的出错代码。pragma 抑制其余文g/模块的该警告。用下面Ҏ(gu)可以重新启用q个警告Q?/p>
#pragma warning( default : 4100 ) 不管怎样Q较好的Ҏ(gu)是在用特定的警告之前保存所有的警告状态,然后Q等你做完之后再回到以前的配|。那P你便回到的以前的状态,q个状态不一定是~译器的默认状态?br> 所以你能象下面q样在代码的前后?pragma 指o抑制单个函数的未引用参数警告Q?/p>
#pragma warning( push )
#pragma warning( disable : 4100 )
void SomeFunction(...)
{
}
#pragma warning( pop ) 当然Q对于未引用参数而言Q这U方法未免冗长,但对于其它类型的警告来说可能׃是这样了。库生成者都是用 #pragma warning 来阻塞警告,q样他们的代码可以用 /W4 q行清洁~译。MFC 中充满了q样?pragmas 指o。还有好多的 #pragma warning 选项我没有在本文讨论。有兛_们的信息请参考相x档?/p>
我注意到一些应用程序,当右键单dd栏最化按钮Ӟ在弹出的上下文菜单中具备Ҏ(gu)的命令。例如,WinAmpQ一个流行的媒体播放器)有一个附加的 “WinAmp”菜单,其中?WinAmp Ҏ(gu)的命令。我如何在程序的d栏按钮中d我自q菜单?
Jirair Osygian
我创Z一个简单的 MFC SDI E序Q该E序用表单视图(Form ViewQ显CZ个计数器。我想通过右键单击d栏上E序的最化按钮来控制启?停止q个计数器。在表单视图上通过按钮控制的启?停止功能q行正常Q我也能启?停止命o加到pȝ菜单。但我单d入的pȝ菜单时没反应。我如何处理q些定制的系l菜单消息?
Monicque Sharman
我两个问题一起回{,Jirair 问题的答案很单:当你右键单击d栏上应用E序的最化按钮Ӟ用户看到的菜单与用户单击左上角应用程序标题栏图标或按 Alt+Space 所看到的菜单一栗如 Figure 1 所C。这个菜单被UCؓpȝ菜单Q其中包括命令如Q还原、最化、最大化和关闭等?/p>
Figure 1 pȝ菜单
你可以调?::GetSystemMenu 来获得此pȝ菜单Q然后可以添加、删除或修改菜单V你甚至可以通过x WS_SYSMENU H口创徏式样标志?PreCreateWindow 虚函数来完全屏蔽掉这个系l菜单。但不论你做什么,当用户右键单MQ务栏上应用程序的最化按钮Ӟq个pȝ菜单q是会显C出来的?br> C Monicque 的问题:如果在系l菜单中d自己的命令,MFC 是如何处理它们的呢?如果按常规来?——在某个地方写一?ON_COMMAND 处理器ƈ它加到消息映射中,你会发现你的处理器不起作用,怎么会这样呢Q?br> 那是因ؓ Windows ?MFC 处理pȝ命o的方式与处理普通菜单命令的方式不一栗当用户调用H体中的常规菜单命o或按钮时QWindows 向主H口发送一?WM_COMMAND 消息。如果你使用 MFCQ那么其命o路由机制捕h消息q过操作pȝ它路由到该命o?ON_COMMAND 命o处理器对象。(有关 MFC 命o处理机制的详l内容,参见我在 MSJ 1995q?月发表的文章Q?#8220;Meandering Through the Maze of MFC Message and Command Routing”Q?br> 然而系l命令不属于 WM_COMMAND 消息范围。而属于另外一个叫做——WM_SYSCOMMAND 的消息。不论命令ID是真正的pȝ命o?SC_MINIMIZE ?SC_CLOSEQ还是你自己d的其它命令ID都是如此。ؓ了处理系l菜单命令,你必d理显式处?WM_SYSCOMMAND q且要选择你自q命o IDs。这需要你在主H口消息映射中添加ON_WM_SYSCOMMANDQ它有一个处理函数如下:
CMainFrame::OnSysCommand(UINT nID, LPARAM lp)
{
if (nID==ID_MY_COMMAND) {
... // 处理?br> return 0;
}
// 传递到基类Q这一步很重要!
return CFrameWnd::OnSysCommand(nID, lp);
} 如果该命令不是你的,不要忘了它传递到你的基类处理——典型地Q那是 CFrameWnd ?CMDIFrameWnd。否则,Windows 无法得到此消息Qƈ且会破坏内徏的命令?br> 在主框架中处?WM_SYSCOMMAND 固然可以Q但q样做感觉太业余。ؓ什么要用特D的机制来处理呢Q就因ؓ它们是系l菜单吗Q如果你惛_视图或文档对象中处理pȝ命o会怎样呢?有一个常见的命o攑ֈ了系l菜单中Q它?yu)?#8220;关于”QID_APP_ABOUTQ,大多?MFC E序都是在应用程序对象中处理 ID_APP_ABOUTQ?/p>
void CMyApp::OnAppAbout()
{
static CAboutDialog dlg;
dlg.DoModal();
} MFC 一个真正很LҎ(gu)是它的命o路由pȝQ它使得?CMyApp q样的非H口对象也能处理菜单命o。许多程序员甚至都不了解怎么会有q样的例外。如果你已经在应用程序对象中处理 ID_APP_ABOUTQ那把ID_APP_ABOUT d到系l菜单后Qؓ什么还要去实现一套单独的机制Q?br> 处理外加pȝ命o的比较好的,或者说?MFC 的方法应该是通过常规的命令\由机制传递它们。然后按 MFC 常规Ҏ(gu)~写 ON_COMMAND 处理例程来处理系l命令。你甚至可以?ON_UPDATE_COMMAND_UI 来更C的系l菜单项Q例如禁用某个菜单项或在菜单Ҏ(gu)Ҏ(gu)CZ个检讫标志?br> Figure 2 是我写的一个类QCSysCmdRouterQ这个类系l命令{成常规命令。ؓ了用这个类Q你要做的只是在L架中实例?CSysCmdRouterQƈ?OnCreate 中调用其 Init Ҏ(gu)卛_Q?/p>
int CMainFrame::OnCreate(...)
{
// 我的菜单项d到系l菜?br> CMenu* pMenu = GetSystemMenu(FALSE);
pMenu->AppendMenu(..ID_MYCMD1..);
pMenu->AppendMenu(..ID_MYCMD2..);
// 通过 MFC 路由pȝ命o
m_sysCmdHook.Init(this);
return 0;
} 一旦你调用 CSysCmdRouter::InitQ你便可以按常规方式处理 ID_MYCMD1 ?ID_MYCMD2Qؓ MFC 命o路由机制中的M对象~写 ON_COMMAND 处理例程——视图,文档Q框Ӟ应用E序或通过改写 OnCmdMsg d的Q何其它命令对象。CSysCmdRouter q让你用 ON_UPDATE_COMMAND_UI 处理器更新系l菜单。唯一要注意的是确保命令IDs不要与其它菜单命令(除非他们实代表相同的命令)或内建系l命令发生冲H,内徏pȝ命o?SC_SIZE = 0xF000 开始。Visual Studio .NET 指定的命?IDs ?0x8000 = 32768 开始,所以如果你?Visual Studio 来指?IDsQ只要不过 0xF000-0x8000 = 0x7000 个命令即可。也是十进制的 28,762。如果你的应用程序有过 28000 个命令,那么你需要咨询编E精病专家?br> CSysCmdRouter 是如何实现其法的呢Q简单:它用我那个以前专栏中无处不在的 CSubclassWnd。CSubclassWnd 使你不用从其z便能子类?MFC H口对象。CSysCmdRouter z?CSubclassWnd q用它子类化主框架。尤其是它截获发送到框架?WM_SYSCOMMAND 消息。如果命?ID 属于pȝ命oQ大?SC_SIZE = 0xF000Q,?CSysCmdRouter 沿着 Windows 一路传递该消息Q否则便吃掉 WM_SYSCOMMAND q新将它作?WM_COMMAND 发送,于是 MFC 按照其常规\pE,调用你的 ON_COMMAND 处理器。很聪明Q是不是Q?br> 那么 ON_UPDATE_COMMAND_UI 处理器呢QCSysCmdRouter 是如何让它处理系l菜单命令的呢?很简单。就?Windows 昄菜单前,他向你的ȝ口发送一?WM_INITMENUPOPUP 消息。这是你更新菜单的最x机——启用或用它们Q添加检讫标志等{。MFC 为每个菜单项创徏一?CCmdUI 对象q将它传递到你的消息映射中相应的 ON_UPDATE_COMMAND_UI 处理器。以它ؓ参数?MFC 函数?CFrameWnd::OnInitMenuPopupQ这个函数是q样的:
void CFrameWnd::OnInitMenuPopup(CMenu* pMenu, UINT nIndex, BOOL bSysMenu)
{
if (bSysMenu)
return; // don''t support system menu
...
} MFC 初始化系l菜单时不做M事情。ؓ什么要d心这U事呢?万一你要?bSysMenu ?FALSEQ即使是pȝ菜单Q那该怎么办?q恰恰是 CSysCmdRouter 做的事情。它截取 WM_INITMENUPOPUP q清?bSysMenu 标志Q也是 LPARAM ?HIWORDQ?/p>
if (msg==WM_INITMENUPOPUP) {
lp = LOWORD(lp); // (set HIWORD = 0)
} 现在Q当 MFC 获得 WM_INITMENUPOPUPQ它认ؓ该菜单是常规菜单。只要你的命?IDs 与真正的pȝ菜单不冲H,一切都q行得很好。如果你改写 OnInitMenuPopupQ唯一丢失的东西是不能从主H口菜单中区分系l菜单。嘿Q你不能什么都惌Q通过改写 CWnd::WindowProcQ你L能处?WM_INITMENUPOPUP 的,或你惌区分Q就比较 HMENUs。但你确实不用关心命令来自何处?/p>
Figure 3 d栏菜?/p>
Z展示所有的实践Q我写了一个小试E序Q?TBMenu。如?Figure 3 所C,当你右键单击d栏上 TBMenu 的最化按钮Q便会显C出菜单。你可以看到在菜单底部有两个额外的命令。TBMenu ?CMainFrame代码?Figure 4 所C。便知道?OnCreate 的什么地Ҏ(gu)加命令ƈ?CMainFrame 的消息映中?ON_COMMAND 以及 ON_UPDATE_COMMAND_UI 处理器处理它们。TBMenu 在其应用E序cM处理 ID_APP_ABOUTQ代码未列出Q。CSysCmdRouter 使系l命令的工作机制cM其它命o?/p>
说到命oQ我们来看看一个小资料Q?/p>
在我一月䆾的专栏中Q我问是否有人知?Ctrl+Alt+Del 的由来。显Ӟ有几个读者知道如何?GoogleQ因Z们发l我的是相同的链接:《今日美国》上的一文章:“Thank this guy for 'control-alt-delete'”Q我在一月发问之前就发现了这文章。Ctrl+Alt+Del 是由一个名?David J. Bradley 的h发现的,他在 IBM 工作q?br>IBM 觉得应该有一U方法不用关闭电(sh)源就能重|(resetQ其新的 PC 机。ؓ什么要专门?Ctrl+Alt+Del q三个键呢?从技术上来说QDavid 需要用两个修饰键。他惌一U没有h可能意外敲入的键l合。所以他选择?
Ctrl+Alt 作ؓ修饰键(?Shift 用得)?DeleteQ此键位于键盘的另一端,所以敲?Ctrl+Alt+Del 需要两只手Q至在q去是这样做的?br>当今C键盘在右边也?Ctrl ?Alt 键。重启特性的初衷是ؓ IBM 的h设计的秘密安全出口,但不可避免地Q它已经成ؓ一个不是秘密的U密。一旦开发h员知道了它,他们便开始告诉客户用这个特性来解决机器挂v问题。随着历史的发展,Ctrl+Alt+Del 被h们亲切地成ؓ“三指敬礼”Q即便是在当今的 Windows 中仍然具有生命力Q用它调ZQ务管理器Q以便你能杀LLd或终止系l(更多有关cM Ctrl+Alt+Del 安全键序列的内容Q参见本月的 Security Briefs 专栏Q。那么,如果 Ctrl+Alt+Del p|了怎么办?Z么会p|Q请按住它保?5 U钟?br> David Bradley 是当初徏?IBM 个h计算机的 12 个工E师之一。他~写?ROM BIOSQ有?David 的简介,参见 David J. Bradley?/p>
编E愉快!
(zhn)的提问和评论可发送到 Paul 的信:cppqa@microsoft.com
作者简?br> Paul DiLascia 是一名自׃ӞN?Web/UI 设计者。他是《Writing Reusable Windows Code in C++》书QAddison-Wesley, 1992Q的作者。通过 http://www.dilascia.com 可以获得更多了解?
本文 MSDN Magazine ?May 2005 期刊Q可通过当地报摊获得Q或者最好是 订阅
本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/liuchanghe/archive/2006/12/31/1471302.aspx
以前只是单是使用sscanf,却没发现其还有如此强大的功能
char str0[100],str1[100],str2[100],str3[100];
sscanf("abcde abc 123 aaa","%s %s %s %s",str0,str1,str2,str3);
l果:str0="abcde" str1="abc" str2 = "123" str3="aaa"
sscanf("abcde abc 123 aaa","abc%s %s %*d %s",str0,str1,str3);
l果:str0="abcde" str1="abc" str3="aaa"
sscanf("abc123 efg456","%4s",str0);
l果:str0="abc1"
sscanf("abc123 efg456a4","%[a-z 1-5]",str0);
l果:str0="abc123 efg45"
sscanf("abc123 efg456a4","%[a-z1-5]",str0);
l果:str0="abc123"
sscanf("ABCTabcZ123 efg456","%[A-P]",str0);
l果:str0="ABC"
sscanf("abc 12345","%[^ ]",str0);
l果:str0="abc"
sscanf("abc 12345","%[^4]",str0);
l果:str0="abc 123"
sscanf("fdaBs 52aB1asdf","%[^4-0]",str0);
l果:str0="fdaBs 5"
sscanf("abc 12345","%[^ ]%[^3]",str0,str1);
l果:str0="abc" str1="12"
sscanf("abc301abc2345","%*[^9-0]%s",str0);
l果:str0="301abc2345"
sscanf("bca@123efg4@5abc","%*[^@]@%[^@]",str0);
l果:str0="123efg4"
Precedence | Operator | Description | Example | Associativity |
---|---|---|---|---|
1 | () [] -> . :: ++ -- |
Grouping operator Array access Member access from a pointer Member access from an object Scoping operator Post-increment Post-decrement |
(a + b) / 4; array[4] = 2; ptr->age = 34; obj.age = 34; Class::age = 2; for( i = 0; i < 10; i++ ) ... for( i = 10; i > 0; i-- ) ... |
left to right |
2 | ! ~ ++ -- - + * & (type) sizeof |
Logical negation Bitwise complement Pre-increment Pre-decrement Unary minus Unary plus Dereference Address of Cast to a given type Return size in bytes |
if( !done ) ... flags = ~flags; for( i = 0; i < 10; ++i ) ... for( i = 10; i > 0; --i ) ... int i = -1; int i = +1; data = *ptr; address = &obj; int i = (int) floatNum; int size = sizeof(floatNum); |
right to left |
3 | ->* .* |
Member pointer selector Member pointer selector |
ptr->*var = 24; obj.*var = 24; |
left to right |
4 | * / % |
Multiplication Division Modulus |
int i = 2 * 4; float f = 10 / 3; int rem = 4 % 3; |
left to right |
5 | + - |
Addition Subtraction |
int i = 2 + 3; int i = 5 - 1; |
left to right |
6 | << >> |
Bitwise shift left Bitwise shift right |
int flags = 33 << 1; int flags = 33 >> 1; |
left to right |
7 | < <= > >= |
Comparison less-than Comparison less-than-or-equal-to Comparison greater-than Comparison geater-than-or-equal-to |
if( i < 42 ) ... if( i <= 42 ) ... if( i > 42 ) ... if( i >= 42 ) ... |
left to right |
8 | == != |
Comparison equal-to Comparison not-equal-to |
if( i == 42 ) ... if( i != 42 ) ... |
left to right |
9 | & | Bitwise AND | flags = flags & 42; | left to right |
10 | ^ | Bitwise exclusive OR | flags = flags ^ 42; | left to right |
11 | | | Bitwise inclusive (normal) OR | flags = flags | 42; | left to right |
12 | && | Logical AND | if( conditionA && conditionB ) ... | left to right |
13 | || | Logical OR | if( conditionA || conditionB ) ... | left to right |
14 | ? : | Ternary conditional (if-then-else) | int i = (a > b) ? a : b; | right to left |
15 | = += -= *= /= %= &= ^= |= <<= >>= |
Assignment operator Increment and assign Decrement and assign Multiply and assign Divide and assign Modulo and assign Bitwise AND and assign Bitwise exclusive OR and assign Bitwise inclusive (normal) OR and assign Bitwise shift left and assign Bitwise shift right and assign |
int a = b; a += 3; b -= 4; a *= 5; a /= 2; a %= 3; flags &= new_flags; flags ^= new_flags; flags |= new_flags; flags <<= 2; flags >>= 2; |
right to left |
16 | , | Sequential evaluation operator | for( i = 0, j = 0; i < 10; i++, j++ ) ... | left to right |