5ce2c8a982
为 Claude Code 提供原生 Windows toast 通知:点击跳回原窗口、切回 Windows Terminal 标签、跨虚拟桌面、调用方图标、非阻塞投递;NativeAOT 单文件分发。
40 lines
2.9 KiB
Markdown
40 lines
2.9 KiB
Markdown
# 原生互操作与 AOT
|
||
|
||
所有 Win32 / COM 互操作都为 **NativeAOT** 准备:用 `LibraryImport`(源生成 P/Invoke)和 `[GeneratedComInterface]`(源生成 COM),**不用** `System.Drawing`、经典 `[ComImport]`、反射式封送(它们在 AOT 下不可用)。
|
||
|
||
| 文件 | 职责 | 关键点 |
|
||
|------|------|--------|
|
||
| `Win32.cs` | 基础 P/Invoke | `GetForegroundWindow`、`GetClassName`、工具窗口样式等 |
|
||
| `WindowActivator.cs` | 抢前台激活 | ALT 模拟 + `AttachThreadInput` + 多 API 组合,绕过防焦点抢占 |
|
||
| `WinTerminalTabs.cs` | WT 切标签 | 源生成 COM 调 UIAutomation,按 RuntimeId 定位标签 |
|
||
| `VirtualDesktopPinner.cs` | 跨虚拟桌面 | 未公开 COM `IVirtualDesktopPinnedApps.PinView` |
|
||
| `ProcessTree.cs` | 进程树上溯 | Toolhelp 快照,跳过 shell/运行时找调用方 App |
|
||
| `AppIcon.cs` | 取图标 | `ExtractIconEx` + GDI 读 BGRA 像素 → Avalonia 位图 |
|
||
| `Sound.cs` | 提示音 | winmm `PlaySound` 从内存异步播放 |
|
||
|
||
## 窗口激活(WindowActivator)
|
||
|
||
Windows 限制后台进程抢焦点。组合技:还原最小化 → 模拟一次 ALT 抬起 → `AttachThreadInput` 把当前线程与前台/目标线程输入队列挂接 → `AllowSetForegroundWindow` + `SetWindowPos`/`BringWindowToTop`/`SwitchToThisWindow`/`SetForegroundWindow` 多管齐下 → 解除挂接。
|
||
|
||
## Windows Terminal 切标签(WinTerminalTabs)
|
||
|
||
- `save` 时:检测前台窗口类是否 `CASCADIA_HOSTING_WINDOW_CLASS`;是则用 UIAutomation 找当前选中的 `TabItem`,取其 **RuntimeId**(一串 int,SAFEARRAY)存入状态。
|
||
- 点击时:激活 WT 窗口后,枚举标签找到 RuntimeId 匹配的,调 `SelectionItemPattern.Select()`。
|
||
- **AOT 要点**:接口用 `[GeneratedComInterface]`;未用到的 vtable 槽用占位方法按 SDK 头文件顺序补齐(顺序 / GUID 均取自 `UIAutomationClient.h`);`IApplicationView` 以裸 `IntPtr` 传递。
|
||
|
||
## 跨虚拟桌面(VirtualDesktopPinner)
|
||
|
||
- 经 `CoCreateInstance`(`CLSCTX_LOCAL_SERVER`)拿 ImmersiveShell → `IApplicationViewCollection.GetViewForHwnd` → `IVirtualDesktopPinnedApps.PinView`。
|
||
- GUID 取自 Win11 24H2;整段 try/catch,失败自动退回"仅当前桌面"。
|
||
- 窗口刚打开时 view 可能尚未就绪,短间隔重试直到成功。
|
||
|
||
## 取图标(AppIcon)
|
||
|
||
`ExtractIconEx` 拿 HICON → `GetIconInfo` 取彩色位图 → `GetDIBits` 以 32bpp 自上而下读出 BGRA → 构造 `Avalonia.Media.Imaging.Bitmap`。老图标无 alpha(全 0)时补成不透明,避免整块透明。取不到则回退默认 Claude 图标。
|
||
|
||
## AOT
|
||
|
||
- `csproj` 开 `IsAotCompatible`;`PublishAot` 条件块 + `TrimmerRootAssembly`(Ursa / Semi 整体保留)+ `CoreUtils.*.Static` 静态链接 Skia / HarfBuzz / ANGLE。
|
||
- 原生链接需要 MSVC 工具链(从 "Developer Command Prompt for VS" 跑,或用配好 vcvars 的脚本)。
|
||
- 源生成 COM / UIAutomation 与静态渲染需在真机运行验证。
|