功峰's profile旋动家族PhotosBlogListsMore ![]() | Help |
|
旋动家族September 18 【翻译文章】如何升级基于STL的应用来支持Unicode
翻译作者:dozb,Nicole 译注:注意,本文仅仅适合于MSVC环境中STL库,对于STLPort有问题 原作者:Taka Muraoka 原出处:http://www.codeproject.com/vcpp/stl/upgradingstlappstounicode.asp 介绍我最近升级一个想当大的程序,目的是用Unicode代替single-byte 字符。除了少数遗留下来的模块,我忠实地使用t-functions并且用_T()宏包裹我的字符串和字符常量,众所周知这能安全的转换成Unicode,我要做的事情是定义UNICODE 和 _UNICODE,我祈祷所有事情将如我所愿的工作。 天啊,我是多么地错误:(( 因此,我写这篇文章是为了治疗两周工作之痛,并且希望解除其他人的痛苦,这痛苦是我已经经受的。唉... 基础理论上,写出用single- 或 double-字节字符能被编译的代码是直接的。我曾经想在这里写一节,但是Chris Maunder 已经写了 done it. 他描述的技术是广为人知的,因此对理解这篇文章的内容非常有帮助。 Wide 文件 I/O这里是stream类的wide版本,它容易地定义t-风格的宏去管理他们: 你将像这样用它们: tofstream testFile( "test.txt" ) ; testFile << _T("ABC") ; 现在,你期待的结果是,当用single-byte 字符编译的时候,执行代码将生成3字节的文件,当用double-byte 字符编译的时候,执行代码将生成6字节的文件。但是你错了,都是3字节的文件。 这渊源是标准C++的规定,wide流当写到 file。必须转换double-byte 到single-byte 。如上例,宽字符串L"ABC"(有6个字节长),当写到文件前,被转换成窄字符串(3字节)。更坏的情况,如何转换由库的实现来决定的( implementation-dependent)。 我不能找出一个确切的解释,为什么事情会弄成这样子。我猜测,文件被定义为考虑作为字符(single-byte)流。若允许同时写2字节的字符将无法提取。不管对还是错,这都导致严重的问题。例如,你不能写二进制数据到wofstream,因为这个类试图在输出前先窄字符化它。 这对我是明显的问题,因为我有大量的函数像这样写: void outputStuff( tostream& os ) { // output stuff to the stream os << .... } 假如你传递的是tstringstream 对象将没有问题(例如,它流出宽字符),但是假如你传递的是tofstream 将得到怪异的结果(因为所有内容都被窄化了)。 Wide 文件 I/O: 解决方案用调试器单步跟踪STL,结果发现wofstream 在写输出到文件以前,调用std::codecvt 对象来窄化输出的数据。std::codecvt对象是造成字符串从一种字符集到另一种字符集转换的原因。C++要求作为标准提供:1、转换chars 到 chars(例如,费力地什么也不做),2、转换wchar_ts 到chars。后一种就是引起我们这么多伤心事的原因。 解决方案:写一个新的继承自codecvt的类,用来转换wchar_ts 到 wchar_ts(什么也不做),绑定到wofstream 对象中。当wofstream 试图转换它所输出的数据时,它将调用我们新的codecvt 对象,实际上什么也不做,不改变地写输出数据。 在google groups浏览找一些P. J. Plauger写的代码 code (是MSVC环境中STL库的作者),但是用 Stlport 4.5.3 编译还是有问题。 这是最后敲定的版本: #include // nb: MSVC6+Stlport can't handle "std::" // appearing in the NullCodecvtBase typedef. using std::codecvt ; typedef codecvt < wchar_t , char , mbstate_t > NullCodecvtBase ; class NullCodecvt : public NullCodecvtBase { public: typedef wchar_t _E ; typedef char _To ; typedef mbstate_t _St ; explicit NullCodecvt( size_t _R=0 ) : NullCodecvtBase(_R) { } protected: virtual result do_in( _St& _State , const _To* _F1 , const _To* _L1 , const _To*& _Mid1 , _E* F2 , _E* _L2 , _E*& _Mid2 ) const { return noconv ; } virtual result do_out( _St& _State , const _E* _F1 , const _E* _L1 , const _E*& _Mid1 , _To* F2, _E* _L2 , _To*& _Mid2 ) const { return noconv ; } virtual result do_unshift( _St& _State , _To* _F2 , _To* _L2 , _To*& _Mid2 ) const { return noconv ; } virtual int do_length( _St& _State , const _To* _F1 , const _To* _L1 , size_t _N2 ) const _THROW0() { return (_N2 < (size_t)(_L1 - _F1)) ? _N2 : _L1 - _F1 ; } virtual bool do_always_noconv() const _THROW0() { return true ; } virtual int do_max_length() const _THROW0() { return 2 ; } virtual int do_encoding() const _THROW0() { return 2 ; } } ; 你能看得出这些函数都是空架子,实际上什么也不做,仅仅返回noconv 指示而已。 剩下要做的仅仅是把其实例化,并连接到wofstream 对象中。用MSVC,假定你用_ADDFAC() 宏(非标准的)来imbue一个locale到对象。可是它不能和我的新的NullCodecvt类工作,因此我绕过这个宏,写一个新的来代替: #define IMBUE_NULL_CODECVT( outputFile ) \ { \ NullCodecvt* pNullCodecvt = new NullCodecvt ; \ locale loc = locale::classic() ; \ loc._Addfac( pNullCodecvt , NullCodecvt::id, NullCodecvt::_Getcat() ) ; \ (outputFile).imbue( loc ) ; \ } 好,上面给出的不能好好工作的例子代码,现在能这样写: tofstream testFile ; IMBUE_NULL_CODECVT( testFile ) ; testFile.open( "test.txt" , ios::out | ios::binary ) ; testFile << _T("ABC") ; 重要的是必须是在打开文件前,文件流对象要用新的codecvt对象imbue。文件也必须用binary模式打开。假如不是这种模式,每次文件看一个宽字符的高位或低位是10的时候,它将进行既定的CR/LF翻译,结果不是你想要的。假如你真的想要CR/LF序列,你可以明确地插入"\r\n"来代替std::endl。 wchar_t 问题
typedef unsigned short wchar_t ; 不幸的是,因为它用typedef 代替真正的C++类型,这样定义有一个棘手的缺点:你不能重载它。看下面的代码: TCHAR ch = _T('A') ;
tcout << ch << endl ;
用窄字符串,正如你期望的:打印出字符A。用宽字符,它打印出65。编译器决定出,你正在流出一个unsigned short 并且把它作为数字值来代替宽字符来打印它。哈哈!!!找出在你流出特别的字符的地方并修正它,比起贯串你整个代码的基础,这不是办法。我写了一个小函数,使得情况好一些: #ifdef _UNICODE // NOTE: Can't stream out wchar_t's - convert to a string first! inline std::wstring toStreamTchar( wchar_t ch ) { return std::wstring(&ch,1) ; } #else // NOTE: It's safe to stream out narrow char's directly. inline char toStreamTchar( char ch ) { return ch ; } #endif // _UNICODE TCHAR ch = _T('A') ; tcout << toStreamTchar(ch) << endl ; Wide 异常类多数C++程序用异常来捕获错误的发生。不幸地,std::exception 被定义成这个样子: class std::exception { // ... virtual const char *what() const throw() ; } ; 仅仅能捕获窄字符的错误信息。我曾经throw自己定义的或std::runtime_error的异常,因此我写了一个std::runtime_error 的版本如下: class wruntime_error : public std::runtime_error { public: // --- PUBLIC INTERFACE --- // constructors: wruntime_error( const std::wstring& errorMsg ) ; // copy/assignment: wruntime_error( const wruntime_error& rhs ) ; wruntime_error& operator=( const wruntime_error& rhs ) ; // destructor: virtual ~wruntime_error() ; // exception methods: const std::wstring& errorMsg() const ; private: // --- DATA MEMBERS --- // data members: std::wstring mErrorMsg ; ///< Exception error message. } ; #ifdef _UNICODE #define truntime_error wruntime_error #else #define truntime_error runtime_error #endif // _UNICODE /* -------------------------------------------------------------------- */ wruntime_error::wruntime_error( const wstring& errorMsg ) : runtime_error( toNarrowString(errorMsg) ) , mErrorMsg(errorMsg) { // NOTE: We give the runtime_error base the narrow version of the // error message. This is what will get shown if what() is called. // The wruntime_error inserter or errorMsg() should be used to get // the wide version. } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ wruntime_error::wruntime_error( const wruntime_error& rhs ) : runtime_error( toNarrowString(rhs.errorMsg()) ) , mErrorMsg(rhs.errorMsg()) { } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ wruntime_error& wruntime_error::operator=( const wruntime_error& rhs ) { // copy the wruntime_error runtime_error::operator=( rhs ) ; mErrorMsg = rhs.mErrorMsg ; return *this ; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ wruntime_error::~wruntime_error() { } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ const wstring& wruntime_error::errorMsg() const { return mErrorMsg ; } ( class MyExceptionClass : public std::truntime_error { public: MyExceptionClass( const std::tstring& errorMsg ) : std::truntime_error(errorMsg) { } } ; 最后的问题是我有大量的代码看起来如下: try { // do something... } catch( exception& xcptn ) { tstringstream buf ; buf << _T("An error has occurred: ") << xcptn ; AfxMessageBox( buf.str().c_str() ) ; } 我已经定义了一个std::exception的插入者,如下: tostream& operator<<( tostream& os , const exception& xcptn ) { // insert the exception // NOTE: toTstring() converts a string to a tstring - defined below os << toTstring( xcptn.what() ) ; return os ; } 问题是我的插入者调用what(),其仅仅返回窄版本的错误信息。但是假如错误信息包含外国字符,我想看他们在错误对话框。因此我重写了插入者如下: tostream& operator<<( tostream& os , const exception& xcptn ) { // insert the exception if ( const wruntime_error* p = dynamic_cast<const wruntime_error*>(&xcptn) ) os << p->errorMsg() ; else os << toTstring( xcptn.what() ) ; return os ; } 现在,它检测是否给的是一个宽异常类,假如是,流出宽错误信息。否则它用标准的窄错误信息取回。即使我可以专门用truntime_error起源的类在我的应用中,后面的情况仍然是重要的,因为STL或其他第三方库可以throw 来自std::exception的错误。 其他各种问题
各种有用的东东最后,假如你做类似工作,一些小函数对你来说可能有用: extern std::wstring toWideString( const char* pStr , int len=-1 ) ; inline std::wstring toWideString( const std::string& str ) { return toWideString(str.c_str(),str.length()) ; } inline std::wstring toWideString( const wchar_t* pStr , int len=-1 ) { return (len < 0) ? pStr : std::wstring(pStr,len) ; } inline std::wstring toWideString( const std::wstring& str ) { return str ; } extern std::string toNarrowString( const wchar_t* pStr , int len=-1 ) ; inline std::string toNarrowString( const std::wstring& str ) { return toNarrowString(str.c_str(),str.length()) ; } inline std::string toNarrowString( const char* pStr , int len=-1 ) { return (len < 0) ? pStr : std::string(pStr,len) ; } inline std::string toNarrowString( const std::string& str ) { return str ; } #ifdef _UNICODE inline TCHAR toTchar( char ch ) { return (wchar_t)ch ; } inline TCHAR toTchar( wchar_t ch ) { return ch ; } inline std::tstring toTstring( const std::string& s ) { return toWideString(s) ; } inline std::tstring toTstring( const char* p , int len=-1 ) { return toWideString(p,len) ; } inline std::tstring toTstring( const std::wstring& s ) { return s ; } inline std::tstring toTstring( const wchar_t* p , int len=-1 ) { return (len < 0) ? p : std::wstring(p,len) ; } #else inline TCHAR toTchar( char ch ) { return ch ; } inline TCHAR toTchar( wchar_t ch ) { return (ch >= 0 && ch <= 0xFF) ? (char)ch : '?' ; } inline std::tstring toTstring( const std::string& s ) { return s ; } inline std::tstring toTstring( const char* p , int len=-1 ) { return (len < 0) ? p : std::string(p,len) ; } inline std::tstring toTstring( const std::wstring& s ) { return toNarrowString(s) ; } inline std::tstring toTstring( const wchar_t* p , int len=-1 ) { return toNarrowString(p,len) ; } #endif // _UNICODE /* -------------------------------------------------------------------- */ wstring toWideString( const char* pStr , int len ) { ASSERT_PTR( pStr ) ; ASSERT( len >= 0 || len == -1 , _T("Invalid string length: ") << len ) ; // figure out how many wide characters we are going to get int nChars = MultiByteToWideChar( CP_ACP , 0 , pStr , len , NULL , 0 ) ; if ( len == -1 ) -- nChars ; if ( nChars == 0 ) return L"" ; // convert the narrow string to a wide string // nb: slightly naughty to write directly into the string like this wstring buf ; buf.resize( nChars ) ; MultiByteToWideChar( CP_ACP , 0 , pStr , len , const_cast(buf.c_str()) , nChars ) ; return buf ; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ string toNarrowString( const wchar_t* pStr , int len ) { ASSERT_PTR( pStr ) ; ASSERT( len >= 0 || len == -1 , _T("Invalid string length: ") << len ) ; // figure out how many narrow characters we are going to get int nChars = WideCharToMultiByte( CP_ACP , 0 , pStr , len , NULL , 0 , NULL , NULL ) ; if ( len == -1 ) -- nChars ; if ( nChars == 0 ) return "" ; // convert the wide string to a narrow string // nb: slightly naughty to write directly into the string like this string buf ; buf.resize( nChars ) ; WideCharToMultiByte( CP_ACP , 0 , pStr , len , const_cast<char*>(buf.c_str()) , nChars , NULL , NULL ) ; return buf ; } September 11 Windbg 使用指南 转debug常用命令解析:
1 !address eax查看对应内存页的属性
2 vertarget 显示当前进程的大致信息
3 !peb 显示process Environment Block
4 lmvm 可以查看任意一个dll的详细信息
例如:0:026 lmvm msvcrt (deferred)表示察看msvcrt.dll的信息,但是没有加载
symbol可以通过.reload命令来加载
5.reload /!sym 加载符号文件
6 lmf 列出当前进程中加载的所有dll文件和对应的路径
0:018> lmf
7 r 命令显示和修改寄存器上的值
r命令显示和修改寄存器上的值
0:018> r 显示寄存器的值
0:018> r eax=0 修改了寄存器,把eax的值修改为0x0
8 d命令显示esp寄存器指向的内存
如下
0:018>d esp
用dd命令直接指定054efc14地址
0:018>dd 054efc14
注意:第二个d表示DWORD格式,此外还有db(byte),du(Unicode),dc(char)等等。
数据查看指令 d{a|b|c|d|D|f|p|q|u|w|W}
d{b|c|d|D|f|p|q}分别是显示: byte&ASCII, double-word&ASCII,double-word,double-precision,float,pointer-sized,quad-word数据; DA用于显示ASCII,DU用于显示UNICODE; BYB,BYD,显示binary和Byte及binary和DWORD 补充一个DV,用于查看本地变量用的 9 e命令可以用来修改内存地址
跟d命令一样,e命令后面也可以跟类型后缀,比如ed命
令表示用DWORD的方式修改。下面的命令把054efc14地址上的值修改为11112222。
0:018>ed 054efc14 11112222
修改后可以用dd命令来查看内存。
0:018>dd 0543fc14 L4 L4参数指定内存区间的长度为4个DWORD,这样输出只有1行,
而不是8行了。
10s 命令用来搜索内存具体见help文档
11!runaway 可以显示每一个线程的cpu消耗
0:018> !runaway 结果如下:
0:83c 0 days 0:00:00.406
13:bd4 0 days 0:00:00.046
10:ac8 0 days 0:00:00.046
24:4f4 0 days 0:00:00.031
上面输出的第一列是线程的编号和线程ID,后一列对应的是该线程在用户态模式中的
总的繁忙时间。
在该命令加上f参数,还可以看到内核态的繁忙时间,当进程内存占用率比较高的时候
,通过该命令可以方便的找到对应的繁忙线程。
12 ~ 命令是用来切换目标线程
0:018> ~ 可以显示线程的信息
0:018> ~0s把当前的线程切换到0号线程,也就是主线程,切换后提示符会变为0:000.
13 ~* 命令列出当前进程中的所有线程的详细信息
14~*kb命令列出所有线程的堆栈
15 k 命令用来显示当前线程的堆栈,如下
0:018> k
跟d命令一样,k后面也可以跟很多后缀,比如kb kp,kn,kv,kl等,这些后缀控制了
显示的格式和信息。
栈指令k[b|p|P|v]
这四条指令显示的内容类似,但是每个指令都有特色,KB显示三个参数,Kp显示所有的参数,但需要Full Symbols或Private PDBSymbols支持。KP与Kp相似,只是KP将参数换行显示了。Kv用于显示FPO和调用约定,KD,用于显示Stack的Dump,在跟踪栈时比较有用。 这些指令区分大小。 16 u命令把指定地址上的代码翻译成汇编输出
0:018> u 7739d023
USER32!NtUserWaitMessage:
7739d023 b84a120000 mov eax,0x124a
7739d028 ba0003fe7f mov edx,0x7ffe0300
7739d02d ff12 call dword ptr [edx]
7739d02f c3 ret
如果符号文件加载正确,可以用uf命令直接反汇编整个函数,比如uf USER32! NtUserWaitMessage
17 x 查找符号的二进制地址如下
0:018> x msvcr!printf
77bd27c2 msvcrt!printf = <no type information>
上面的命令找到了printf函数的入口地址在77bd27c2
0:001> x ntdll!GlobalCounter
7c99f72c ntdll!GlobalCounter = <no type information>
上面的命令表示ntdll!GlobalCounter这个变量保存的地址是7c99f72c。
注意:符号对应的是变量和变量所在的地址,不是变量的值,上面只是找到GlobalCounter这个变量的值是7c99f72,要找到变量的值,需要用d命令读取内存地址来获取。
X命令还支持通配符,比如x ntdll !*命令列出ntdll模块中的所有的符号,以及对应的二进制地址。
18 dds 打印内存地址上的二进制值
同时自动搜索二进制值对应的符号。
比如要看看当前**中保存了那些函数地址,就可以检查ebp指向的内存
0:018>dds ebp
0013ed98 0013ee24
0013ed9c 75ecb30f BROWSEUI!BrowserProtectedThreadProc+0x44
0013eda0 00163820
0013eda4 0013ee50
0013eda8 00163820
0013edac 00000000
0013edb0 0013ee10
0013edb4 75ece83a BROWSEUI!__delayLoadHelper2+0x23a
0013edb8 00000005
0013edbc 0013edcc
0013edc0 0013ee50
0013edc4 00163820
0013edc8 00000000
0013edcc 00000024
0013edd0 75f36d2c BROWSEUI!_DELAY_IMPORT_DESCRIPTOR_SHELL32
0013edd4 75f3a184 BROWSEUI!_imp__SHGetInstanceExplorer
0013edd8 75f36e80 BROWSEUI!_sz_SHELL32
0013eddc 00000001
0013ede0 75f3726a BROWSEUI!urlmon_NULL_THUNK_DATA_DLN+0x116
0013ede4 7c8d0000 SHELL32!_imp__RegCloseKey <PERF> (SHELL32+0x0)
0013ede8 7c925b34 SHELL32!SHGetInstanceExplorer
这里dds命令从ebp指向的内存地址0013ed98开始打印,第一列是内存地址的值,第二列是地址上对应的二进制数据,第三列是二进制对应的符号。上面的命令自动找到了75ecb390f对应的符号是BROWSEUI!BrowserProtectedThreadProc +0x44.
Com interface 和c++ vtable里面的成员函数都是顺序排列的。所以,dds命令可以方便的找到虚函数表中的具体的函数地址,比如用下面的命令可以找到OpaqueDatinfo类型中虚函数的实际函数地址。
首先通过x命令找到OpaqueDataInfo虚函数地址
0:000> x ole32!OpaqueDataInfo::vftable’
7768265c ole32!OpaqueDataInfo::`vftable' = <no type information>
77682680 ole32!OpaqueDataInfo::`vftable' = <no type information>
接下来dds命令可以打印出虚函数表中的函数名字
0:000> dds 7768265c
19 .frame 命令在栈中切换以便检查局部变量
要查看局部变量的需要如下:
1查看线程的callstack
0:018>knl
00 0012f7a0 7c821c94 ntdll!KiFastSystemCallRet
01 0012f7a4 7c836066 ntdll!NtRequestWaitReplyPort+0xc
02 0012f7c4 77eaaba3 ntdll!CsrClientCallServer+0x8c
03 0012f8bc 77eaacb8 kernel32!ReadConsoleInternal+0x1b8
04 0012f944 77e41990 kernel32!ReadConsoleA+0x3b
第一列的号称为Frame num,通过.frame命令就可以切换到对应的函数中检查局部变量,比如我们检查kernel32!ReadConsoleA,这个函数的frame num是4,于是,我们如下
2 iframe切换到指定行号的函数中
0:018> .frame 4
3然后调用x显示当前frame的局部变量,比如这个函数中有两个局部变量pcls和rawptr
0:018> x
0012fced pcls = 0x0039ba80
0012fcd8 rawptr = 0x0039ba80
20 dt 格式化显示资料
Dt命令格式化显示变量的资料和结构
0:000> dt pcls
Local var @ 0x12fce4 Type MyCls*
0x0039ba80
+0x000 str : 0x00416648 "abcd"
+0x004 inobj : inner
上面的命令打印出pcls的类型是MyCls指针,指向的地址是0x0039ba80,其中的两个class成员的偏移分别在+0和+4,对应的值在第2列显示。加上-b -r参数可以显示inner class和数组的信息:
0:000> dt pcls -b -r
Local var @ 0x12fce4 Type MyCls*
0x0039ba80
+0x000 str : 0x00416648 "abcd"
+0x004 inobj : innner
+0x000 arr : "abcd"
[00] 97 'a'
[01] 98 'b'
[02] 99 'c'
[03] 100 'd'
[04] 0 ''
[05] 0 ''
[06] 0 ''
[07] 0 ''
[08] 0 ''
[09] 0 ''
对于任意的地址,也可以手动指定符号类型来格式化显示。比如把0x0039ba80地址上的数据用MyCls类型来显示:
0:000> dt 0x0039ba80 MyCls
+0x000 str : 0x00416648 "abcd"
+0x004 inobj : innner
21bp设定调试断点
比如可以这样写:0:018>bp notepad!WinMain 在notepade的winmain函数处下断点
断点的位置可以用符号来表示,如上,也可以直接用地址以及windbg的Pseudo_Register(虚拟寄存器)。比如,我们用$exentry表示进程的入口,那么可以用bp @$exentry在进程的入口设置断点,如果notepade的winmain的入口地址为01006420,那么断点也可以这么写
Bp 01006420
bp mysource.cpp:143` "j (poi(MyVar)”0n20) ''; 'g' "
意思就是:当myvar的值等于0x20时,g命令继续执行 下面一个设置条件断点
0:001> bp exceptioninject!foo3 “k; .echo ‘breaks’ ; g”
在exceptioninject!foo3上设置断点后,每次断下来后,先用k显示callstack,然后用.echo命令输出简单的字符串‘breaks’,最后g命令继续执行。
下面看一个更复杂的设置条件断点的例子:
ba w4 execptioninject!i ”j(poi(exceptioninject!i)<0n40) ‘.printf\”exceptioninject!i value is :%d\”,poi(exceptioninject!i); g’ ; ‘.echo stop!’ ”
首先ba w4 exceptioninject!i 表示在修改exceptioninject!i这个全局变量的时候,停下来,
j(judge)命令的作用就是对后面的表达式作条件判断如果为true,执行第一个单引号里面的命令,否则执行第2个单引号里面的命令。
条件表达式是(poi(exceptioninject!i)<0n40),在windbg中excepioninject!i符号表示符号所在的内存地址,而不是符号的数值,相当于c语言的&操作符的作用,poi命令就是取这个地址上的值,相当于c语言的*操作符。所以这个条件判断的意思就是判断exceptioninject!i的值,是否小于十进制的40。如果为真,就执行第一个单引号,‘.printf\”exceptioninject!i value is :%d\”,poi(exceptioninject!i); g’,如果为假,就执行第二个单引号‘.echo stop!’
第一个单引号里有三个命令,.printf .echo 和g。这里的printf和c语言的printf函数语法一样,不过由于这个printf命令本身是在ba命令的双引号里面,所以需要用\来转义print中的引号。第一个引号的作用是:打印出当前exceptioninject!i的值,.echo命令换行 g命令继续执行
第二个引号的作用就是显示stop,由于后面没有g命令,所以windbg会停下。
22 bm 使用模式匹配设置断点
这个功能需要符号表的支持,bm可以通过模式一次设置多个断点,比如
bm mydriver!FastIO* 可以将所有与FastIO*模式匹配的函数下设置断点,比如FastIoRead ,FastIoWriter等函数都会被设置上断点。需要注意的是,bm命令需要full or export symbols支持。
23 ba 对内存访问设置断点 break on access
就是对于内存访问设置断点,对于在多核处理或者多核处理器调试的时候很有用,对于调试多线程也很有用,比如说,我们可以对一个全局变量设置断点,
ba mydriver!gMonitoreedDevices , 如果你认为这个变量的值被莫名的修改了,相信通过ba设置的断点,你可以很快找到是谁修改的。
也可以这样
ba w4 0x4000000 "kb;g" 当0x4000000地址有写操作时,进入断点 。w表示类型为写 4表示长度为4个字节
24 bl 列出所有的断点 break list
25 bc 清除断点 break clear
26 be 开启断点 break enable
27 bd禁用断点 break disable
以上提到的断点指令通过和j指令很容易形成条件断点,比如
bp USER32!GetMessageW "r $t1=poi(esp+4);r $t2=poi(@$t1+4); j(@$t2 = 0x102 ) 'du @$t1+8 L2;gc';'gc'"
这个条件断点,截取WM_CHAR消息,并将字符(包括中文)显示出来。 条件断点的最简形式:bp Address "j (Condition) 'OptionalCommands'; 'gc' " Address是指令的地址,Condition是一个条件表达式,如果@eax=1,'OptionalCommands'是在断点被击中并且表达式成立时要执行的指令;gc指定是从一个条件断点返回,是不可少的一部分。 28跟踪指令T,TA,TB,TC,WT,P,PA,PC
T指令单步执行,在源码调试状态下,可指源码的一行,根据不同的选项也可以为一行ASM指令;
TA单步跟踪到指定地址,如果没有参数将运行到断点处。 TB执行到分支指令,分支指令包括calls, returns, jumps, counted loops, and while loops TC执行到Call指令 WT Trace and Watch Data,一条强大指令,对执行流程做Profile,执行一下看看结果吧 P,PA,PC相信不用多做解释,大家也都明白了 29源代码操作指令.,lsf,lsc,ls,l,lsp
.指令打一个源文件,可以打开一个全路径的文件,也可以通过函数地址来打开并定位到源文件中函数的位置,如. –a myapp!main,. j:\mydriver\mydriver.c
lsf指定一个源文件为当前源文件,使用lsc可显示当前指定的源文件ls可显示源文件的代码。Lsf可以使用全路径,如果源路径已经设置,也可以直接指定源文件名称。如lsf mydriver.c,lsf j:\mydriver\mydriver.c lsc显示当前源文件 ls显示当前源文件的代码,如ls 200显示第200行 l 用于设置源文件选项 lsp 设置源文件行在调试时显示范围比如,显示当前行的前50,后50,lsp 100 但通常使用Windbg时,可以直接用Ctrl+O来打开并查看源文件 30 查询符号
kd> x nt!KeServiceDescriptorTable*
8046e100 nt!KeServiceDescriptorTableShadow = <no type information>
8046e0c0 nt!KeServiceDescriptorTable = <no type information>
kd> ln 8046e100
(8046e100) nt!KeServiceDescriptorTableShadow | (8046e140) nt!MmSectionExtendResource
Exact matches:
nt!KeServiceDescriptorTableShadow = <no type information>
31!gle 查看LastError值
32指定进制的形式0x/0n/0t/y 分别表示 16/10/8/2进制
? 0x12345678+0n10
Evaluate expression: 305419906 = 12345682
33!sym noice/quiet symbol prompts开关
34.srcpath 设置源代码的路径
35dv查看本地变量
36!teb 显示当前线程的执行块(execution block)
37!peb 显示当前进程的执行块(execution block)
38ln[Address] 显示当前地址上的对象类型
39!locks 显示死锁
40!handle可以获取整个进程或者某一个handle的详细信息
首先运行以下!handle,可以看到当前进程的每个一个handle的类型,以及统计信息
0:002>!handle
Handle 4
Type key
Handle c
Type keyEvent
…….
然后找到一个key,查看详细信息
0:001>!handle 4 f
就会列出这个handle的详细信息。
41!htrace命令检查操作句柄的历史记录
!htrace命令可以打印出指定的handle的最近几次调用堆栈
0:001>!htrace 384
42!cs列出CriticalSection的详细信息
43!threadpool能看到完成端口,线城池工作线程和timer回调占线程池的情况
44.time 可以看到进程跑了多长时间
45 !dso 查看当前线程中有哪些对象,分析泄露时用到
46.dump保存进程的dump文件
Dump文件是进程的内存镜像,可当在调试器中打开dump文件时,使用上面的命令检查,看到的结果跟用调试检查进程看到的一样
.dump /ma c:\testdump.dmp
这个命令把当前进程的镜像保存为c:\testdump.dmp,其中/ms参数表示dump的文件应该包含进程的完整信息。
在windbg中,通过file—open---open Crash dump菜单打开dump文件进行分析。打开文件后,运行调试命令看到的信息和状态就是dump文件保存时进程的状态。通过dump文件能够方便的保存发生问题时进程的状态,方便事后分析。 August 26 C++的一些免费库
科学计算库,提供矩阵、随机数、复数、四元数,快速复利叶变换(C++语言)
BLAS 线性代数接口,支持 稀疏,对称,共轭矩阵
风波早上洗脚出门,顺便把鞋子给洗了下!
走在半路上,我低头一看,吓了一跳,不知道怎么搞的,满脚的泡沫。
天呢,丢大了。 赶紧走路先,边走边甩脚,真是可恶! 好不容易捱到公司,我去了不下十次厕所,还是没洗干净!
于是一不做二不休,换拖鞋!我阴干你个破鞋子! 看你还干跟我吐泡泡.... 等着它干..... August 08 2008年8月8日8点 奥运今天是奥运开幕的日子,是我们北京举办奥运的日子!
昨天晚上折腾个电脑到凌晨四点,早上很早就起床了。为嘛?为的是早早的回家看奥运开幕!
这几天心里总是暖暖的,感到一阵喜悦,不知道为什么,有点要过年的味道,又有种家里来客人的感觉!
预祝奥运圆满成功,预祝我们国家可以取得更多的金牌!
August 02 一生相随 转Life Together One fine day, an old couple around the age of 70, walks into a lawyer’s office. Apparently, they are there to file a divorce. Lawyer was very puzzled, after having a chat with them, he got their story. This couple had been quarreling all their 40 over years of marriage nothing ever seems to go right. They hang on because of their children, afraid that it might affect their up-bringing. Now, all their children have already grown up, have their own family, there’s nothing else the old couple have to worry about, all they wanted is to lead their own life free from all these years of unhappiness from their marriage, so both agree on a divorce. Lawyer was having a hard time trying to get the papers done, because he felt that after 40 years of marriage at the age of 70, he couldn’t understand why the old couple would still wants a divorce. While they were signing the papers, the wife told the husband. “I really love you, but I really can’t carry on anymore, I’m sorry.” “It’s OK, I understand.” said the husband. Looking at this, the lawyer suggested a dinner together, just three of them, wife thought, why not, since they are still going be friends. At the dining table, there was a silence of awkwardness. The first dish was roasted chicken, immediately, the old man took the drumstick for the old lady. “Take this, it’s your favorite.” Looking at this, the lawyer thought maybe there’s still a chance, but the wife was frowning when she answer. “This is always the problem, you always think so highly of yourself, never thought about how I feel, don’t you know that I hate drumsticks?” Little did she know that, over the years, the husband have been trying all ways to please her, little did she know that drumsticks was the husband’s favorite. Little did he know that she never thought he understand her at all, little did he know that she hates drumsticks even though all he wants is the best for her. That night, both of them couldn’t sleep, toss and turn, toss and turn. After hours, the old man couldn’t take it anymore, he knows that he still loves her, and he can’t carry on life without her, he wants her back, he wants to tell her, he is sorry, he wanted to tell her, “I love you.” He picks up the phone, started dialing her number. Ringing never stops. He never stop dialing. On the other side, she was sad, she couldn’t understand how come after all these years, he still doesn’t understand her at all, she loves him a lot, but she just can’t take it any- more. Phone’s ringing, she refuses to answer knowing that it’s him. “What’s the point of talking now that it’s over. I have asked for it and now. I want to keep it this way, if not I will lose face. “She thought. Phone still ringing. She has decided to pull out the cord. Little did she remember, he had heart problems. The next day, she received news that he had passed away. She rushed down to his apartment, saw his body, lying on the couch still holding on to the phone. He had a heart attack when he was still trying to get thru her phone line. As sad as she could be. She will have to clear his belongings. When she was looking thru the drawers, she saw this insurance policy, dated from the day they got married, beneficiary is her. Together in that file there’s this note. “To my dearest wife, by the time you are reading this, I’m sure I’m no longer around, I bought this policy for you, though the amount is only $100k, I hope it will be able to help me continue my promise that I have made when we got married, I might not be around anymore, I want this amount of money to continue taking care of you, just like the way I will if I could have live longer. I want you to know I will always be around, by your side. I love you.” Tears flowed like river. When you love someone, let them know. You never know what will happen the next minute. Learn to build a life together. Learn to love each other for who they are,Not what they are. 【中文译文】: 在一个阳光明媚的日子里,一对70多岁的老夫妇走进了律师事务所。显然地,他们准备到那儿办理离婚手续。律师对这对年老的夫妇提出要离婚的事感到非常困惑。后来,跟他们交谈了之后,他得知他们之间有这样一段故事: 这对夫妇从40年前结婚之日起就一直吵个不停。他们似乎找不到共同点,一切在他们看来都格格不入。 由于担心他们的离婚会给孩子的成长带来不良影响,这对老夫妇把离婚的事搁浅到现在。现在,他们的孩子都长大成人了,也有他们各自的家庭了。于是,这对老夫妇再也没有什么事可以担忧的了。他们现在渴望的就是过各自的生活,免受这些年来婚姻带给他们的种种不幸。正因为这样,两个老人都赞同通过离婚解决事情的争端。 律师极其艰难地为他们拟造了一份离婚协议书,因为他觉得,经过婚后40年的相濡以沫现在两个老人都70多岁了,他就是弄不明白为什么这对老夫妇仍然坚持要离婚。 当他们签署文件时,老夫人遗憾地告诉丈夫:“我真的很爱你,但我再也不能忍受下去了,我非常抱歉。” “没有关系,我理解。”她的丈夫有点悲伤地回答道。看到他们夫妇还有一线挽救的希望,律师于是建议他们三个人一起去吃顿晚餐。就他们三个人,老夫人想道,为什么不呢,反正他们很快就成为朋友了。 餐桌上,这对夫妇沉默不语,尴尬的气氛顿时弥漫开来。 第一道菜是烤鸡。立刻地,老夫人的丈夫夹了一个鸡腿给她说道:“尝尝这个,我知道你最喜欢吃鸡腿了。” 见到这种情景,律师心想,他们相亲相爱到这个地步本不应该提出离婚的。然而,出奇意料的是,当老夫人接过丈夫所夹的菜时,眉毛却很不自然地皱了一下答道:“这就是问题所在,你总是自以为是,从来没有顾及过我的感受,难道你就不知道我很讨厌吃鸡腿吗?” 她一点也不清楚,这些年来,她的丈夫一直使尽办法讨她开心;她一点也不知道,鸡腿是她丈夫最喜爱吃的食物。 他一点也不清楚,他的妻子会认为他完全不了解她;他一点也不知道,他妻子讨厌吃鸡腿,尽管他把自己最喜爱吃的都给了她。 那天晚上,两个老人都睡不着,各自在自己的床上辗转反侧,辗转反侧。挣扎了几个小时后,老夫人的丈夫终于忍耐不住,他发觉他仍然深爱着老夫人。他的生活不能没有老夫人,他要她回来,他要亲口告诉她,“我很抱歉;”他要亲口告诉她,“我爱你。” 于是,他拿起电话,开始按老夫人的电话号码,铃声响个不停,但另一边却没人接。尽管对方不接通他的电话,他还是一直不停地在按着重拔键。 另一方面,老夫人也很伤心,她搞不清楚为什么经过多年来的相处她丈夫仍然一点都不了解她。事实上,她也非常爱她的丈夫,但她再也不愿意跟他一起生活了。电话铃声在响,老夫人知道是她丈夫打来的,但她心意已决不再接他的电话。 “现在谈论还有什么意思呢?我和你的感情已经结束了。当初,第一次提出离婚的人是我,那我现在也得保持这种现状。要不然,你会说我反悔,那我岂不是很丢脸。对,对,就这样下去。”老夫人心想道。电话铃声仍然在响,她于是索性把电话线拉开了。 悲剧就这样发生了,她一点也不曾记起,她的丈夫有心脏病。 第二天早上,老夫人得知她丈夫昨晚已逝世的消息。她径直向他的公寓里跑去,发现死后的丈夫躺在沙发上,手里仍然拿着电话。那天晚上,当她的丈夫试图接通她的电话时,心脏病突然发作,他就这样离开了她。 尽管她很悲伤,老夫人仍不得不亲自动手清理他的遗物。当老夫人认真细致地翻着一个抽屉时,她发现了一张保险单。保险日期从他们结婚之日起算起,毫无疑问,保险受益人是她。在这个文件夹里,还有一份就是她丈夫亲手写的遗嘱,里面说道: “献给我最亲爱的妻子:当你读着这封遗嘱的时候,我确信我已不在人间。我为你买了这份保险。虽然金额总数才区区100英磅,但我希望它能帮助我继续履行我们结婚时我所起的照顾你一生一世的诺言。我不能再陪你一起度过你的余生,但我希望保险金额里的钱能够帮助我实现照顾你后半生生活的愿望,就像我可以重生的话那样照顾你。我同时也想让你知道,我会一直在你的周围,在你的身边,保护你关心你,我爱你!” 老夫人读着读着,泪水如小河流水般奔涌而出。 当你爱着一个人的时候,务必要让他们知道,因为你永远不知道下一分钟将会发生什么事。学会一起生活,学会互爱,不是他们是你的什么,而是他们是你的谁. August 01 2008年8月1日 建军节今天是建军节,距离奥运会开幕一而就只剩下7天了!
今天还是我拿到驾照四个月的纪念日,只是很可惜,一直没有钱来买属于自己的小汽车。
努力,赚钱!
好消息,下周三,EF测试。 July 29 为了忘却的纪念没想到就这么永别了!
一个鲜活的生命,往事还是那么历历在目,我有点不敢相信,但是我却要接受这个事实!
老爸生日,晚上给了电话,本来想第二天再打的,但是由于自己的懒惰去一直没打。
直到昨天晚上,我收到哥哥的一个短信,我不敢相信自己的眼睛,“咱xx哥没了。挺想你的。”眼泪在眼眶里打转,往日一起玩耍,一起喝酒的场面又跃然脑海。
本来七兄弟,现在只有六个了。
我见他最后一面应该算是去年年底了。谁知那是一别,竟是永别。心在痛,脑子里乱乱的。
他真的好年轻啊,不应该啊!万恶的老天爷。
收到短信,我急忙给爸爸妈妈电话,家里无人接电话。我知道,他们现在一定是在我哥哥家,我大娘大爷一定很难过。我好希望这一切都不是真的。
同龄人,竟这样和我们分别,怎么能叫人不心疼。
家里没人接电话,我又打电话给妹妹,从妹妹口中我再得知详细情形。好恨呢,为什么不可以活得再久一些呢?
快十点半了,我又打电话给爸爸妈妈,这次爸爸妈妈在家了。
爸爸妈妈怕我担心挂念,一直没敢告诉我。要不是我先说我知道了,他们或许还是不会现在就告诉我!
我理解他们做父母的心。对着电话,我哽咽着,我伤心难过,电话那头的爸爸妈妈,很无奈,不知道该怎么劝我。
我知道自己要坚强,我不能让他们担心我。我说我没事,只是一时难以接受,自己的兄弟就这么离开我们了。难过是有的,但是我不会一直沉湎于这种悲伤之中的。
说了好一会,才挂断电话,我努力坚强着,不能让爸爸妈妈担心我。
今天早上七点多,我接到我哥哥的电话,哥哥跟我说了一些话,是啊,我们都是大人了,而哥哥他们也都是为人父母的人了。哥哥大意就是事已至此,只愿逝者安息,生者坚强,小心照顾自己,不要让意外再次发生。我又伤心了。
挂断电话,去洗了澡,出门上班。台风“凤凰”来了,杭州笼罩在一片雨雾之中。我的心情也就想这天一样,阴着。
再次愿逝者安息。
好好活着就是对关心你的人的最好的礼物。
July 21 电脑升级啦实在很不幸,我那老爷车式的CRT显示器终于退役了。
代替它的是一ViewSonic的1916.
宽屏显示器还是很不错的很爽,但是我那不争气的显卡不配合,分辨率不支持1440*900.
可恶的很!
最后换了台机器才好的!
打算最近进行一系列的升级,包括内存 CPU之流的硬件!!
不知道这样可以支持Vista吗?
呵呵,希望吧! |
|
||||||
|
|