Работа с функциями WinAPI, которые используют строки стиля C в качестве параметров OUT

Имеется функция 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;

Приведенный выше код работает, но у меня есть следующие проблемы с ним:

  1. Размер буфера совершенно произвольный, так как у меня нет способа узнать длину строки, которую функция может вернуть. Мне это кажется «неправильным» — я всегда старался избегать магических чисел в моем коде.

  2. Если функция возвращает строку, которая больше буфера, она будет усечена — это плохо!

  3. Всякий раз, когда функция возвращает строку, которая меньше буфера, я трачу впустую память. Это не так плохо, как (2), но я не в восторге от идеи выделить большие куски памяти (например, 1024 байта в моем примере выше) для чего-то, что на практике может потребовать всего несколько байтов.

Есть ли другие альтернативы?

0

Решение

Вызовите функцию несколько раз с временными буферами разных размеров. Начните с буфера, скажем, 8. Удвойте размер буфера и вызовите его снова. Повторяйте, пока не вернется тот же счет, что и в прошлый раз. Затем вы можете выделить буфер точного размера и скопировать то, что у вас есть. Существует ряд функций Win32 с похожим поведением.

Вы можете использовать GetWindowTextLength(), но это, вероятно, не очень поможет, если есть условия гонки (из-за них вы можете получить усеченный текст).

3

Другие решения

Существует несколько различных шаблонов в зависимости от того, какую функцию 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 ++ вы можете обернуть этот вид бухгалтерии, как я сделал в этом примере.

2

По вопросам рекламы [email protected]