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

📄 article3.htm

📁 The code for this article was written for version 1.0 of the Active Template Library (ATL). The cu
💻 HTM
📖 第 1 页 / 共 5 页
字号:
                          [out] BSTR * pbsOut);
</FONT></PRE><P>We talked about <i>in</i> and <i>out</i> parameters in Article 1, but at that point they were primarily documentation. With the BSTR type (as well as with VARIANT and SAFEARRAY) you had better code your DLL functions to match their ODL declarations. Otherwise, your disagreements with Visual Basic can have drastic consequences. 
<P>The purpose of an <i>in </i>parameter (such as <i>bsIn</i>)<i> </i>is to pass a copy of a string for you to read or copy. It's not yours, so don't mess with the contents. The purpose of an <i>in/out </i>parameter (such as <i>pbsInOut</i>)<i> </i>is to pass you some input and receive output from you. Do what you want with it. Modify its contents, copy it to another String, or pass a completely different string back through its pointer. The purpose of an out parameter (such as <i>pbsOut</i>) is to receive an output string from you. There's nothing there on input, but there had better be something there (if only a NULL) when you leave the function, because Visual Basic will be counting on receiving something.
<P><h4>String Constructors</H4>
<P>Once you receive your BSTRs, you need to convert them to String. You can also create brand new Strings to return through the return value or out parameters, or just to serve as temporary work space. The String constructors create various kinds of strings: 
<P><PRE><FONT FACE="COURIER" SIZE="2">// Constructors
String sTmp;                    // Uninitialized
String sIn = bsIn;              // In argument from BSTR
String sCopy = *pbsInOut;       // In/out argument from BSTR
String sString = sIn;           // One String from another
String sChar(1, WCHAR('A'));    // A single character
String sChars(30, WCHAR('B'));  // A filled buffer 
String sBuf(30);                // An uninitialized buffer
String sWide = _W(&quot;Wide&quot;);      // From Unicode string
String sNarrow = &quot;Narrow&quot;;      // From ANSI string
String sNative = _T(&quot;Native&quot;);  // From native string
String sRet;
</FONT></PRE><P>Most of these speak for themselves, but notice the WCHAR casts and the use of the <b>_W</b> macro to initialize with a wide-character constant. When initializing Strings with constants, you should always use Unicode characters or strings. The String type will just have to convert your ANSI strings to Unicode anyway. Conversion is a necessary evil if you have an ANSI character string variable, but if you have a constant, you can save run-time processing by making it a Unicode string to start with. <P>Unfortunately, you can't just initialize a String with a Unicode string like this: <P><PRE><FONT FACE="COURIER" SIZE="2">String s = L&quot;Test&quot;;
</FONT></PRE><P>The problem is that the String type has a BSTR constructor and a LPCWSTR constructor, but what you'll get here is an LPWSTR and there's no separate constructor for that. There can't be a separate constructor because to C++, a BSTR looks the same as an LPWSTR, but of course internally it's very different. Any time you assign a wide character string to a String, you must cast it to an LPCWSTR so that it will go through the right constructor. The <b>_W</b> macro casts to LPCWSTR unobtrusively. C++ is a very picky language, and the String class seems to hit the edges of the pickiness in a lot of places. You have to develop very careful habits to use it effectively. <P><blockquote><b>Note:</b>     Many of the problems in writing a String class are caused by Unicode confusion, and much of that confusion comes from the fact that in most current compilers the wchar_t type (called WCHAR in this article) is a typedef to an unsigned short rather than an intrinsic type. Overloaded functions are a critical part of designing a safe, convenient class in C++, but when overloading, C++ considers a typedef to be a simple alias rather than a unique type. A constructor overloaded to take a WCHAR type actually sees an unsigned short, which may conflict with other overloaded integer constructors. Debuggers won't know whether to display a WCHAR pointer as a string or as an array of unsigned shorts. Compile-time error messages will display confusing errors showing unsigned short rather than the character type you thought you were using. If you're fortunate enough to use a compiler that provides wchar_t as an intrinsic type, you won't see these problems. Unfortunately, Microsoft Visual C++ is not yet among those compilers.</blockquote><P><h4>String Assignment</H4><P>As you already know (or had better find out soon if you're going to program in C++), initialization is a very different thing from assignment, even though the syntax may look similar. The String type provides the assignments you expect through the operator= function:<P><PRE><FONT FACE="COURIER" SIZE="2">// Assignment
WCHAR wsz[] = L&quot;Wide&quot;;
char sz[] = &quot;Narrow&quot;;
sTmp = sIn;                     // From another String variable
sTmp = _W(&quot;Wide&quot;);              // From Unicode literal string
sTmp = WCHAR('W');              // From Unicode character
sTmp = LPCWSTR(wsz);            // From Unicode string variable
sTmp = LPCSTR(sz);              // From ANSI string variable
</FONT></PRE><P>Again, you have to jump through some hoops to make sure your wide-character string assignments go through the proper const operator. C++ can't tell the difference between a wide-character string and a BSTR, so you have to tell it. Generally, you should avoid doing anything with ANSI character strings. The String type can handle ANSI strings, but you just end up sending a whole lot of zeros to and from nowhere. The only reason to use ANSI strings is to pass them to API functions or to C run-time functions, and you normally shouldn't do the latter either, because it's much more efficient to use the <b>wsc</b><i><B>xxx</b></i> versions of the run-time functions. <P><h4>String Returns</H4><P>Let's skip all the cool things you can do to massage String variables and go to the end of <b>TestString</b> where you return your Strings: <P><PRE><FONT FACE="COURIER" SIZE="2">    // Return through out parameters.
    sTmp = _W(&quot;...send me back&quot;);
    *pbsInOut = sTmp;
    *pbsOut = _B(&quot;Out of the fire&quot;);

    // Return value
    return sRet;

  } catch(Long err) {
    HandleError(err);
  } 
}
</FONT></PRE><P>In the first line we assign a wide string to the temporary variable (<i>sTmp</i>) and then assign <i>sTmp</i> to the BSTR out parameter (<i>pbsInOut</i>). A BSTR conversion operator in the String type enables you to perform the assignment of the wide string stored in <i>sTmp</i> to the BSTR out parameter, <i>pbsInOut</i>. The second assignment does the same thing, but uses the <b>_B</b> macro to create and destroy a temporary String variable on the stack. The <b>_B</b> macro uses a double typecast and token paste to hide the following atrocity: <P><PRE><FONT FACE="COURIER" SIZE="2">*pbsOut = String(LPCWSTR(L&quot;Out of the fire&quot;));
</FONT></PRE><P>Finally, the return value is set to the <i>sRet </i>variable containing the string that we'll build in the next section. Internally, the return works exactly like the assignment to an out parameter and in fact calls the same BSTR conversion operator. Think of the Basic syntax:<P><PRE><FONT FACE="COURIER" SIZE="2">TestString = sRet
</FONT></PRE><P>This gives you a better picture of what actually happens in a C++ return statement. <P><h3>A String Workout</h3><P>There's a lot more to the String type than initialization and assignment. It's designed to be a full-featured string package--duplicating most of the functions you find in the C run-time library or in popular string classes such as MFC's <b>CString</b>. You won't find everything you could ever need, but conversion operators make it easy to pass Strings to run-time string functions. Or better yet, whenever you want to do something that isn't directly supported, add it to the library and send me the code. Be sure to use the <b>wsc</b><i><B>xxx</b></i> version of run-time library calls. <P>The <b>TestString</b> function uses the iostream library to build a formatted string that tests the String methods and operators, and then assigns that string to the return value. Here's how it works: <P><PRE><FONT FACE="COURIER" SIZE="2">ostrstream ostr;

