Files

152 lines
4.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AtomUI.Native — Windows 平台 API 文档
## 概述
Windows 平台通过 `user32.dll` 的窗口扩展样式(Extended Window Styles)机制实现窗口级鼠标事件穿透。所有 API 均标注 `[SupportedOSPlatform("windows")]`。
---
## 1. 公共扩展方法
### 1.1 SetWindowIgnoreMouseEvents
设置窗口是否忽略(穿透)鼠标输入事件。
```csharp
internal static void SetWindowIgnoreMouseEvents(this WindowBase window, bool flag)
```
**参数:**
| 参数 | 类型 | 说明 |
|------|------|------|
| `window` | `WindowBase` | Avalonia 窗口实例 |
| `flag` | `bool` | `true` 启用穿透,`false` 禁用穿透 |
**异常:**
| 异常类型 | 条件 |
|----------|------|
| `ArgumentException` | 窗口句柄无效(`IntPtr.Zero`) |
| `PlatformNotSupportedException` | 当前平台不受支持 |
**Windows 实现原理:**
通过修改窗口扩展样式(`GWL_EXSTYLE`)中的 `WS_EX_TRANSPARENT` 和 `WS_EX_LAYERED` 位标志:
- **启用穿透**:`ExStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED)`
- **禁用穿透**:`ExStyle &= ~(WS_EX_TRANSPARENT | WS_EX_LAYERED)`
> ⚠️ `WS_EX_LAYERED` 是 `WS_EX_TRANSPARENT` 生效的前提条件,两者必须同时设置。
---
### 1.2 IsWindowIgnoreMouseEvents
查询窗口当前是否处于鼠标事件穿透状态。
```csharp
internal static bool IsWindowIgnoreMouseEvents(this WindowBase window)
```
**参数:**
| 参数 | 类型 | 说明 |
|------|------|------|
| `window` | `WindowBase` | Avalonia 窗口实例 |
**返回值:**
| 类型 | 说明 |
|------|------|
| `bool` | `true` 表示窗口正在穿透鼠标事件 |
**Windows 实现原理:**
读取窗口扩展样式并检查 `WS_EX_TRANSPARENT` 位:
```csharp
(GetWindowLongPtr(handle, GWL_EXSTYLE) & WS_EX_TRANSPARENT) != 0
```
---
## 2. P/Invoke 声明
> 命名空间:`AtomUI.Native.Windows`
> 类:`internal static class WindowUtilsInterop`
> 平台:`[SupportedOSPlatform("windows")]`
### 2.1 Win32 函数
#### GetWindowLongPtr
获取窗口的扩展样式或其他窗口属性。
```csharp
[DllImport("user32.dll", EntryPoint = "GetWindowLongW", SetLastError = true)]
public static extern long GetWindowLongPtr(IntPtr hWnd, int nIndex);
```
| 参数 | 类型 | 说明 |
|------|------|------|
| `hWnd` | `IntPtr` | 窗口句柄 |
| `nIndex` | `int` | 属性索引(如 `GWL_EXSTYLE = -20`) |
**返回值:** 请求的窗口属性值(`long`)。
---
#### SetWindowLongPtr
设置窗口的扩展样式或其他窗口属性。
```csharp
[DllImport("user32.dll", EntryPoint = "SetWindowLongW", SetLastError = true)]
public static extern long SetWindowLongPtr(IntPtr hWnd, int nIndex, long dwNewLong);
```
| 参数 | 类型 | 说明 |
|------|------|------|
| `hWnd` | `IntPtr` | 窗口句柄 |
| `nIndex` | `int` | 属性索引 |
| `dwNewLong` | `long` | 新的属性值 |
**返回值:** 设置前的旧属性值(`long`)。
---
### 2.2 常量定义
| 常量 | 值 | 类型 | 说明 |
|------|----|------|------|
| `GWL_STYLE` | `-16` | `int` | 窗口样式索引 |
| `GWL_EXSTYLE` | `-20` | `int` | 扩展窗口样式索引 |
| `WS_EX_TRANSPARENT` | `0x20L` | `long` | 鼠标事件穿透标志位 |
| `WS_EX_LAYERED` | `0x80000L` | `long` | 分层窗口标志位(穿透的前提条件) |
---
## 3. 使用示例
```csharp
// 启用窗口鼠标事件穿透
window.SetWindowIgnoreMouseEvents(true);
// 查询是否处于穿透状态
bool isPassThrough = window.IsWindowIgnoreMouseEvents();
// 禁用穿透,恢复正常鼠标交互
window.SetWindowIgnoreMouseEvents(false);
```
---
## 4. 注意事项
1. **句柄有效性** — 调用前确保 `window.PlatformImpl?.Handle?.Handle` 不为 `null` 或 `IntPtr.Zero`
2. **样式组合** — `WS_EX_TRANSPARENT` 必须与 `WS_EX_LAYERED` 同时使用才能实现完整的鼠标穿透
3. **线程安全** — Win32 窗口操作应在 UI 线程执行
4. **副作用** — 设置 `WS_EX_LAYERED` 可能影响窗口的渲染行为,对于已经是分层窗口的场景无额外影响