CString convert(BSTR b) { if(b == NULL) return CString(_T("")); CString s(b); // in UNICODE mode return s; }
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.
![](https://i-blog.csdnimg.cn/blog_migrate/74fd2798202fde65c452efa4582b9818.gif)
CString convert(BSTR b) { CString s; if(b == NULL) return s; // empty for NULL BSTR #ifdef UNICODE s = b; #else LPSTR p = s.GetBuffer(SysStringLen(b) + 1); ::WideCharToMultiByte(CP_ACP, // ANSI Code Page 0, // no flags b, // source widechar string -1, // assume NUL-terminated p, // target buffer SysStringLen(b)+1, // target buffer length NULL, // use system default char NULL); // don't care if default used s.ReleaseBuffer(); #endif return s; }
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.
VARIANT to CString
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.
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
. 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).
VARIANT vaData; vaData = m_com.YourMethodHere(); ASSERT(vaData.vt == VT_BSTR); CString strData(vaData.bstrVal);
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:
CString VariantToString(VARIANT * va) { CString s; switch(va->vt) { /* vt */ case VT_BSTR: return CString(vaData->bstrVal); case VT_BSTR | VT_BYREF: return CString(*vaData->pbstrVal); case VT_I4: s.Format(_T("%d"), va->lVal); return s; case VT_I4 | VT_BYREF: s.Format(_T("%d"), *va->plVal); case VT_R8: s.Format(_T("%f"), va->dblVal); return s; ... remaining cases left as an Exercise For The Reader default: ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional) return CString(""); } /* vt */ }
Loading STRINGTABLE values
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
CString s = "There is an error";
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
s.Format(_T("%d - %s"), code, text);
in your program; that literal string is not language-sensitive. However, you must be very careful to not use strings like
// fmt is "Error in %s file %s" // readorwrite is "reading" or "writing" s.Format(fmt, readorwrite, filename);
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:
// fmt is "Error in reading file %s" // "Error in writing file %s" s.Format(fmt, filename);
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.
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.
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
STRINGTABLE IDS_READING_FILE "Reading file %s" IDS_WRITING_FILE "Writing file %s" END
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
// previous code CString fmt; if(...) fmt = "Reading file %s"; else fmt = "Writing file %s"; ... // much later CString s; s.Format(fmt, filename);
and instead do
// revised code CString fmt; if(...) fmt.LoadString(IDS_READING_FILE); else fmt.LoadString(DS_WRITING_FILE); ... // much later CString s; s.Format(fmt, filename);
Now your code can be moved to any language. The LoadString
method takes a string ID and retrieves the STRINGTABLE
value it represents, and assigns that value to the CString
.
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:
CString s; s.LoadString(IDS_WHATEVER); CString t( (LPCTSTR)IDS_WHATEVER); ASSERT(s == t);
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.
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.
CString s; s.LoadString(IDS_WHATEVER); CString t( MAKEINTRESOURCE(IDS_WHATEVER)); ASSERT(s == t);
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.
CStrings and temporary objects
Here's a little problem that came up on the microsoft.public.vc.mfc
newsgroup a while ago. I'll simplify it a bit. The basic problem was the programmer wanted to write a string to the Registry. So he wrote:
I am trying to set a registry value using
RegSetValueEx()
and it is the value that I am having trouble with. If I declare a variable ofchar[]
it works fine. However, I am trying to convert from aCString
and I get garbage. "ÝÝÝÝ...ÝÝÝÝÝÝ" to be exact. I have triedGetBuffer
, typecasting tochar*
,LPCSTR
. The return ofGetBuffer
(from debug) is the correct string but when I assign it to achar*
(orLPCSTR
) it is garbage. Following is a piece of my code:char* szName = GetName().GetBuffer(20); RegSetValueEx(hKey, "Name", 0, REG_SZ, (CONST BYTE *) szName, strlen (szName + 1));The
Name
string is less then 20 chars long, so I don't think theGetBuffer
parameter is to blame. It is very frustrating and any help is appreciated.
Dear Frustrated,
You have been done in by a fairly subtle error, caused by trying to be a bit too clever. What happened was that you fell victim to knowing too much. The correct code is shown below:
CString Name = GetName(); RegSetValueEx(hKey, _T("Name"), 0, REG_SZ, (CONST BYTE *) (LPCTSTR)Name, (Name.GetLength() + 1) * sizeof(TCHAR));
Here's why my code works and yours didn't. When your function GetName
returned a CString
, it returned a "temporary object". See the C++ Reference manual §12.2.
In some circumstances it may be necessary or convenient for the compiler to generate a temporary object. Such introduction of temporaries is implementation dependent. When a compiler introduces a temporary object of a class that has a constructor it must ensure that a construct is called for the temporary object. Similarly, the destructor must be called for a temporary object of a class where a destructor is declared.
The compiler must ensure that a temporary object is destroyed. The exact point of destruction is implementation dependent....This destruction must take place before exit from the scope in which the temporary is created.
Most compilers implement the implicit destructor for a temporary at the next program sequencing point following its creation, that is, for all practical purposes, the next semicolon. Hence the CString
existed when the GetBuffer
call was made, but was destroyed following the semicolon. (As an aside, there was no reason to provide an argument to GetBuffer
, and the code as written is incorrect since there is no ReleaseBuffer
performed). So what GetBuffer
returned was a pointer to storage for the text of the CString
. When the destructor was called at the semicolon, the basic CString
object was freed, along with the storage that had been allocated to it. The MFC debug storage allocator then rewrites this freed storage with 0xDD, which is the symbol "Ý". By the time you do the write to the Registry, the string contents have been destroyed.
There is no particular reason to need to cast the result to a char *
immediately. Storing it as a CString
means that a copy of the result is made, so after the temporary CString
is destroyed, the string still exists in the variable's CString
. The casting at the time of the Registry call is sufficient to get the value of a string which already exists.
In addition, my code is Unicode-ready. The Registry call wants a byte count. Note also that the call lstrlen(Name+1)
returns a value that is too small by 2 for an ANSI string, since it doesn't start until the second character of the string. What you meant to write was lstrlen(Name) + 1
(OK, I admit it, I've made the same error!). However, in Unicode, where all characters are two bytes long, we need to cope with this. The Microsoft documentation is surprisingly silent on this point: is the value given for REG_SZ
values a byte count or a character count? I'm assuming that their specification of "byte count" means exactly that, and you have to compensate.
CString Efficiency
One problem of CString
is that it hides certain inefficiencies from you. On the other hand, it also means that it can implement certain efficiencies. You may be tempted to say of the following code
CString s = SomeCString1;
s += SomeCString2;
s += SomeCString3;
s += ",";
s += SomeCString4;
that it is horribly inefficient compared to, say
char s[1024]; lstrcpy(s, SomeString1); lstrcat(s, SomeString2); lstrcat(s, SomeString 3); lstrcat(s, ","); lstrcat(s, SomeString4);
After all, you might think, first it allocates a buffer to hold SomeCString1
, then copies SomeCString1
to it, then detects it is doing a concatenate, allocates a new buffer large enough to hold the current string plus SomeCString2
, copies the contents to the buffer and concatenates the SomeCString2
to it, then discards the first buffer and replaces the pointer with a pointer to the new buffer, then repeats this for each of the strings, being horribly inefficient with all those copies.
The truth is, it probably never copies the source strings (the left side of the +=) for most cases.
In VC++ 6.0, in Release mode, all CString
buffers are allocated in predefined quanta. These are defined as 64, 128, 256, and 512 bytes. This means that unless the strings are very long, the creation of the concatenated string is an optimized version of a strcat
operation (since it knows the location of the end of the string it doesn't have to search for it, as strcat
would; it just does a memcpy
to the correct place) plus a recomputation of the length of the string. So it is about as efficient as the clumsier pure-C code, and one whole lot easier to write. And maintain. And understand.
Those of you who aren't sure this is what is really happening, look in the source code for CString
, strcore.cpp, in the mfc/src subdirectory of your vc98 installation. Look for the method ConcatInPlace
which is called from all the += operators.
Aha! So CString
isn't really "efficient!" For example, if I create
CString cat("Mew!");
then I don't get a nice, tidy little buffer 5 bytes long (4 data bytes plus the terminal NUL
). Instead the system wastes all that space by giving me 64 bytes and wasting 59 of them.
If this is how you think, be prepared to reeducate yourself. Somewhere in your career somebody taught you that you always had to use as little space as possible, and this was a Good Thing.
This is incorrect. It ignores some seriously important aspects of reality.
If you are used to programming embedded applications with 16K EPROMs, you have a particular mindset for doing such allocation. For that application domain, this is healthy. But for writing Windows applications on 500MHz, 256MB machines, it actually works against you, and creates programs that perform far worse than what you would think of as "less efficient" code.
For example, size of strings is thought to be a first-order effect. It is Good to make this small, and Bad to make it large. Nonsense. The effect of precise allocation is that after a few hours of the program running, the heap is cluttered up with little tiny pieces of storage which are useless for anything, but they increase the storage footprint of your application, increase paging traffic, can actually slow down the storage allocator to unacceptable performance levels, and eventually allow your application to grow to consume all of available memory. Storage fragmentation, a second-order or third-order effect, actually dominates system performance. Eventually, it compromises reliability, which is completely unacceptable.
Note that in Debug mode compilations, the allocation is always exact. This helps shake out bugs.
Assume your application is going to run for months at a time. For example, I bring up VC++, Word, PowerPoint, FrontPage, Outlook Express, Forté Agent, Internet Explorer, and a few other applications, and essentially never close them. I've edited using PowerPoint for days on end (on the other hand, if you've had the misfortune to have to use something like Adobe FrameMaker, you begin to appreciate reliability; I've rarely been able to use this application without it crashing four to six times a day! And always because it has run out of space, usually by filling up my entire massive swap space!) Precise allocation is one of the misfeatures that will compromise reliability and lead to application crashes.
By making CString
s be multiples of some quantum, the memory allocator will end up cluttered with chunks of memory which are almost always immediately reusable for another CString
, so the fragmentation is minimized, allocator performance is enhanced, application footprint remains almost as small as possible, and you can run for weeks or months without problem.
Aside: Many years ago, at CMU, we were writing an interactive system. Some studies of the storage allocator showed that it had a tendency to fragment memory badly. Jim Mitchell, now at Sun Microsystems, created a storage allocator that maintained running statistics about allocation size, such as the mean and standard deviation of all allocations. If a chunk of storage would be split into a size that was smaller than the mean minus one s
than the prevailing allocation, he didn't split it at all, thus avoiding cluttering up the allocator with pieces too small to be usable. He actually used floating point inside an allocator! His observation was that the long-term saving in instructions by not having to ignore unusable small storage chunks far and away exceeded the additional cost of doing a few floating point operations on an allocation operation. He was right.
Never, ever think about "optimization" in terms of small-and-fast analyzed on a per-line-of-code basis. Optimization should mean small-and-fast analyzed at the complete application level (if you like New Age buzzwords, think of this as the holistic approach to program optimization, a whole lot better than the per-line basis we teach new programmers). At the complete application level, minimum-chunk string allocation is about the worst method you could possibly use.
If you think optimization is something you do at the code-line level, think again. Optimization at this level rarely matters. Read my essay on Optimization: Your Worst Enemy for some thought-provoking ideas on this topic.
Note that the += operator is special-cased; if you were to write:
CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;
then each application of the + operator causes a new string to be created and a copy to be done (although it is an optimized version, since the length of the string is known and the inefficiencies of strcat
do not come into play).
Summary
These are just some of the techniques for using CString
. I use these every day in my programming. CString
is not a terribly difficult class to deal with, but generally the MFC materials do not make all of this apparent, leaving you to figure it out on your own.