Files
2026-04-17 14:55:32 -04:00

8.9 KiB
Raw Permalink Blame History

source_files, generated_at, model, schema_version, sha256
source_files generated_at model schema_version sha256
Common/DTS.CommonCore/Classes/WinApi/WindowsAPIHelpers.cs
2026-04-16T02:41:05.548844+00:00 Qwen/Qwen3-Coder-Next-FP8 1 74033c59308d77b1

WinApi

Documentation: WindowsAPIHelpers.cs


1. Purpose

This module provides low-level Windows API interop helpers for managing window placement and sizing behavior, specifically to ensure that maximized windows align with the work area (i.e., excluding taskbar, docked toolbars) of the appropriate monitor in multi-monitor setups. It defines P/Invoke signatures and supporting structures (POINT, RECT, MINMAXINFO, MONITORINFO, WINDOWPOS) and exposes a helper method GetMinMaxInfo that adjusts the ptMaxSize and ptMaxPosition fields of a MINMAXINFO structure during a WM_GETMINMAXINFO message. This ensures windows maximize correctly to the visible screen area of the monitor containing the window, rather than the primary monitors full screen bounds.


2. Public Interface

Structs & Classes

  • POINT

    [StructLayout(LayoutKind.Sequential)]
    public struct POINT { public int x; public int y; public POINT(int x, int y); }
    

    Represents a point in screen coordinates. Used as a field in MINMAXINFO and elsewhere.

  • MINMAXINFO

    [StructLayout(LayoutKind.Sequential)]
    public struct MINMAXINFO 
    { 
        public POINT ptReserved; 
        public POINT ptMaxSize; 
        public POINT ptMaxPosition; 
        public POINT ptMinTrackSize; 
        public POINT ptMaxTrackSize; 
    }
    

    Used in WM_GETMINMAXINFO to specify minimum/maximum tracking sizes and maximized window geometry. The ptMaxSize and ptMaxPosition fields are modified by GetMinMaxInfo.

  • MONITORINFO

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class MONITORINFO 
    { 
        public int cbSize = Marshal.SizeOf(typeof(MONITORINFO)); 
        public RECT rcMonitor = new RECT(); 
        public RECT rcWork = new RECT(); 
        public int dwFlags = 0; 
    }
    

    Container for monitor-specific information. cbSize is auto-initialized to the correct size. rcMonitor holds the full monitor bounds; rcWork holds the work area (usable screen space).

  • RECT

    [StructLayout(LayoutKind.Sequential, Pack = 0)]
    public struct RECT 
    { 
        public int left, top, right, bottom; 
        public static readonly RECT Empty; 
        public int Width => Math.Abs(right - left); 
        public int Height => bottom - top; 
        public bool IsEmpty => left >= right || top >= bottom; 
        // ... constructors, equality, operators ...
    }
    

    Represents a rectangle in screen coordinates. Includes computed properties (Width, Height, IsEmpty) and value-based equality (==, !=, Equals, GetHashCode). Note: Height is computed as bottom - top (not Math.Abs), so negative heights are possible if bottom < top.

  • WINDOWPOS

    [StructLayout(LayoutKind.Sequential)]
    public struct WINDOWPOS 
    { 
        public IntPtr hwnd; 
        public IntPtr hwndInsertAfter; 
        public int x, y, cx, cy; 
        public int flags; 
    }
    

    Mirrors the Win32 WINDOWPOS structure used in WM_WINDOWPOSCHANGING/WM_WINDOWPOSCHANGED.

Enums

  • WM

    public enum WM 
    { 
        WINDOWMAX = 0x0024, 
        WINDOWPOSCHANGING = 0x0046 
    }
    

    Subset of Windows message constants. Note: WINDOWMAX is likely a typo/misnomer for WM_WINDOWMAXIMIZED (which does not exist); standard WM_SYSCOMMAND with SC_MAXIMIZE is more common. WINDOWPOSCHANGING is correctly WM_WINDOWPOSCHANGING.

  • SWP

    public enum SWP 
    { 
        NOMOVE = 0x0002 
    }
    

    Subset of SetWindowPos flags. NOMOVE preserves current position when resizing.

