For implementations guaranteeing that characters are stored continuously (C++11 and up) you can get text directly into std::wstring
.
// UNICODE build - prefered
int potential_length = GetWindowTextLengthW( hwnd )
std::wstring text;
// Theoretically, some implementations can keep internal buffer not NUL terminated
// Allocate one character more just to be safe
text.resize( potential_length + 1 );
int final_length = GetWindowTextW( hwnd, &text[0], potential_length + 1 );
text.resize( final_length );
If you are learning Windows API or writing new software, you definitely should prefer UNICODE builds over ANSI builds. This forces you to use wstring
instead of string
, wchar_t
instead of char
, but it saves you from many problems with untranslatable characters (not all UNICODE characters can be represented by current code page), internal API conversions (API has to repeatedly convert strings from narrow to wide characters and other way round).
Yo can save yourself from some traps if you define project wide macros UNICODE
and _UNICODE
and remove MBCS
if it is defined somewhere. You can do this probably in options dialog with field named "additional defines" or something like this. If you don't know how to configure additional macros, defining them just before including <windows.h>
header can be acceptable.
#define UNICODE
#define _UNICODE
#include <windows.h>
If you insist on using string
and char
, here it is.
// ANSI build - obsolete, use only if you have to
// You will lose characters untranslatable into current code page
int potential_length = GetWindowTextLengthA( hwnd );
std::string text;
// Theoretically, some implementations can keep internal buffer not NUL terminated
// Allocate one character more just to be safe
text.resize( potential_length + 1 );
int final_length = GetWindowTextA( hwnd, &text[0], potential_length + 1 );
text.resize( final_length );
Older C++ standards disallow modifying internal string data. You could use some intermediate buffer defined on stack, obtained with new wchar_t[]
or as I prefer with std::vector
.
// UNICODE build - preferred
int potential_length = GetWindowTextLengthW( hwnd );
std::vector<wchar_t> buff;
buff.resize( potential_length + 1 );
int final_length = GetWindowTextW( hwnd, buff.data(), potential_length + 1 );
std::wstring text( buff.data(), final_length );
// ANSI build - obsolete, use only if you have to
// You will lose characters untranslatable into current code page
int potential_length = GetWindowTextLengthA( hwnd );
std::vector<char> buff;
buff.resize( potential_length + 1 );
int final_length = GetWindowTextA( hwnd, buff.data(), potential_length + 1 );
std::string text( buff.data(), final_length );
If compiler doesn't support buff.data()
, use &buff[0]
.
Error handling
On error GetWindowTextLengthW
returns 0, so to check error you would need to SetLastError(0)
before calling it and check GetLastError()
result after call. GetWindowTextW
on error returns 0 and checking must be done with same steps. This is because 0 is valid text length.
SetLastError( 0 );
int potential_length = GetWindowTextLengthW( hwnd );
if ( potential_length == 0 )
{
DWORD error = GetLastError();
if ( error != 0 )
{
// Handle error here. Throw exception, log message, display dialog etc.
// Most probably hwnd handle is invalid.
return;
}
}
std::wstring text;
// Theoretically, some implementations can keep internal buffer not NUL terminated
// Allocate one character more just to be safe
text.resize( potential_length + 1 );
SetLastError( 0 );
int final_length = GetWindowTextW( hwnd, &text[0], potential_length + 1 );
if ( final_length == 0 )
{
DWORD error = GetLastError();
if ( error != 0 )
{
// Handle error here. Throw exception, log message, display dialog etc.
// Most probably hwnd handle is invalid or belongs to another process.
return;
}
}
text.resize( final_length );
+ 1 and length manipulation explanation
Additional + 1
covers quirks of the Windows API where some API calls take into account trailing NUL characters, and some don't. This is the case with GetWindowTextLengthW
which returns length of text without NUL character, but last parameter in GetWindowTextW
needs to state buffer size including place for NUL character. If we omit + 1
buffer would contain one character less because API have to put NUL character.
Documentation states also that GetWindowTextLength
can return value few character larger (I think it applies mostly to GetWindowTextLengthA
variant). This explains length correction, because only after GetWindowTextW
we know true length.
Over-allocating string space
Most string implementations which keep string
characters continuously also add NUL character at end to simplify c_str()
and data()
handling, but taking C++11 standard literally, they don't have to. Because GetWindowText
always puts NUL character at end, for implementations that do not keep additional space for NUL character permanently it could overwrite some internal data.
Although
text.resize( potential_length );
could work, resizing string one character more
text.resize( potential_length + 1 );
will cover these implementations. This has a cost of always resizing text
latter to smaller length and unnecessary allocation if we cross string inline storage size.