c00000fd
c00000fd

Reputation: 22327

Preallocating std::string to pass into a WinAPI

I'm curious if the following code is correct? I'm running it on a somewhat older version of VS 2008, for a Windows-only C++ project.

My goal is to preallocate memory in std::string to pass it into a WinAPI knowing the required size in characters:

//'hWnd' = window handle
int nLn = GetWindowTextLength(hWnd);

//Text variable to collect text in
std::wstring str;
str.reserve(nLn);

GetWindowText(hWnd, (LPTSTR)str.data(), nLn);

My concern here is that str.data() returns const wchar_t * and GetWindowText() requests LPTSTR, which is not a const buffer. Would a type cast be OK there?

Upvotes: 2

Views: 921

Answers (2)

Qaz
Qaz

Reputation: 61970

I can't speak for VS2008, but most pre-C++11 follow the C++11 rule of &str[0] + i == str.data() + i, so &str[0] works and doesn't require any casts1, which is a much safer route than subverting the type system and the standard. Just be careful because you aren't permitted to overwrite the null terminator after the string, even with another null terminator. If you aren't able to guarantee this for VS2008, then a std::vector<wchar_t> would be the preferred solution.

However, you have another problem. reserve doesn't stop it being undefined behaviour to access str out of bounds. Bounds are based on size(), not capacity(). You need to change reserve to resize.

Even then, you have another problem. GetWindowTextLength doesn't include the null terminator. You must use nLn + 1 in the code following that call.

With the changes made, this will work with any conforming C++11 implementation, including some pre-C++11 implementations, and ignoring error checking:

int nLnWithNul = GetWindowTextLength(hWnd);

std::wstring str(nLnWithNul, '\0'); // bit of a shorthand for the resize call
int nCopiedLn = GetWindowText(hWnd, &str[0], nLnWithNul);
str.resize(nCopiedLn);

The last resize call handles the copied title being shorter than nLnWithNul. For example, if the title shrinks since GetWindowTextLength was called. After resizing, the string will contain only the copied text, not including any NUL characters after it.


1: See the answer to my previous question.

Upvotes: 3

John Zwinck
John Zwinck

Reputation: 249642

No, you cannot do that according to the C++ standard. But you can do it if you use std::vector<wchar_t> instead of std::wstring:

int nLn = GetWindowTextLength(hWnd);
std::vector<TCHAR> str(nLn); // size, not reserve()
int size = GetWindowText(hWnd, str.data(), nLn);
str.resize(size); // will make empty in case of error

See also: writing directly to std::string internal buffers

Upvotes: 0

Related Questions