Methods

  • GetMinMaxInfo(IntPtr hwnd, IntPtr lParam)

    public static void GetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
    

    Reads a MINMAXINFO structure from lParam, queries the monitor containing hwnd, retrieves its work area (rcWork) and full monitor area (rcMonitor), then updates mmi.ptMaxPosition and mmi.ptMaxSize to align the maximized window with the work area of the current monitor. Writes the modified structure back to lParam. This is intended to be called in response to WM_GETMINMAXINFO (0x0024).

  • GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi)

    [DllImport("user32")]
    public static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);
    

    P/Invoke wrapper for GetMonitorInfoW. Fills lpmi with monitor data. Caller must set lpmi.cbSize (handled by MONITORINFOs field initializer).

  • MonitorFromWindow(IntPtr handle, int flags)

    [DllImport("User32")]
    public static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);
    

    P/Invoke wrapper for MonitorFromWindow. Returns IntPtr.Zero on failure. Used with MONITOR_DEFAULTTONEAREST (0x00000002).


3. Invariants

  • MONITORINFO.cbSize must be set correctly before calling GetMonitorInfo:
    The MONITORINFO class initializes cbSize to Marshal.SizeOf(typeof(MONITORINFO)) in its field initializer, ensuring correctness for P/Invoke.

  • RECT dimensions assume left ≤ right and top ≤ bottom:
    Width uses Math.Abs(right - left), but Height does not (bottom - top). If bottom < top, Height will be negative. No validation enforces non-negative dimensions.

  • GetMinMaxInfo modifies ptMaxSize and ptMaxPosition only:
    Other MINMAXINFO fields (ptReserved, ptMinTrackSize, ptMaxTrackSize) are preserved as-is.

  • Monitor query uses MONITOR_DEFAULTTONEAREST:
    If hwnd is invalid or no monitor contains it, MonitorFromWindow returns IntPtr.Zero, and GetMinMaxInfo skips modification (no crash, but no adjustment either).


4. Dependencies

  • Runtime Dependencies:

    • System.Runtime.InteropServices (for StructLayout, Marshal, DllImport)
    • System.Windows (though no WPF types are used in this file—likely a legacy/unused reference)
    • Win32 user32.dll (for GetMonitorInfo, MonitorFromWindow)
  • Consumers (inferred):
    This module is designed to be used by code handling WM_GETMINMAXINFO (e.g., in a WPF/WinForms window procedure). The method GetMinMaxInfo is called with the window handle and lParam from the message. No other direct dependencies are visible in the source.

  • Missing Dependencies:

    • RECT is used but not defined in this file—this is a contradiction. The source defines RECT here, but the RECT used in MONITORINFO is initialized as new RECT(). This implies RECT must be defined here (and it is), so no external dependency is needed.
    • No using for System.Drawing or other types—RECT is self-contained.

5. Gotchas

  • RECT.Height is not absolute:
    Height => bottom - top (not Math.Abs). If bottom < top, Height is negative. This may cause unexpected behavior if consumers assume non-negative height.

  • MONITORINFO is a class, not a struct:
    This is unusual for P/Invoke marshaling (where struct is typical). While it works due to cbSize initialization and Marshal.PtrToStructure/StructureToPtr, using a class here risks reference-type semantics (e.g., accidental sharing). A struct would be safer and more conventional.

  • GetMinMaxInfo silently fails if monitor query fails:
    If MonitorFromWindow returns IntPtr.Zero (e.g., invalid hwnd), no adjustment occurs. No error is logged or thrown—consumers must verify behavior.

  • WM.WINDOWMAX is likely incorrect:
    0x0024 is WM_SYSCOMMAND, not a maximization message. WM_GETMINMAXINFO is 0x0024this is the same value. The enum name WINDOWMAX is misleading; it should be GETMINMAXINFO. This is a naming bug.

  • RECT.IsEmpty uses >= (not >):
    IsEmpty => left >= right || top >= bottom correctly handles degenerate rectangles (e.g., left == right), but consumers may overlook that empty rectangles have zero area.

  • No thread-safety guarantees:
    GetMinMaxInfo mutates a MINMAXINFO via Marshal.PtrToStructure/StructureToPtr. While the operation is read-modify-write on a copy (safe), the use of MONITORINFO as a class (reference type) could introduce issues if reused across threads without copying.

  • MONITORINFO.rcMonitor and rcWork are initialized to new RECT():
    RECT() sets all fields to 0, so rcMonitor and rcWork start as (0,0,0,0). This is safe for P/Invoke since GetMonitorInfo overwrites them.

  • No handling of DPI scaling:
    All coordinates are in physical pixels (Win32 convention). If the application is DPI-aware, callers must ensure hwnd corresponds to the correct DPI context. No DPI scaling is applied in GetMinMaxInfo.


No other issues are apparent from the source alone.