Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the length of a window class name so I know how large of a buffer to allocate?

How do I get the class name length beforehand so I can pass it into nMaxCount parameter in GetClassName() function? something like WM_GETTEXTLENGTH message which exists for Controls or does a window class name have a fixed size limit defined? if so, what's that value?

My goal is pass exact size rather do the reallocation approach (call GetClassName() until it return size smaller than its buffer).

My current implementation (without the reallocation approach):

[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();
}
like image 574
Jack Avatar asked Jul 06 '16 20:07

Jack


People also ask

What is a window class name?

There are three types of window classes: System Classes. Application Global Classes. Application Local Classes.

What is the use of the window class?

A window class is a set of attributes that the system uses as a template to create a window. Every window is a member of a window class. All window classes are process specific.

How do I change my Windows class?

There is no way to change the class of an existing window. The only option is to destroy the original window and create a new window using a different class.


1 Answers

In the case of this particular function, the class name is limited to 256 characters. (See the documentation for the lpszClassName member of the WNDCLASSEX struct.) So just allocate a fixed buffer of that size and be done with it!

For completeness, let's look at the more general solution for calling functions where there is no fixed-size buffer. In this case we can apply a simple try-double-retry algorithm as follows:

  1. Make a good guess about how large of a buffer you'll need, and allocate one of that size.
  2. Call the function. If the number of characters written is less than the size of the provided buffer, then it fit—return the value.
  3. If the number of characters is equal to the size of the provided buffer, assume that the string has been truncated, so double the size of the buffer and go back to step 2.

You can see the algorithm in action with this code:

[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;
}

(Set a breakpoint and step through to really see it in action.)

like image 50
theB Avatar answered Nov 15 '22 04:11

theB