⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 subject_21948.htm

📁 一些关于vc的问答
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<blockquote><p>
<font color=red>答案被接受</font><br>回复者:徐景周 回复日期:2002-11-21 14:20:03
<br>内容:CString to BSTR&nbsp;&nbsp;<BR>When programming with ActiveX, you will sometimes need a value represented as a type BSTR. A BSTR is a counted string, a wide-character (Unicode) string on Intel platforms and can contain embedded NUL characters.&nbsp;&nbsp;<BR>You can convert at CString to a BSTR by calling the CString method AllocSysString: <BR>CString s;<BR>s = ... ; // whatever<BR>BSTR b = s.AllocSysString()<BR> The pointer b points to a newly-allocated BSTR object which is a copy of the CString, including the terminal NUL character. This may now be passed to whatever interface you are calling that requires a BSTR. Normally, a BSTR is disposed of by the component receiving it. If you should need to dispose of a BSTR, you must use the call <BR>::SysFreeString(b);<BR>to free the string. <BR>The story is that the decision of how to represent strings sent to ActiveX controls resulted in some serious turf wars within Microsoft. The Visual Basic people won, and the string type BSTR (acronym for "Basic String") was the result. <BR>BSTR to CString&nbsp;&nbsp;<BR> Since a BSTR is a counted Unicode string, you can use standard conversions to make an 8-bit CString. Actually, this is built-in; there are special constructors for converting ANSI strings to Unicode and vice-versa. You can also get BSTRs as results in a VARIANT type, which is a type returned by various COM and Automation calls. <BR>For example, if you do, in an ANSI application, <BR>BSTR b;<BR>b = ...; // whatever<BR>CString s(b == NULL ? L"" : s)<BR>works just fine for a single-string BSTR, because there is a special constructor that takes an LPCWSTR (which is what a BSTR is) and converts it to an ANSI string. The special test is required because a BSTR could be NULL, and the constructors Don't Play Well with NULL inputs (thanks to Brian Ross for pointing this out!). This also only works for a BSTR that contains only a single string terminated with a NUL; you have to do more work to convert strings that contain multiple NUL characters. Note that embedded NUL characters generally don't work well in CStrings and generally should be avoided. <BR>Remember, according to the rules of C/C++, if you have an LPWSTR it will match a parameter type of LPCWSTR (it doesn't work the other way!). <BR>In UNICODE mode, this is just the constructor <BR>&lt;code&gt;CString::CString(LPCTSTR);<BR>As indicated above, in ANSI mode there is a special constructor for <BR>&lt;code&gt;CString::CString(LPCWSTR); <BR>this calls an internal function to convert the Unicode string to an ANSI string. (In Unicode mode there is a special constructor that takes an LPCSTR, a pointer to an 8-bit ANSI string, and widens it to a Unicode string!). Again, note the limitation imposed by the need to test for a BSTR value which is NULL. <BR>There is an additional problem as pointed out above: BSTRs can contain embedded NUL characters; CString constructors can only handle single NUL characters in a string. This means that CStrings will compute the wrong length for a string which contains embedded NUL bytes. You need to handle this yourself. If you look at the constructors in strcore.cpp, you will see that they all do an lstrlen or equivalent to compute the length.&nbsp;&nbsp;<BR>Note that the conversion from Unicode to ANSI uses the ::WideCharToMultiByte conversion with specific arguments that you may not like. If you want a different conversion than the default, you have to write your own. <BR>If you are compiling as UNICODE, then it is a simple assignment: <BR>&lt;code&gt;CString convert(BSTR b)<BR>&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;if(b == NULL)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return CString(_T(""));<BR>&nbsp;&nbsp;&nbsp;&nbsp;CString s(b); // in UNICODE mode<BR>&nbsp;&nbsp;&nbsp;&nbsp;return s;<BR>&nbsp;&nbsp; }<BR>If you are in ANSI mode, you need to convert the string in a more complex fashion. This will accomplish it. Note that this code uses the same argument values to ::WideCharToMultiByte that the implicit constructor for CString uses, so you would use this technique only if you wanted to change these parameters to do the conversion in some other fashion, for example, specifying a different default character, a different set of flags, etc. <BR>&lt;code&gt;CString convert(BSTR b)<BR>&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;CString s;<BR>&nbsp;&nbsp;&nbsp;&nbsp;if(b == NULL)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return s; // empty for NULL BSTR<BR>#ifdef UNICODE<BR>&nbsp;&nbsp;&nbsp;&nbsp;s = b;<BR>#else<BR>&nbsp;&nbsp;&nbsp;&nbsp;LPSTR p = s.GetBuffer(SysStringLen(b) + 1); <BR>&nbsp;&nbsp;&nbsp;&nbsp;::WideCharToMultiByte(CP_ACP,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ANSI Code Page<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // no flags<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // source widechar string<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-1,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// assume NUL-terminated<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // target buffer<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SysStringLen(b)+1, // target buffer length<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// use system default char<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // don't care if default used<BR>&nbsp;&nbsp;&nbsp;&nbsp;s.ReleaseBuffer();<BR>#endif<BR>&nbsp;&nbsp;&nbsp;&nbsp;return s;<BR>&nbsp;&nbsp; }<BR>Note that I do not worry about what happens if the BSTR contains Unicode characters that do not map to the 8-bit character set, because I specify NULL as the last two parameters. This is the sort of thing you might want to change. <BR>VARIANT to CString <BR>Actually, I've never done this; I don't work in COM/OLE/ActiveX where this is an issue. But I saw a posting by Robert Quirk on the microsoft.public.vc.mfc newsgroup on how to do this, and it seemed silly not to include it in this essay, so here it is, with a bit more explanation and elaboration. Any errors relative to what he wrote are my fault. <BR>A VARIANT is a generic parameter/return type in COM programming. You can write methods that return a type VARIANT, and which type the function returns may (and often does) depend on the input parameters to your method (for example, in Automation, depending on which method you call, IDispatch::Invoke may return (via one of its parameters) a VARIANT which holds a BYTE, a WORD, an float, a double, a date, a BSTR, and about three dozen other types (see the specifications of the VARIANT structure in the MSDN). In the example below, it is assumed that the type is known to be a variant of type BSTR, which means that the value is found in the string referenced by bstrVal.&nbsp;&nbsp;This takes advantage of the fact that there is a constructor which, in an ANSI application, will convert a value referenced by an LPCWCHAR to a CString (see BSTR-to-CString). In Unicode mode, this turns out to be the normal CString constructor. See the caveats about the default ::WideCharToMultibyte conversion and whether or not you find these acceptable (mostly, you will). <BR>VARIANT vaData;<BR><BR>vaData = m_com.YourMethodHere();<BR>ASSERT(vaData.vt == VT_BSTR);<BR><BR>CString strData(vaData.bstrVal);<BR>Note that you could also make a more generic conversion routine that looked at the vt field. In this case, you might consider something like: <BR>CString VariantToString(VARIANT * va)<BR>&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;CString s;<BR>&nbsp;&nbsp;&nbsp;&nbsp;switch(va-&gt;vt)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ /* vt */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case VT_BSTR:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return CString(vaData-&gt;bstrVal);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case VT_BSTR | VT_BYREF:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return CString(*vaData-&gt;pbstrVal);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case VT_I4:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.Format(_T("%d"), va-&gt;lVal);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return s;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case VT_I4 | VT_BYREF:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.Format(_T("%d"), *va-&gt;plVal);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case VT_R8:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.Format(_T("%f"), va-&gt;dblVal);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return s;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ... remaining cases left as an Exercise For The Reader<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return CString("");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} /* vt */<BR>&nbsp;&nbsp; }<BR>Loading STRINGTABLE values <BR>If you want to create a program that is easily ported to other languages, you must not include native-language strings in your source code. (For these examples, I'll use English, since that is my native language (aber Ich kann ein bischen Deutsch sprechen). So it is very bad practice to write <BR>CString s = "There is an error";<BR>Instead, you should put all your language-specific strings (except, perhaps, debug strings, which are never in a product deliverable). This means that is fine to write <BR>s.Format(_T("%d - %s"), code, text);<BR>in your program; that literal string is not language-sensitive. However, you must be very careful to not use strings like <BR>// fmt is "Error in %s file %s"<BR>// readorwrite is "reading" or "writing"<BR>s.Format(fmt, readorwrite, filename); <BR>I speak of this from experience. In my first internationalized application I made this error, and in spite of the fact that I know German, and that German word order places the verb at the end of a sentence, I had done this. Our German distributor complained bitterly that he had to come up with truly weird error messages in German to get the format codes to do the right thing. It is much better (and what I do now) to have two strings, one for reading and one for writing, and load the appropriate one, making them string parameter-insensitive, that is, instead of loading the strings "reading" or "writing", load the whole format: <BR>// fmt is "Error in reading file %s"<BR>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Error in writing file %s"<BR>s.Format(fmt, filename);<BR>Note that if you have more than one substitution, you should make sure that if the word order of the substitutions does not matter, for example, subject-object, subject-verb, or verb-object, in English. <BR>For now, I won't talk about FormatMessage, which actually is better than sprintf/Format, but is poorly integrated into the CString class. It solves this by naming the parameters by their position in the parameter list and allows you to rearrange them in the output string.&nbsp;&nbsp;<BR>So how do we accomplish all this? By storing the string values in the resource known as the STRINGTABLE in the resource segment. To do this, you must first create the string, using the Visual Studio resource editor. A string is given a string ID, typically starting IDS_. So you have a message, you create the string and call it IDS_READING_FILE and another called IDS_WRITING_FILE. They appear in your .rc file as <BR>STRINGTABLE<BR>&nbsp;&nbsp;&nbsp;&nbsp;IDS_READING_FILE "Reading file %s"<BR>&nbsp;&nbsp;&nbsp;&nbsp;IDS_WRITING_FILE "Writing file %s"<BR>END<BR>Note: these resources are always stored as Unicode strings, no matter what your program is compiled as. They are even Unicode strings on Win9x platforms, which otherwise have no real grasp of Unicode (but they do for resources!). Then you go to where you had stored the strings&nbsp;&nbsp;<BR>// previous code<BR>&nbsp;&nbsp; CString fmt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(...)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fmt = "Reading file %s";<BR>&nbsp;&nbsp;&nbsp;&nbsp; else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt = "Writing file %s";<BR>&nbsp;&nbsp;...<BR>&nbsp;&nbsp;&nbsp;&nbsp;// much later<BR>&nbsp;&nbsp;CString s;<BR>&nbsp;&nbsp;s.Format(fmt, filename);<BR>and instead do <BR>// revised code<BR>&nbsp;&nbsp;&nbsp;&nbsp;CString fmt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(...)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.LoadString(IDS_READING_FILE);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fmt.LoadString(DS_WRITING_FILE);<BR>&nbsp;&nbsp;&nbsp;&nbsp;...<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// much later<BR>&nbsp;&nbsp;&nbsp;&nbsp;CString s;<BR>&nbsp;&nbsp;&nbsp;&nbsp;s.Format(fmt, filename);<BR>Now your code can be moved to any language. The LoadString method takes a string ID and retrieves the STRINGTABLE&nbsp;&nbsp;value it represents, and assigns that value to the CString.&nbsp;&nbsp;<BR>There is a clever feature of the CString constructor that simplifies the use of STRINGTABLE entries. It is not explicitly documented in the CString::CString specification, but is obscurely shown in the example usage of the constructor! (Why this couldn't be part of the formal documentation and has to be shown in an example escapes me!). The feature is that if you cast a STRINGTABLE ID to an LPCTSTR it will implicitly do a LoadString. Thus the following two examples of creating a string value produce the same effect, and the ASSERT will not trigger in debug mode compilations: <BR>CString s;<BR>s.LoadString(IDS_WHATEVER);<BR>CString t( (LPCTSTR)IDS_WHATEVER);<BR>ASSERT(s == t);<BR>Now, you may say, how can this possibly work? How can it tell a valid pointer from a STRINGTABLE ID? Simple: all string IDs are in the range 1..65535. This means that the high-order bits of the pointer will be 0. Sounds good, but what if I have valid data in a low address? Well, the answer is, you can't. The lower 64K of your address space will never, ever, exist. Any attempt to access a value in the address range 0x00000000 through 0x0000FFFF (0..65535) will always and forever give an access fault. These addresses are never, ever valid addresses. Thus a value in that range (other than 0) must necessarily represent a STRINGTABLE ID. <BR>I tend to use the MAKEINTRESOURCE macro to do the casting. I think it makes the code clearer regarding what is going on. It is a standard macro which doesn't have much applicability otherwise in MFC. You may have noted that many methods take either a UINT or an LPCTSTR as parameters, using C++ overloading. This gets us around the ugliness of pure C where the "overloaded" methods (which aren't really overloaded in C) required explicit casts. This is also useful in assigning resource names to various other structures. <BR>CString s;<BR>s.LoadString(IDS_WHATEVER);<BR>CString t( MAKEINTRESOURCE(IDS_WHATEVER));<BR>ASSERT(s == t);<BR>Just to give you an idea: I practice what I preach here. You will rarely if ever find a literal string in my program, other than the occasional debug output messages, and, of course, any language-independent string. <BR>
<br>
<a href="javascript:history.go(-1)">返回上页</a><br><a href=http://www.copathway.com/cndevforum/>访问论坛</a></p></blockquote>
<hr size=1>
<blockquote><p>
回复者:超超 回复日期:2002-11-21 14:58:56
<br>内容:不管怎样还是要说声谢谢!但是是否能够提供中文的资料!
<br>
<a href="javascript:history.go(-1)">返回上页</a><br><a href=http://www.copathway.com/cndevforum/>访问论坛</a></p></blockquote>
<hr size=1>
<blockquote><p>
回复者:徐景周 回复日期:2002-11-21 15:10:40
<br>内容:已发到你信箱中
<br>
<a href="javascript:history.go(-1)">返回上页</a><br><a href=http://www.copathway.com/cndevforum/>访问论坛</a></p></blockquote>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -