Search code examples
c#c++windowsdwm

DwmGetWindowAttribute doesn't get the correct Rect size for MediaPlayer if it's in full screen mode


I am trying to get application size using this code :

      [DllImport(@"dwmapi.dll")]
      private static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out Rect pvAttribute, int cbAttribute);

    [Serializable, StructLayout(LayoutKind.Sequential)]
    private struct Rect
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;

        public Rectangle ToRectangle()
        {
            return Rectangle.FromLTRB(Left, Top, Right, Bottom);
        }
    }

      private static bool DWMWA_EXTENDED_FRAME_BOUNDS(IntPtr handle, out Rectangle rectangle)
        {
        Rect rect;
        var result = DwmGetWindowAttribute(handle, (int)Dwmwindowattribute.DwmwaExtendedFrameBounds,
            out rect, Marshal.SizeOf(typeof(Rect)));
        rectangle = rect.ToRectangle();
        return result >= 0;
         }

it's working fine for all running applications but if it's Media Player in fullscreen mode I didn't get the right Rect size.


Solution

  • Windows Media Player is weird in full screen mode such that the main window handle doesn't correspond to the full screen window displayed. The full screen window still has a handle but a little more work is needed to get to it.

    First you'd need to declare some WinAPI functions and structs:

    delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
    
    [DllImport("User32.dll")]
    static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
    
    [DllImport("User32.dll")]
    static extern bool GetMonitorInfo(IntPtr hMonitor, ref MonitorInfo lpmi);
    
    [DllImport("User32.dll")]
    static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
    
    [DllImport("User32.dll")]
    static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
    
    [StructLayout(LayoutKind.Sequential)]
    struct MonitorInfo
    {
        public uint Size;
        public Rect Monitor;
        public Rect Work;
        public uint Flags;
    }
    
    // You seem to have this one already
    [StructLayout(LayoutKind.Sequential)]
    struct Rect
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }
    

    From there, the method looks like this:

    // Pass Windows Media Player's main window handle here.
    static bool GetWmpFullScreenHandle(IntPtr mainHandle, out IntPtr fullScreenHandle)
    {
        IntPtr tempHandle = IntPtr.Zero;
    
        // Getting WMP's PID from the main window handle.
        GetWindowThreadProcessId(mainHandle, out uint wmpProcessId);
    
        // Optionally, check if the PID resolves to a WMP process.
        if (System.Diagnostics.Process.GetProcessById(wmpProcessId).ProcessName != "wmplayer")
        {
            fullScreenHandle = IntPtr.Zero;
    
            return false;
        }
    
        // This iterates through all the open window handles on the machine
        // and passes them to the callback below.
        EnumWindows((hWnd, lParam) =>
        {
            // Getting the window handle's PID.
            GetWindowThreadProcessId(hWnd, out uint windowProcessId);
    
            // Checking if the window handle belongs to the WMP process.
            if (windowProcessId == wmpProcessId)
            {
                var monitorInfo = new MonitorInfo
                {
                    Size = Convert.ToUInt32(Marshal.SizeOf(typeof(MonitorInfo)))
                };
    
                // Getting the dimensions of the monitor the window is displayed on,
                // as well as the window dimensions.
                if (GetMonitorInfo(MonitorFromWindow(hWnd, 0), ref monitorInfo) && 
                    GetWindowRect(hWnd, out Rect windowRect))
                {
                    Rect monitorRect = monitorInfo.Monitor;
    
                    // If the window dimensions are the same as its monitor's
                    // dimensions, then we found a hidden full-screen window!
                    if (windowRect.Left == monitorRect.Left &&
                        windowRect.Top == monitorRect.Top &&
                        windowRect.Right == monitorRect.Right &&
                        windowRect.Bottom == monitorRect.Bottom)
                    {
                        tempHandle = hWnd;
                    }
                }
            }
    
            return true;
        },
        IntPtr.Zero);
    
        fullScreenHandle = tempHandle;
    
        // Returns true if the hidden full-screen handle was found, false otherwise.
        return fullScreenHandle != IntPtr.Zero;
    }
    

    If found, you can then pass the resulting handle to DWMWA_EXTENDED_FRAME_BOUNDS to get the Rectangle.