tzdwindows/FileBrowsWindowPatch: 为用户资源管理器添加模糊效果
https://github.com/tzdwindows/FileBrowsWindowPatch
Windows 11的文件资源管理器是一个经典的“混合体”应用,其界面由两种截然不同的UI架构协同绘制:
-
传统Win32区域:如左侧导航树(
SysTreeView32)、地址栏下拉列表、部分上下文菜单等。这些部分依赖于user32.dll、gdi32.dll等核心模块,通过标准的BeginPaint、FillRect、DrawText等GDI函数进行渲染。 -
现代WinUI 3区域:如顶部的命令栏(Ribbon)、主文件显示区域的项容器、属性侧边栏、新的主页等。这些部分基于Windows App SDK (WinUI 3),在名为
Microsoft.UI.Content.DesktopChildSiteBridge的XAML岛上运行,使用DirectComposition和现代化的XAML渲染引擎。
对于传统Win32区域,我们已经有了相当成熟的技术方案:通过API钩取(Hook)技术拦截一系列GDI渲染函数(如 BeginPaint, EndPaint, FillRect, DrawTextW, ExtTextOutW, PatBlt, BitBlt, AlphaBlend, StretchBlt, Rectangle 等),在绘制过程中将不透明的背景和控件替换为透明或半透明,再结合桌面窗口管理器(DWM)的 DwmSetWindowAttribute 和 SetWindowCompositionAttribute 等接口为整个窗口应用亚克力(Acrylic)或云母(Mica)模糊效果。
然而,这套方法对WinUI 3区域完全无效。WinUI 3的渲染完全绕过了GDI,直接在更高的合成层级上进行。要修改这些区域的视觉效果(例如,使其背景透明),我们必须进入WinUI 3的领域,这就需要一套全新的、更为深入的方法。
探索之路:从旧的Xaml诊断接口到新的Udk接口
微软确实提供过用于检查和调试XAML应用的官方接口。资深Windows UI开发者可能会想到 InitializeXamlDiagnosticsEx 这个函数。它位于 Windows.UI.Xaml.dll 中,是UIA(UI Automation)和Visual Studio等工具用于连接和调试基于 UWP XAML (WinUI 2) 的应用程序的核心API。
然而,这条路走不通。原因很直接:Windows.UI.Xaml.dll 是旧的、系统级的UWP XAML运行时,它根本无法感知或调试运行在独立进程中的、基于Windows App SDK的 WinUI 3 应用。试图用它来连接文件资源管理器中的WinUI 3岛屿,无异于用USB-A接口去连接一台只有USB-C端口的设备。
那么,WinUI 3的调试支持在哪里?答案指向了Windows App SDK的核心运行时库:Microsoft.Internal.FrameworkUdk.dll。这个库包含了Windows App SDK的内部实现和开发工具包(Udk)功能。
通过使用IDA、Ghidra等反汇编工具对 Microsoft.Internal.FrameworkUdk.dll 进行分析,我们可以发现一个令人兴奋的事实:确实存在一个现代版的 InitializeXamlDiagnosticsEx 函数(或其功能等价物)。这个函数是WinUI 3诊断基础设施的入口,其设计目的与旧版类似,但专门用于初始化与WinUI 3 XAML树的诊断会话。
理论上的实现蓝图
发现这个新的入口点为我们提供了理论上的实现路径。一个完整的方案可能包含以下步骤:
-
DLL注入与运行时初始化:
-
将我们的自定义DLL注入到
explorer.exe进程。 -
在DLL中,动态加载
Microsoft.Internal.FrameworkUdk.dll并获取InitializeXamlDiagnosticsEx等关键函数的地址。 -
调用该函数,传入必要的参数(如进程ID、回调接口指针)来初始化一个针对当前进程的XAML诊断会话。
-
-
实现并注册诊断回调接口:
-
我们需要实现一个COM对象,该对象暴露特定的诊断接口(例如
IXamlDiagnostics)。 -
在这个接口的回调方法中,我们将接收来自WinUI 3运行时的“元素树已创建/更新”等事件通知。
-
-
遍历与筛选元素树:
-
当收到事件后,利用诊断接口提供的方法来遍历整个WinUI 3视觉树。
-
编写筛选逻辑,精确地定位我们想要修改的目标元素。这通常需要通过检查元素的
x:Name、AutomationProperties.AutomationId、类型(如Grid,Panel,Button)或其在视觉树中的位置来实现。例如,我们的目标可能是找到承载命令栏背景的Grid或Rectangle元素。
-
-
动态修改属性:
-
一旦获取到目标元素的句柄或接口(可能通过诊断接口提供的
GetHandleFromIInspectable或类似方法),我们就可以在运行时动态地修改其属性。 -
核心操作是将元素的
Opacity属性设置为0以实现完全透明,或修改其BackgroundBrush的属性来实现半透明模糊效果。
-

1422

被折叠的 条评论
为什么被折叠?



