79992680c9
- README 对齐当前 C# / Avalonia 架构与安装方式 - docs: architecture / sequences / hooks-and-cli / interop / build-and-install
40 lines
3.2 KiB
Markdown
40 lines
3.2 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`** 传递,绕开现代 .NET 不支持的 `IInspectable` 封送。
|
||
|
||
## 跨虚拟桌面(VirtualDesktopPinner)
|
||
|
||
- 经 `CoCreateInstance` 拿 ImmersiveShell(**必须 `CLSCTX_LOCAL_SERVER`**,否则 `REGDB_E_CLASSNOTREG`)→ `IApplicationViewCollection.GetViewForHwnd` → `IVirtualDesktopPinnedApps.PinView`。
|
||
- GUID 随 Windows 版本变化(这里用 24H2 一套,实测 26200 仍匹配);整段 try/catch,失败自动退回"仅当前桌面"。
|
||
- 窗口刚打开时 shell 可能还没登记 view(`TYPE_E_ELEMENTNOTFOUND`),故短间隔重试。
|
||
|
||
## 取图标(AppIcon)
|
||
|
||
`ExtractIconEx` 拿 HICON → `GetIconInfo` 取彩色位图 → `GetDIBits` 以 32bpp 自上而下读出 BGRA → 构造 `Avalonia.Media.Imaging.Bitmap`。老图标无 alpha(全 0)时补成不透明,避免整块透明。取不到则回退默认 Claude 图标。
|
||
|
||
## AOT 状态
|
||
|
||
- `csproj` 已开 `IsAotCompatible`(静态分析 0 警告);`PublishAot` 条件块 + `TrimmerRootAssembly`(Ursa/Semi 整体保留)。
|
||
- **AOT 发布的原生链接需要 MSVC 工具链**(从 "Developer Command Prompt for VS" 跑,或用配好 vcvars 的脚本)。
|
||
- ⚠️ **build 通过 ≠ 能跑**:源生成 COM / UIAutomation 必须真机运行验证(本项目踩过 `CLSCTX`、`IInspectable` 封送两个坑,都是编译过但运行崩)。
|