如何获取窗口类名称的长度,以便知道要分配的缓冲区大小?

Jac*_*ack 5 .net c# windows winapi winforms

如何预先获取类名的长度,以便可以将其传递给函数中的nMaxCount参数GetClassName()?诸如WM_GETTEXTLENGTH控件中存在的消息之类的信息,还是窗口类名称是否定义了固定的大小限制?如果是这样,那有什么价值?

我的目标是传递确切的大小,而不要使用重新分配方法(调用GetClassName()直到返回的大小小于其缓冲区)。

我当前的实现(没有重新分配方法):

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

string GetWindowClass(IntPtr hWnd)
{
    const int size = 256;
    StringBuilder buffer = new StringBuilder(size + 1);

    if (GetClassName(hWnd, buffer, size) == 0)
        Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

    return buffer.ToString();
}
Run Code Online (Sandbox Code Playgroud)

the*_*heB 5

对于此特定函数,类名限制为 256 个字符。(请参阅结构lpszClassName成员的文档WNDCLASSEX。)因此,只需分配一个该大小的固定缓冲区即可!

为了完整起见,让我们看一下在没有固定大小缓冲区的情况下调用函数的更通用的解决方案。在这种情况下,我们可以应用一个简单的 try-double-retry 算法,如下所示:

  1. 很好地猜测您需要多大的缓冲区,并分配一个该大小的缓冲区。
  2. 调用函数。如果写入的字符数小于提供的缓冲区的大小,则适合——返回值。
  3. 如果字符数等于提供的缓冲区的大小,则假设字符串已被截断,因此将缓冲区的大小加倍并返回步骤 2。

您可以使用以下代码查看算法的运行情况:

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int GetClassNameW(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

string GetWindowClass(IntPtr hWnd)
{
    string className = String.Empty;
    int length = 10; // deliberately small so you can 
                     // see the algorithm iterate several times. 
    StringBuilder sb = new StringBuilder(length);
    while (length < 1024)
    {
        int cchClassNameLength = GetClassNameW(hWnd, sb, length);
        if (cchClassNameLength == 0)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        else if (cchClassNameLength < length - 1) // -1 for null terminator
        {
            className = sb.ToString();
            break;
        }
        else length *= 2;
    }
    return className;
}
Run Code Online (Sandbox Code Playgroud)

(设置断点并逐步执行以真正看到它的运行情况。)