ostr &lt;&lt; endcl &lt;&lt; &quot;Test length and resize:&quot; &lt;&lt; endcl;
sTmp = _W(&quot;Yo!&quot;);
ostr &lt;&lt; &quot;sTmp = _W(\&quot;Yo!\&quot;); // sTmp==\&quot;&quot; &lt;&lt; sTmp 
     &lt;&lt; &quot;\&quot;, &quot; &lt;&lt; &quot;sTmp.Length()==&quot; &lt;&lt; sTmp.Length() &lt;&lt; endcl;
.
.
.
ostr &lt;&lt; ends;
char * pch = ostr.str();
sRet = pch;
delete[] pch;
</FONT></PRE><P>The String class defines an iostream insertion operator (&lt;&lt;) so that you can easily insert ANSI character strings (converting from Unicode BSTRs) into an output stream. Notice that I also use a custom <i>endcl</i> manipulator rather than the standard <i>endl </i>manipulator. My version inserts a carriage return/line feed sequence rather than the standard line feed only. <P>You can study up on iostream and check the code if this isn't clear. The point here is to show off String features, not the iostream library. The rest of this section will show chunks of output that put the String type through its paces. <P><h4>Length Methods</H4><P>We'll start with the length-related methods: <P><PRE><FONT FACE="COURIER" SIZE="2">sTmp = _W(&quot;Yo!&quot;); // sTmp==&quot;Yo!&quot;, sTmp.Length()==3
sTmp.Resize(20);  // sTmp==&quot;Yo!&quot;, sTmp.Length()==20, sTmp.LengthZ()==3
sTmp.ResizeZ();   // sTmp==&quot;Yo!&quot;, sTmp.Length()==3 
</FONT></PRE>
<P>The <b>Length()</b> method always returns the real length of the String regardless of nulls, while <b>LengthZ()</b> returns the length to the first null. Normally you'll Resize to truncate a string to a specified length, but you can also expand a string to create a buffer, then truncate back to the first null after passing the buffer to an API function. <P><h4>Empty Strings and Comparisons</H4><P>Internally, a String, like a BSTR, can be either a NULL string or an empty string, although Basic treats these the same. The String type provides methods to test and set this state:<P><PRE><FONT FACE="COURIER" SIZE="2">sTmp = &quot;Empty&quot;;   // sTmp==&quot;Empty&quot;,sTmp.IsEmpty==0, sTmp.IsNull==0
sTmp.Empty();     // sTmp==&quot;&quot;,sTmp.IsEmpty==1, sTmp.IsNull==0
sTmp.Nullify();   // sTmp==&quot;&quot;,sTmp.IsEmpty==1, sTmp.IsNull==1 
</FONT></PRE><P>In the Basic tradition, the <b>IsEmpty()</b> method returns True if the string is either null or empty. That's generally all you need to know. Many C++ run-time functions can't handle null strings, and some API functions can't handle empty strings. So you can use the <b>IsNull()</b> function to identify a null string. There's no direct way to identify what C++ thinks of as an empty string, but the following expression will work: <P><PRE><FONT FACE="COURIER" SIZE="2">sTmp.IsEmpty() &amp;&amp; !sTmp.IsNull()
</FONT></PRE><P>Of course, you can test equality to empty or any other value with logical operators. If <i>sTmp </i>is empty (in either sense), the String == operator will return True for <i>(sTmp == BNULL) </i>or for <i>(sTmp == _B(&quot;&quot;))</i>. Notice how cast macros are used to convert literals to Strings before comparison. You can also test comparisons with expressions such as:
<P><PRE><FONT FACE="COURIER" SIZE="2">(sNarrow &gt;= sWide)

⌨️ 快捷键说明

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