Имеется функция WinAPI, которая возвращает свой результат через параметр OUT строки в стиле C, например:
int WINAPI GetWindowTextW(
_In_ HWND hWnd,
_Out_ LPTSTR lpString,
_In_ int nMaxCount
);
Есть ли лучший способ использования функции, чем то, что я делаю ниже?
HWND handle; // Assume this is initialised to contain a real window handle
std::wstring title;
wchar_t buffer[512];
GetWindowTextW(handle, buffer, sizeof(buffer));
title = buffer;
Приведенный выше код работает, но у меня есть следующие проблемы с ним:
Размер буфера совершенно произвольный, так как у меня нет способа узнать длину строки, которую функция может вернуть. Мне это кажется «неправильным» — я всегда старался избегать магических чисел в моем коде.
Если функция возвращает строку, которая больше буфера, она будет усечена — это плохо!
Всякий раз, когда функция возвращает строку, которая меньше буфера, я трачу впустую память. Это не так плохо, как (2), но я не в восторге от идеи выделить большие куски памяти (например, 1024 байта в моем примере выше) для чего-то, что на практике может потребовать всего несколько байтов.
Есть ли другие альтернативы?
Вызовите функцию несколько раз с временными буферами разных размеров. Начните с буфера, скажем, 8. Удвойте размер буфера и вызовите его снова. Повторяйте, пока не вернется тот же счет, что и в прошлый раз. Затем вы можете выделить буфер точного размера и скопировать то, что у вас есть. Существует ряд функций Win32 с похожим поведением.
Вы можете использовать GetWindowTextLength()
, но это, вероятно, не очень поможет, если есть условия гонки (из-за них вы можете получить усеченный текст).
Существует несколько различных шаблонов в зависимости от того, какую функцию Windows API вы используете. В некоторых случаях вы можете сначала выполнить запрос, иногда вызывая другую функцию (например, GetWindowTextLengthW), но обычно передавая NULL для буфера. После запроса вы назначаете размер и снова вызываете, чтобы получить фактические строковые данные.
Даже с query-allocate-query вам иногда нужно выполнить итерацию, так как может быть условие гонки. Например, рассмотрим, что произойдет, если заголовок окна изменится между вызовами GetWindowTextLengthW и GetWindowTextW.
Вы также можете избежать лишней копии, используя саму строку, а не второй буфер.
std::wstring GetWindowTitle(HWND hwnd) {
std::wstring title(16, L'X');
int cch;
do {
title.resize(2 * title.size());
cch = GetWindowTextW(hwnd, &title[0], title.size());
} while (cch + 1 == title.size());
title.resize(cch);
return title;
}
Хотя это неудобно, на самом деле это не является ошибкой дизайна Windows API. API предназначен для интерфейса C, а не C ++. Поскольку C не является объектно-ориентированным, он довольно ограничен в обработке строк. Для кода C ++ вы можете обернуть этот вид бухгалтерии, как я сделал в этом примере.