# R% h2 B3 `4 g! r5 F 因为我们中大多数人都是用着SBCS字符串成长的,所以我们在遍历字符串时,常常使用指针的++-和-操作。我们也使用数组下标的表示形式来操作字符串中的字符。这两种方式是用于SBCS和Unicode字符串,因为它们中的字符有着相同的宽度,编译器能正确的返回我们需要的字符。 * |0 e& O0 h' z3 k0 v8 L 然而,当碰到DBCS字符串时,我们必须抛弃这些习惯。这里有使用指针遍历DBCS字符串时的两条规则。违背了这两条规则,你的程序就会存在DBCS有关的bugs。</P> 0 d- C- |: _) C' ?7 V<DIR> * G1 `1 Q% P, H6 Z4 a; y" |' K, ~<LI>1.在前向遍历时,不要使用++操作,除非你每次都检查lead byte; ( u- I7 V% Z3 ~& Z' g+ P' q<LI>2.永远不要使用-操作进行后向遍历。 </LI></DIR>* N2 y6 C9 |. E7 |
<> 我们先来阐述规则2,因为找到一个违背它的真实的实例代码是很容易的。假设你有一个程序在你自己的目录里保存了一个设置文件,你把安装目录保存在注册表中。在运行时,你从注册表中读取安装目录,然后合成配置文件名,接着读取该文件。假设,你的安装目录是C:\Program Files\MyCoolApp,那么你合成的文件名应该是C:\Program Files\MyCoolApp\config.bin。当你进行测试时,你发现程序运行正常。 5 i& \! ^- D: f 现在,想象你合成文件名的代码可能是这样的:</P><RE>bool GetConfigFileName ( char* pszName, size_t nBuffSize ) : j' B; b4 n! s+ P) V{ ! y; s' |4 ~& p( n char szConfigFilename[MAX_PATH];; D6 k* ~9 d; l- |
6 |% |( j0 D/ U% g
// Read install dir from registry... we''ll assume it succeeds. % F# ^: y* \ \- d; q6 l $ h! z1 ^& [7 A5 a& C4 g! Z
// Add on a backslash if it wasn''t present in the registry value.( ]) ?- s4 m8 H& |" P: n
// First, get a pointer to the terminating zero.* }, q* ~& H7 F1 |# x3 b- A
char* pLastChar = strchr ( szConfigFilename, ''\0'' );& G! _( E4 {/ S Y; Y! |$ Y$ ~: W T
4 ?- ` s8 G1 X- W- i% |
// Now move it back one character.% g6 F' o( j0 T8 ~' e' N- B
pLastChar--; . i; C9 a* A& ?! X 7 Q8 `/ E) c3 b0 I' c
if ( *pLastChar != ''\\'' ) 4 U1 O/ m r4 p: M: R% u) n- p strcat ( szConfigFilename, "\\" ); . D0 E2 K* q$ I. d 5 ~, s+ W9 B" A& |( y // Add on the name of the config file.0 o7 |5 j" c2 J' A
strcat ( szConfigFilename, "config.bin" ); , ~0 C: I0 \' [7 l) N) W3 G9 x& E/ y 6 P3 W% g0 X% [
// If the caller''s buffer is big enough, return the filename.7 y0 d5 u5 i& b2 d- G/ `$ C
if ( strlen ( szConfigFilename ) >= nBuffSize ) 5 \# J/ g5 N5 m1 r1 o) p6 ^ return false;% e2 r4 I! b' P" D
else & q' B9 x* f. g) Q; N5 V9 ?" Q2 X1 c, u { # m: I6 @# F# V) G strcpy ( pszName, szConfigFilename );8 X- S5 ?, t5 g U
return true;; v3 G, N: m& M1 n4 I* R
} - y; ?2 K) t+ E& E} </PRE> 这是一段很健壮的代码,然而在遇到 DBCS 字符时它将会出错。让我们来看看为什么。假设一个日本用户使用了你的程序,把它安装在 C:\<IMG src="http://www.vckbase.com/document/journal/vckbase30/images/youkoso.gif" border=0>。下面是这个名字在内存中的存储形式:7 }) D% U! o. k, n" A
6 Z$ Q- n0 T& R: E" _' k( D8 C<TABLE> $ e# Z( g' G, o. ~& y 8 m# l2 g( r) z( |<TR>4 ^# o( y- Y" T5 {! ~- \
<TD align=middle width="12%">43</TD>% Y5 V6 D* Q. t/ m9 d& b' b8 b8 d9 B
<TD align=middle width="12%"><FONT color=#990000>3A</FONT></TD> . U6 H; {9 T, L/ r<TD align=middle width="12%"><FONT color=#0000ff>5C</FONT></TD> ) X. |6 t2 k7 d+ U7 n% L" S* e( ~! S<TD align=middle width="12%">83 88</TD>9 o8 D# h5 s9 w& G' ?! R
<TD align=middle width="13%">83 45</TD> 8 J/ ], s% C3 Y G a" J& n<TD align=middle width="13%">83 52</TD>* A% y; j2 n. Y, s" Q+ S1 E
<TD align=middle width="13%">83 <FONT color=#0000ff>5C</FONT></TD>& ?: ]0 [$ q7 t( o3 u+ ^
<TD align=middle width="13%">00</TD></TR> 6 a7 I( K; W. O! p<TR>; i) E+ o5 ]$ z* `
<TD align=middle width="12%"> </TD> + H, \" j- J; D3 d; H7 M, i/ n<TD align=middle width="12%"> </TD> : w/ ^: c3 Q8 C. z+ B! M* {' o, l t<TD align=middle width="12%"> </TD>, L$ h8 H+ S; u5 r
<TD align=middle width="12%"><FONT color=#990000>LB TB </FONT></TD>% b. \8 z* {/ z+ ^1 J# C( l
<TD align=middle width="13%"><FONT color=#990000>LB TB </FONT></TD> / G6 o) f4 [1 D0 h<TD align=middle width="13%"><FONT color=#990000>LB TB </FONT></TD> . X9 x7 X) w" i& h<TD align=middle width="13%"><FONT color=#990000>LB TB </FONT></TD> 1 R/ @& f- u5 X<TD align=middle width="13%"> </TD></TR> . i5 a4 z9 r4 K8 ^4 x<TR> * u3 v* {& c+ W8 U: L<TD align=middle width="12%">C</TD> 4 n: j! e, y3 G. W7 q2 m<TD align=middle width="12%">:</TD> 3 v$ d1 E- k- r<TD align=middle width="12%">\</TD>/ o4 z, u% p8 `# s
<TD align=middle width="12%"><IMG src="http://www.vckbase.com/document/journal/vckbase30/images/yo.gif" border=0></TD>/ l7 W$ r g, ]
<TD align=middle width="13%"><IMG src="http://www.vckbase.com/document/journal/vckbase30/images/u.gif" border=0></TD> 9 c& P6 S2 A0 o5 C, a: _<TD align=middle width="13%"><IMG src="http://www.vckbase.com/document/journal/vckbase30/images/ko.gif" border=0></TD> - |2 h$ F# h# V& D, [/ h8 M) U<TD align=middle width="13%"><IMG src="http://www.vckbase.com/document/journal/vckbase30/images/so.gif" border=0></TD>3 D9 x' _- U4 Y1 e* v' t! L
<TD align=middle width="13%">EOS</TD></TR></TABLE> , |* |- m8 b3 Z4 _( @: S<> 当使用 GetConfigFileName() 检查尾部的''\\''时,它寻找安装目录名中最后的非0字节,看它是等于''\\''的,所以没有重新增加一个''\\''。结果是代码返回了错误的文件名。 8 ?3 ?+ q' M% C2 [$ N 哪里出错了呢?看看上面两个被用蓝色高量显示的字节。斜杠''\\''的值是0x5c。'' ''的值是83 5c。上面的代码错误的读取了一个 trail byte,把它当作了一个字符。0 |5 W) Y( V0 ]) O9 Y- E
正确的后向遍历方法是使用能够识别DBCS字符的函数,使指针移动正确的字节数。下面是正确的代码。(指针移动的地方用红色标明) </P><RE>bool FixedGetConfigFileName ( char* pszName, size_t nBuffSize )7 p6 M8 X) l) r1 Y' C. J" ]
{ # H* T8 u# u' ?# p0 n0 A/ w char szConfigFilename[MAX_PATH];* _7 X2 B; ]8 S9 h1 P
& [4 z7 P( v1 n+ M6 P
// Read install dir from registry... we''ll assume it succeeds. ) r1 s( w1 z4 E& b. [4 A + ~3 j4 E, |/ f( u& U/ f/ U // Add on a backslash if it wasn''t present in the registry value. 9 J4 a u9 r% Y+ Q( a6 n6 F/ d // First, get a pointer to the terminating zero. . P9 Q$ y; J+ a% }8 C/ ^3 _7 \3 d3 K char* pLastChar = _mbschr ( szConfigFilename, ''\0'' ); ( I) B" m0 A% d1 \7 n0 v9 ` ) S% ~: o# |) T( R0 r7 \ // Now move it back one double-byte character. . s$ Q/ R1 f! j0 }9 ^' e* x <FONT color=#ff0000> pLastChar = CharPrev ( szConfigFilename, pLastChar );</FONT> 3 ]) ?# r2 i- C0 `4 } % ~/ `5 G8 C7 g0 a4 b: o9 I. o6 j
if ( *pLastChar != ''\\'' ) : j+ I6 c% V: i/ ?+ o _mbscat ( szConfigFilename, "\\" ); 1 P1 z8 ^3 U' H ) Z0 Z) U+ h4 e* i( \+ I5 a. w5 r // Add on the name of the config file.- h; ~$ l# W/ c$ t: s
_mbscat ( szConfigFilename, "config.bin" );/ K( Q1 L% s3 N5 {: T0 x3 i/ H. M
; k4 n; y0 y( r, A% i; J9 W2 l" v! N
// If the caller''s buffer is big enough, return the filename. % }1 s. p- @( b0 L) I2 J( o if ( _mbslen ( szInstallDir ) >= nBuffSize )9 h0 ]8 x& W2 {& _; V" F4 F( N
return false;9 m% F- T4 }+ }' u/ J
else+ N4 k) K2 J' g! `3 E
{) P# y# v. P9 \4 `% A5 \
_mbscpy ( pszName, szConfigFilename ); # t* M5 j0 w9 R( d, `& L return true;9 I; f6 b$ [2 P5 Y; o) z% m
}- {- s! X7 n8 `( p
} q( r$ S+ g, n% [</PRE> 上面的函数使用CharPrev() API使pLastChar向后移动一个字符,这个字符可能是两个字节长。在这个版本里,if条件正常工作,因为lead byte永远不会等于0x5c。) X5 z. M+ e# s9 @$ r
让我们来想象一个违背规则1的场合。例如,你可能要检测一个用户输入的文件名是否多次出现了'':''。如果,你使用++操作来遍历字符串,而不是使用CharNext(),你可能会发出不正确的错误警告如果恰巧有一个trail byte它的值的等于'':''的值。 " } Z. o) F# T与规则2相关的关于字符串索引的规则:<RE>2a. 永远不要使用减法去得到一个字符串的索引。</PRE> / }4 w4 c5 y4 D% c3 E0 k' ?& `( \<>违背这条规则的代码和违背规则2的代码很相似。例如,</P><RE>char* pLastChar = &szConfigFilename [strlen(szConfigFilename) - 1];</PRE>( C1 Q; {- K/ I; S, o
<>这和向后移动一个指针是同样的效果。 : \* q; E2 r! p# D$ T% T5 S' k) y/ B, E9 E
<IMG src="http://www.vckbase.com/document/image/paragraph.gif"><B> 回到关于str***()和_mbs***()的区别</B> , P; U. G) o6 c* x! } 6 V1 i0 k1 c; j8 Q2 a2 S' C8 }; C; X 现在,我们应该很清楚为什么_mbs***()函数是必需的。Str***()函数根本不考虑DBCS字符,而_mbs***()考虑。如果,你调用strrchr("C:\\ ", ''\\''),返回结果可能是错误的,然而_mbsrchr()将会认出最后的双字节字符,返回一个指向真的''\\''的指针。 i: ]! t" g% P% J. Z% o+ b 关于字符串函数的最后一点:str***()和_mbs***()函数认为字符串的长度都是以char来计算的。所以,如果一个字符串包含3个双字节字符,_mbslen()将会返回6。Unicode函数返回的长度是按wchar_t来计算的。例如,wcslen(L"Bob")返回3。 8 T' ^! q. D* x, d5 I. P7 q$ \: b3 B
<IMG src="http://www.vckbase.com/document/image/paragraph.gif"><B> Win32 API中的MBCS和Unicode</B> 0 Q9 d9 Q1 p; M P8 }2 c7 Y$ b3 ? D: J2 d/ U
两组 APIs: 5 S% @. w x; }3 r& p' ? 尽管你也许从来没有注意过,Win32中的每个与字符串相关的API和message都有两个版本。一个版本接受MBCS字符串,另一个接受Unicode字符串。例如,根本没有SetWindowText()这个API,相反,有SetWindowTextA()和SetWindowTextW()。后缀A表明这是MBCS函数,后缀W表示这是Unicode版本的函数。 & l9 U' ~5 W8 e2 t k$ J9 T! w 当你 build 一个 Windows 程序,你可以选择是用 MBCS 或者 Unicode APIs。如果,你曾经用过VC向导并且没有改过预处理的设置,那表明你用的是MBCS版本。那么,既然没有 SetWindowText() API,我们为什么可以使用它呢?winuser.h头文件包含了一些宏,例如: </P><RE>BOOL WINAPI SetWindowTextA ( HWND hWnd, LPCSTR lpString ); $ }8 O' ]" m8 R" A: I' b VBOOL WINAPI SetWindowTextW ( HWND hWnd, LPCWSTR lpString ); ) m. J6 n8 z2 K* R J6 T) @# U" t & I) J! ^) W' k* ?
#ifdef UNICODE : `7 T7 G- r; n: e" }/ D#define SetWindowText SetWindowTextW" F o& d+ M% H* }- ?
#else M3 p, G( G4 c6 o
#define SetWindowText SetWindowTextA9 o5 G/ ^7 J7 u: W* N7 F/ A5 u
#endif </PRE>当使用MBCS APIs来build程序时,UNICODE没有被定义,所以预处理器看到:<RE>#define SetWindowText SetWindowTextA</PRE> 5 |$ K! p6 \% U2 X: T) E: t<> 这个宏定义把所有对SetWindowText的调用都转换成真正的API函数SetWindowTextA。(当然,你可以直接调用SetWindowTextA() 或者 SetWindowTextW(),虽然你不必那么做。)9 E7 }: R* H; u& V5 h5 t5 L. q5 V
所以,如果你想把默认使用的API函数变成Unicode版的,你可以在预处理器设置中,把_MBCS从预定义的宏列表中删除,然后添加UNICODE和_UNICODE。(你需要两个都定义,因为不同的头文件可能使用不同的宏。) 然而,如果你用char来定义你的字符串,你将会陷入一个尴尬的境地。考虑下面的代码:</P><RE>HWND hwnd = GetSomeWindowHandle();: g& e6 H: U8 j: W: | \
char szNewText[] = "we love Bob!";- l$ U2 C/ h! Q v3 ~- N) Z# Q
SetWindowText ( hwnd, szNewText );</PRE>( X2 W+ P8 S: M
<>在预处理器把SetWindowText用SetWindowTextW来替换后,代码变成:</P><RE>HWND hwnd = GetSomeWindowHandle(); 2 y" \+ s6 H0 R2 a2 p$ Jchar szNewText[] = "we love Bob!"; . f6 w9 X6 D9 @2 dSetWindowTextW ( hwnd, szNewText );</PRE> ' h0 t/ b9 f# U' F; {<> 看到问题了吗?我们把单字节字符串传给了一个以Unicode字符串做参数的函数。解决这个问题的第一个方案是使用 #ifdef 来包含字符串变量的定义:</P><RE>HWND hwnd = GetSomeWindowHandle(); 8 c1 |8 R" R5 \# p' n- |# ^' ~#ifdef UNICODE' E, I. K8 l& g/ r4 t
wchar_t szNewText[] = L"we love Bob!"; 2 h- C' Z' R4 x9 X#else5 Q, p9 r) D& c
char szNewText[] = "we love Bob!"; 0 H2 `4 V D; f( G8 x#endif0 b* x. Q( G" I$ S
SetWindowText ( hwnd, szNewText );</PRE> ; y+ e q! R' A3 E<>你可能已经感受到了这样做将会使你多么的头疼。完美的解决方案是使用TCHAR. 2 @# ]. y( M) {6 u6 x; E8 t' C1 F4 E V9 L. {! V2 H2 [! Y
<IMG src="http://www.vckbase.com/document/image/paragraph.gif"><B> 使用TCHAR</B> ( L0 l0 z+ m6 R4 [9 m+ ` 8 u# r7 y; W! r0 B1 ?7 p TCHAR是一种字符串类型,它让你在以MBCS和UNNICODE来build程序时可以使用同样的代码,不需要使用繁琐的宏定义来包含你的代码。TCHAR的定义如下:</P><RE>#ifdef UNICODE : o: L) O" [9 ~0 u7 K, Atypedef wchar_t TCHAR;: t8 W- G7 x. ^0 Y
#else8 x. r6 R$ n8 B4 e; s) o& i
typedef char TCHAR; h! c1 {/ j* J6 H#endif</PRE>' c& a/ M0 X& U0 B
<>所以用MBCS来build时,TCHAR是char,使用UNICODE时,TCHAR是wchar_t。还有一个宏来处理定义Unicode字符串常量时所需的L前缀。</P><RE>#ifdef UNICODE+ H4 _5 r# b8 _9 |- L7 |
#define _T(x) L##x7 k/ Y/ K2 `5 b8 r
#else- W! t! |8 |8 F
#define _T(x) x 9 f2 p! h4 i7 g+ r, b#endif</PRE> 7 R3 e8 M/ c. G l) I<P> ##是一个预处理操作符,它可以把两个参数连在一起。如果你的代码中需要字符串常量,在它前面加上_T宏。如果你使用Unicode来build,它会在字符串常量前加上L前缀。</P><PRE>TCHAR szNewText[] = _T("we love Bob!");</PRE>( b5 L& I6 H" K/ q3 {4 h0 c
<P> 像是用宏来隐藏SetWindowTextA/W的细节一样,还有很多可以供你使用的宏来实现str***()和_mbs***()等字符串函数。例如,你可以使用_tcsrchr宏来替换strrchr()、_mbsrchr()和wcsrchr()。_tcsrchr根据你预定义的宏是_MBCS还是UNICODE来扩展成正确的函数,就像SetWindowText所作的一样。 i* p6 K7 q8 W d# W; {
不仅str***()函数有TCHAR宏。其他的函数如, _stprintf(代替sprinft()和swprintf()),_tfopen(代替fopen()和_wfopen())。 MSDN中"Generic-Text Routine Mappings."标题下有完整的宏列表。. C0 N' M7 J6 Z; T- J0 X
% D$ q$ W. N ^$ f' y
<IMG src="http://www.vckbase.com/document/image/paragraph.gif"><B> 字符串和TCHAR typedefs</B> p' {; v) _* A! `4 w1 b
' T" Y/ a, p. {4 G 由于Win32 API文档的函数列表使用函数的常用名字(例如,"SetWindowText"),所有的字符串都是用TCHAR来定义的。(除了XP中引入的只适用于Unicode的API)。下面列出一些常用的typedefs,你可以在msdn中看到他们。</P># Q/ j' O* C0 ]7 d
<TABLE>6 t& W* W4 l+ l: F/ i
7 k {* q7 R3 E" K: A<TR>9 x. ~ w$ |2 G# j* ]6 |: o. L3 e
<TD align=middle width="16%"><B>type </B></TD> . Z9 u9 k: T; B' i4 K<TD align=middle width="42%"><B>Meaning in MBCS builds </B></TD> # R( y s% A3 q<TD align=middle width="42%"><B>Meaning in Unicode builds</B></TD></TR>, ^# U6 L8 J7 }$ Y! P
<TR> ; L; {5 E1 G2 ^$ e7 m<TD width="16%">WCHAR</TD># O" S. g0 P2 M: P9 |# B$ `: c, z/ C/ ]
<TD width="42%">wchar_t</TD>. u3 s {- m! f$ g
<TD width="42%">wchar_t</TD></TR> . T% }& w, t" X# f# Y; _6 L1 g7 |* n<TR> F9 ^: w) R/ x. C/ E" o% I/ y
<TD width="16%">LPSTR </TD> 4 `& W& Q% O5 `/ O4 X<TD width="42%">zero-terminated string of char (char*)</TD>5 L9 @ G. s( X! J& N
<TD width="42%">zero-terminated string of char (char*)</TD></TR> + ]4 o' X& o$ {1 H<TR> / V1 b5 ]% t" ~ G) T \<TD width="16%">LPCSTR </TD> 7 M' E( f0 l, j6 Y( c<TD width="42%">constant zero-terminated string of char (const char*)</TD> 8 O* H$ K1 B; B( Q5 {& i( \<TD width="42%">constant zero-terminated string of char (const char*)</TD></TR> 2 m6 y3 R! X& W R6 ]<TR> ' F! K7 m6 w) [* x. U<TD width="16%">LPWSTR</TD>% K: p+ ?- v4 F4 o, F
<TD width="42%">zero-terminated Unicode string (wchar_t*) </TD> ( c2 ^+ R+ ^) R/ y& @' O5 E/ {<TD width="42%">zero-terminated Unicode string (wchar_t*)</TD></TR>0 C& H4 Y4 C- E1 C- w
<TR> : N0 L* w; y$ Z+ v; p<TD width="16%">LPCWSTR</TD>: {! ~& ~3 h, X
<TD width="42%">constant zero-terminated Unicode string (const wchar_t*)</TD>/ c4 M+ e) s- E$ X
<TD width="42%">constant zero-terminated Unicode string (const wchar_t*) </TD></TR>5 _3 f/ n( h1 x Q) \/ @1 z
<TR>' a( e7 d9 h) G8 t* s9 K
<TD width="16%"><XXXXIME xime="7">TCHAR</XXXXIME></TD>" z/ z6 B. \: J) G) e8 Z y O$ M
<TD width="42%"><XXXXIME xime="7">char</XXXXIME></TD> ' {2 U7 }1 B# V$ F; f5 d1 l<TD width="42%"><XXXXIME xime="7">wchar_t</XXXXIME></TD></TR> 0 X* G J4 \& M! y<TR>% O: M7 i; c% D+ D( {. C+ {
<TD width="16%">LPTSTR</TD>* | {, U9 Q* x
<TD width="42%">zero-terminated string of TCHAR (TCHAR*) </TD> ! e+ H. U3 F) P* y5 h2 f# ^$ B<TD width="42%">zero-terminated string of TCHAR (TCHAR*)</TD></TR>7 X$ l* |1 t) j; O9 H
<TR>6 M" |! ^, k# f) s' {
<TD width="16%">LPCTSTR </TD> $ _& D) u2 p2 | ?) r5 _, {<TD width="42%">constant zero-terminated string of TCHAR (const TCHAR*)</TD> ) K& `, ~; Y7 q. Y' l<TD width="42%">constant zero-terminated string of TCHAR (const TCHAR*)</TD></TR></TABLE>7 E# F0 X$ g5 g' J
<P><IMG src="http://www.vckbase.com/document/image/paragraph.gif"><B> 何时使用 TCHAR 和 Unicode</B> - v# E" }, X" Y' R9 P z8 E/ f" L/ ?* j* _0 R/ }4 t
到现在,你可能会问,我们为什么要使用Unicode。我已经用了很多年的char。下列3种情况下,使用Unicode将会使你受益:</P>+ j; K# y1 S& x9 Q9 \0 B
<DIR>- j* U2 x. a3 _/ {( Y3 v" J4 ]
<LI>1.你的程序只运行在Windows NT系统中。 0 `/ O% |) X @. z# R8 v
<LI>2. 你的程序需要处理超过MAX_PATH个字符长的文件名。 0 B4 J& ]7 J$ i' X9 D4 E
<LI>3. 你的程序需要使用XP中引入的只有Unicode版本的API. </LI></DIR>/ w* R5 B0 S/ w% s! p, f) o. f
<P> Windows 9x 中大多数的 API 没有实现 Unicode 版本。所以,如果你的程序要在windows 9x中运行,你必须使用MBCS APIs。然而,由于NT系统内部都使用Unicode,所以使用Unicode APIs将会加快你的程序的运行速度。每次,你传递一个字符串调用MBCS API,操作系统会把这个字符串转换成Unicode字符串,然后调用对应的Unicode API。如果一个字符串被返回,操作系统还要把它转变回去。尽管这个转换过程被高度优化了,但它对速度造成的损失是无法避免的。 ! u& k! b. J6 j1 s! | 只要你使用Unicode API,NT系统允许使用非常长的文件名(突破了MAX_PATH的限制,MAX_PATH=260)。使用Unicode API的另一个优点是你的程序会自动处理用户输入的各种语言。所以一个用户可以输入英文,中文或者日文,而你不需要额外编写代码去处理它们。* T7 \" V' Y* s# L4 T: g6 f
最后,随着windows 9x产品的淡出,微软似乎正在抛弃MBCS APIs。例如,包含两个字符串参数的SetWindowTheme() API只有Unicode版本的。使用Unicode来build你的程序将会简化字符串的处理,你不必在MBCS和Unicdoe之间相互转换。% b ?* O5 R( s3 [3 \% d
即使你现在不使用Unicode来build你的程序,你也应该使用TCHAR及其相关的宏。这样做不仅可以的代码可以很好地处理DBCS,而且如果将来你想用Unicode来build你的程序,你只需要改变一下预处理器中的设置就可以实现了。 , V3 ~- q9 Q' M& x/ f+ a: r& E! A6 {3 }! h. E3 e' O& i5 U( I& y
</P> 5 v- Y Y& Y* I" \; K6 [6 I+ b% s/ f