Getting Write Access to CString’s Internal Buffer

In the MSDN Magazine article “Using STL Strings at Win32 API Boundaries”, I discussed how to use std::wstring with Win32 APIs.

Note: The aforementioned article focuses on std::wstring containing Unicode UTF-16 encoded text; if you are interested in how to convert between an UTF-8 std::string and an UTF-16 std::wstring, this other MSDN Magazine article “Unicode Encoding Conversions with STL Strings and Win32 APIs” can come in handy.

But if you are developing (or maintaining) Windows C++ applications using ATL, WTL or MFC, you already have a convenient string class available for use at the Win32 API boundary: CString.

CString is designed in a way to be very well integrated with Win32 APIs. For example, you can simply pass instances of CString to Win32 APIs taking input string pointers; in other words, CString is a drop-in replacement for LPCTSTR parameters.

Moreover, using CString you can easily load strings from resources (via its LoadString method), format strings in a printf-like way using Format, or in a more advanced form using FormatMessage, etc.

Of course std::wstring has other advantages, like SSO (Small String Optimization), move semantics, STL iterator semantics, working with C++11 range-for loops, good integration with Boost, etc.

If you use ATL, WTL or MFC, many Win32 C-interface APIs are wrapped in a convenient object-oriented interface, and you can simply retrieve strings in CString instances (for example, calling a proper overload of CWindow::GetWindowText).

But what if you want to get a string from a Win32 API that isn’t wrapped in a nice way by the existing ATL/WTL/MFC classes?

In this case, you may need to let the Win32 API to scribble its output in the CString’s internal string buffer. With STL’s wstring you can simply call the wstring::resize method to make enough room in the string, and then pass a pointer to that buffer, as described in the aforementioned MSDN Magazine article.

But how can you access CString’s internal buffer for writing?

Well, you can call a couple of methods of the CString class: GetBuffer and ReleaseBuffer. The usage pattern is as follows:

  1. Call CString::GetBuffer to allocate a sufficiently large internal buffer; on success, this method returns a non-const pointer to the allocated buffer, giving you write access to it.
  2. Call the Win32 API (or whatever function) passing the pointer returned by CString::GetBuffer, so the called function can write its result text in the specified buffer.
  3. Call CString::ReleaseBuffer, which releases the access to CString’s internal buffer, so that the CString object can sync its state to the new buffer content.

Note that if the called function writes a NUL-terminator at the end of the string (as most Win32 APIs usually do), you can simply call CString::ReleaseBuffer, without passing any argument. In fact, in this case, the ReleaseBuffer method will simply scan the string stored in the CString’s internal buffer, until it finds the terminating NUL.

In addition, you can specify an explicit string length value to CString::ReleaseBuffer. In this case, the string in the internal buffer will be considered for that exact length.

Note that in Unicode builds those lengths are expressed in wchar_ts (not in bytes!).

This commented C++ sample code shows CString’s GetBuffer and ReleaseBuffer in action:

CString s;

// Allocate an internal buffer for CString, 
// and get write access to it.
const int bufferSize = 100; // in wchar_ts!
wchar_t* buffer = s.GetBuffer(bufferSize);

// Scribble in the CString's internal buffer.
wcscpy_s(buffer, bufferSize, L"Connie");

// Release access to the internal buffer.
// Now the CString object synchronizes its 
// state with the content of its internal 
// buffer.
//
// NOTE: This assumes that the string 
// in the buffer is *NUL*-terminated!
s.ReleaseBuffer();

// Avoid dangling references.
// After calling CString::ReleaseBuffer
// we cannot touch the CString's internal 
// buffer anymore.
buffer = nullptr;

// Let's check if it works!
ATLASSERT(s == L"Connie");

Note that if you explicitly pass a string length to CString::ReleaseBuffer, the string in the internal buffer is truncated at the specified position; e.g. if you call “s.ReleaseBuffer(3);” only the first three characters in the CString’s internal buffer are considered for the final CString’s text, making s == “Con” in the example above.

P.S. Pay attention when you manipulate CString’s internal buffers. For example, note that CString is not designed to store strings with embedded NULs. In fact, as stated in this MSDN documentation web page (emphasis mine):

“Although it is possible to create CStringT instances that contain embedded null characters, we recommend against it. Calling methods and operators on CStringT objects that contain embedded null characters can produce unintended results.

 

Leave a Reply

Your email address will not be published. Required fields are marked *