193 lines
8.9 KiB
Markdown
193 lines
8.9 KiB
Markdown
|
|
---
|
|||
|
|
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 monitor’s 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.*
|