As discussed in the previous blog post, CString offers convenient methods for Windows C++ programming, including those to load strings from resources.
What Is a String Resource and Why Should You Care?
Basically, in a Windows Win32 C++ application you can store your strings (like messages for the user) as resources, and then reference them in your C++ code via their IDs. If you want to localize your application for a different language, a translator can take the string resources and translate them into the destination language. The C++ source code remains the same, as it references strings not by their language-specific literal form (e.g. “Cannot access the Registry key XYZ”), but using language-neutral resource IDs (e.g. IDS_CANNOT_ACCESS_REGISTRY_KEY_XYZ).
Accessing String Resources via the LoadString(W) API
To access a Unicode (UTF-16) string resource, you can use the LoadStringW Win32 API.
The Microsoft documentation for the LoadStringW API reads:
Loads a string resource from the executable file associated with a specified module and either copies the string into a buffer with a terminating null character or returns a read-only pointer to the string resource itself.
From that paragraph, we get that there are two different behaviors available for this API:
- Copy the string resource into a user-provided buffer.
- Return a read-only pointer to the string resource itself.
Behavior #1: Copying the String Resource into a User-provided Buffer
The first behavior is shown several times in Microsoft documentation. For example, the doc page about LoadStringW refers to the following sample code:

Basically, the idea here is to create a TCHAR buffer, and invoke LoadString to load the string resource into that buffer:
TCHAR sz[160];
LoadString(hInst, IDS_UNTITLED, sz, sizeof(sz)/sizeof(TCHAR));
In Unicode builds, TCHAR becomes WCHAR (i.e. wchar_t), and LoadString becomes LoadStringW.
One of the problem of this kind of code is that the buffer is pre-allocated with a given fixed size. Then, what happens if the string read from resources is larger than the pre-allocated buffer? The string is silently truncated, as per the official MS documentation about the cchBufferMax parameter:
The string is truncated and null-terminated if it is longer than the number of characters specified.
This could make the UI ugly if it is just a user message string that gets truncated; or it can become a much more serious bug if the truncated string is something like the format specifier for some printf-like function.
Moreover, in that code there is no check on the return value of LoadString. So, if the function fails, the buffer could contain garbage, and that could cause other bugs. (Although, I understand that often MS documentation doesn’t include error checking; but it’s important to point that out, as often people copy-paste code from MS doc samples, and silent bugs are introduced in their own code.)
In addition, it’s important to remember to properly scale the “sizeof” the destination buffer, dividing it by sizeof(TCHAR), which is sizeof(WCHAR) == 2 (bytes) in Unicode builds. Failing to do so would cause additional buffer overflow problems.
You can use a std::wstring of proper size, and pass a pointer to the internal string characters instead of using a raw wchar_t array, but the aforementioned problems still remain.
Behavior #2: Getting a Read-only Pointer to the String Resource
To me the behavior #2 (which is: get a read-only pointer to the string resource itself) is what leads to better and easier code (despite some type cast). However, it’s not very clear from the current MS documentation how to operate the LoadStringW API in this “behavior #2” mode.
So, let’s explain that here. Let’s start with the function declaration:
int LoadStringW(
[in, optional] HINSTANCE hInstance,
[in] UINT uID,
[out] LPWSTR lpBuffer,
[in] int cchBufferMax
);
To enable behavior #2 (i.e. return a read-only pointer to the string resource itself), you need to pass 0 (zero) for the last parameter cchBufferMax.
If you pass zero for cchBufferMax, the LoadStringW API says: “Hey, the caller wants me to just return a read-only pointer to the string resource itself, with no deep-copies in a user-provided buffer. Ok, let’s do that!”
Where does the API get the information about the destination string pointer? In other words: Where can the LoadStringW API write the string resource’s address? Well, it uses the third parameter for that: lpBuffer.
However, there’s a gotcha here (that I think is not well documented in the official MS web page). Basically, you need to create a const wchar_t pointer that will store the address of the resource string (which is read-only, hence the const):
// Pointer to the start of the string resource
const wchar_t* pchStringBegin = nullptr;
Then you need to pass the address of this pointer as the lpBuffer parameter to LoadStringW: &pchStringBegin. Note that, in this way, you are using a double-indirection, i.e. a pointer-to-a-pointer. Since the lpBuffer parameter is declared as LPWSTR (i.e. wchar_t*), you have a type mismatch for this parameter. In fact, LoadStringW expects a simple wchar_t* pointer; but you are passing a pointer-to-a-wchar_t-pointer (double indirection). However, this type mismatch is needed and expected by the behavior #2, so in this case you can safely use reinterpret_cast to make the C++ compiler happy about that type mismatch.
So, the LoadStringW call can look like this:
// Invoke LoadStringW, requesting a pointer
// to the beginning of the string resource
int cchStringLength = ::LoadStringW(
hInstance,
resourceId,
reinterpret_cast<PWSTR>(&pchStringBegin),
0);
Note that the string resource is not necessarily NUL-terminated! But, thankfully, on success, the LoadStringW API returns the number of characters (as count of wchar_ts) in the string resource.
So, you can use these two pieces of information to build a std::wstring instance to safely store the string resource:
- The pointer to the start of the string resource
- The number of wchar_ts in the string resource
In code, this becomes:
if (cchStringLength > 0) // LoadStringW succeeded
{
// Create a std::wstring storing the string loaded from resources
return std::wstring(pchStringBegin, cchStringLength);
}
On failure, LoadStringW returns zero. Then you can choose if you want to throw an exception, log an error message, return an empty string to the user, return a std::optional<std::wstring>, or whatever.
Wrapping Up: An Easy-to-Use and Hard-to-Misuse Helper Function for Loading String Resources into std::wstring
To wrap up this blog post, this is a nice LoadStringFromResources reusable C++ function that you can invoke to load string resources into std::wstring:
//
// Load a string resource into a std::wstring
//
[[nodiscard]] std::wstring LoadStringFromResources(
_In_opt_ HINSTANCE hInstance,
_In_ UINT resourceId
)
{
// Pointer to the first character of the string.
// NOTE: *Not* necessarily null-terminated!!
const wchar_t* pchStringBegin = nullptr;
// Invoke LoadStringW, requesting a pointer
// to the beginning of the string resource itself
const int cchStringLength = ::LoadStringW(
hInstance,
resourceId,
reinterpret_cast<PWSTR>(&pchStringBegin),
0);
ATLASSERT(cchStringLength >= 0);
if (cchStringLength > 0)
{
// Success
return std::wstring(pchStringBegin, cchStringLength);
}
else
{
// Failure: Throw an exception
// const DWORD error = ::GetLastError();
AtlThrowLastWin32();
}
}




