File: Windows\Win32\PInvoke.GetWindowText.cs
Web Access
Project: src\src\System.Private.Windows.Core\src\System.Private.Windows.Core.csproj (System.Private.Windows.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.ComponentModel;
using System.Runtime.CompilerServices;
#if DEBUG
using System.Runtime.InteropServices;
#endif
 
namespace Windows.Win32;
 
internal static partial class PInvokeCore
{
    [SkipLocalsInit]
    public static unsafe string GetWindowText<T>(T hWnd) where T : IHandle<HWND>
    {
        int textLength = 0;
 
        using BufferScope<char> buffer = new(stackalloc char[128]);
 
        while (true)
        {
            // GetWindowTextLength returns the length of the text not including the null terminator.
            int newTextLength = GetWindowTextLength(hWnd.Handle);
 
            if (newTextLength == 0)
            {
                // The window has no text. Return an empty string.
#if DEBUG
                int error = Marshal.GetLastWin32Error();
                if (error != 0)
                {
                    Debug.Fail($"GetWindowTextLength failed. Error: {new Win32Exception(error).Message}");
                }
#endif
                return string.Empty;
            }
 
            textLength = Math.Max(textLength, newTextLength);
 
            // Use a buffer that has room for at least two additional chars (one for the null terminator,
            // and one to detect if the text length has increased).
            buffer.EnsureCapacity(textLength + 2);
            fixed (char* b = buffer)
            {
                int actualTextLength = GetWindowText(hWnd.Handle, b, buffer.Length);
 
                if (actualTextLength == 0)
                {
                    // Failed to get the text. Return an empty string.
 
                    Debug.Fail($"GetWindowText failed. Error: {new Win32Exception().Message}");
                    return string.Empty;
                }
 
                // The window text may have changed between calls. Keep looping until we get a buffer that can fit.
                if (actualTextLength > buffer.Length - 2)
                {
                    // We know the text is at least actualTextLength characters
                    // long, so use this as minimum value for the next iteration.
                    textLength = actualTextLength;
                    continue;
                }
 
                GC.KeepAlive(hWnd.Wrapper);
                return buffer[..actualTextLength].ToString();
            }
        }
    }
}