0

给定一个 WinAPI 函数,该函数通过 C 风格的字符串 OUT 参数返回结果,例如:

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 字节)的想法并不感到兴奋。

还有其他选择吗?

4

2 回答 2

3

使用不同大小的临时缓冲区多次调用该函数。从 8 的缓冲区开始。将缓冲区大小加倍并再次调用它。重复直到它返回与上次相同的计数。然后您可以分配确切大小的缓冲区并复制您在那里的内容。有许多具有类似行为的 Win32 函数。

您可以使用GetWindowTextLength(),但如果存在竞争条件,它可能不会有太大帮助(因为它们,您最终可能会收到截断的文本)。

于 2013-02-05T20:07:15.880 回答
2

根据您使用的 Windows API 函数,有几种不同的模式。对于一些,您可以先查询,有时通过调用另一个函数(例如,GetWindowTextLengthW),但通常通过为缓冲区传递NULL。查询后,分配大小并再次调用以获取实际的字符串数据。

即使使用查询分配查询,您有时也需要迭代,因为可能存在竞争条件。例如,考虑如果窗口标题在 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++ 代码,您可以像我在本示例中所做的那样包装这种簿记。

于 2013-02-05T22:28:43.183 回答