Files

193 lines
8.9 KiB
Markdown
Raw Permalink Normal View History

2026-04-17 14:55:32 -04:00
---
source_files:
- Common/DTS.CommonCore/Classes/WinApi/WindowsAPIHelpers.cs
generated_at: "2026-04-16T02:41:05.548844+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "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`**
```csharp
[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`**
```csharp
[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`**
```csharp
[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`**
```csharp
[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`**
```csharp
[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`**
```csharp
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`**
```csharp
public enum SWP
{
NOMOVE = 0x0002
}
```
Subset of `SetWindowPos` flags. `NOMOVE` preserves current position when resizing.
#### Methods
- **`GetMinMaxInfo(IntPtr hwnd, IntPtr lParam)`**
```csharp
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)`**
```csharp
[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 `MONITORINFO`s field initializer).
- **`MonitorFromWindow(IntPtr handle, int flags)`**
```csharp
[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 `0x0024`—*this 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.*