本文使用 DraftDepot(稿仓 https://github.com/xland/DraftDepot)编辑并发布,DraftDepot 可以把一篇文章一键发布到多个平台。
建立 WinRT 消息分发队列
应用启动时,首先要创建 WinRT 消息分发队列:
DispatcherQueueOptions options{sizeof(DispatcherQueueOptions),DQTYPE_THREAD_CURRENT,DQTAT_COM_NONE};staticwinrt::Windows::System::DispatcherQueueController controller{nullptr};autohr=CreateDispatcherQueueController(options,reinterpret_cast<ABI::Windows::System::IDispatcherQueueController**>(winrt::put_abi(controller)));if(FAILED(hr)){MessageBox(NULL,L"无法创建DispatcherQueueController",L"系统提示",MB_OK);ExitProcess(-1);}这段代码在当前线程创建 DispatcherQueueController(变量名 controller),用于建立 WinRT 消息分发队列并管理其生命周期。
它是使用 Windows UI Composition 等异步 API 的前提,为后续创建合成器(Compositor)及构建视觉树提供必要的消息循环基础。
桥接原生窗口与 WinRT 合成器
这个任务完成后,就可以创建窗口了,如下代码所示
// winrt::Windows::UI::Composition::Compositor compositor{ nullptr };compositor=Composition::Compositor();autohIns=GetModuleHandle(nullptr);autocls=getWinClsName(hIns);hwnd=CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP|exStyle,cls.data(),title.data(),style,x,y,(float)w,(float)h,NULL,NULL,hIns,NULL);SetWindowLongPtr(hwnd,GWLP_USERDATA,reinterpret_cast<LONG_PTR>(this));autointerop=compositor.as<ABI::Windows::UI::Composition::Desktop::ICompositorDesktopInterop>();// winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget winTarget{ nullptr };autor=reinterpret_cast<ABI::Windows::UI::Composition::Desktop::IDesktopWindowTarget**>(winrt::put_abi(winTarget));interop->CreateDesktopWindowTarget(hwnd,false,r);// winrt::Windows::UI::Composition::SpriteVisual visual{ nullptr };visual=compositor.CreateSpriteVisual();winTarget.Root(visual);这段代码在 DispatcherQueue 就绪后,负责创建原生窗口并将其与 WinRT 合成器桥接:
- 创建合成器:
首先实例化 Compositor 对象,它是整个 Windows UI Composition 视觉系统的工厂和入口。 - 创建透明窗口:
通过 CreateWindowEx 创建窗口,其中 WS_EX_NOREDIRECTIONBITMAP 标志至关重要,
它禁用了传统的 GDI/DWM 重定向表面,使窗口成为仅由 DirectComposition 驱动的纯合成器窗口。
这种窗口天生支持背景半透明效果。 - 关联上下文:
使用 SetWindowLongPtr 将当前对象指针存入窗口的用户数据区,以便在窗口过程(WndProc)中访问类实例。
这部分代码不是本文的重点,我没有给出。 - 桥接 HWND 与合成器:
利用 ICompositorDesktopInterop 接口调用 CreateDesktopWindowTarget,
将原生窗口句柄注册为合成器的桌面渲染目标(winTarget),
这一步打通了 Win32 窗口与 WinRT Composition 视觉树之间的通道,使后续创建的 Visual 能够正确呈现到该窗口上。 - 建立视觉树根节点:
创建一个 SpriteVisual 作为根视觉对象,并将其设置为 winTarget 的 Root。
此后所有子 Visual 都挂载在该根节点下,最终通过合成器呈现到窗口上。
绘制元素
如果你想调整窗口的背景色,可以使用如下方法修改 visual 的颜色
winrt::Windows::UI::Color color=winrt::Windows::UI::ColorHelper::FromArgb(a,r,g,b);visual.Brush(win->compositor.CreateColorBrush(color));如你所见,颜色支持透明通道,可以设置透明颜色。
如果你想增加子元素,可以用如下代码:
auto childVisual=compositor.CreateSpriteVisual();visual.Children().InsertAtTop(childVisual);//移除子元素的代码//visual.Children().Remove(borderVisual);如果你想设置元素的位置和尺寸,可以用如下方式:
visual.Offset({x,y,0.0f});visual.Size({w,h});可以通过如下方式控制元素的显隐
visual.IsVisible(false);visual.IsVisible(true);所有绘图操作都是直接完成的,你不需要通过类似这样的方法刷新窗口
InvalidateRect(hwnd,nullptr,FALSE);通过这种方式编译的程序可以单exe执行(不需要分发一大堆dll),单exe的体积小于1M
如果大家感兴趣的话,可以点赞、关注。
点赞的人多的话,后面我再介绍如何做布局、圆角、滚动条、渲染文本、画布、字体图标等一系列的知识。
甚至可以介绍如何让这个技术与FaceBook的Yoga布局引擎整合,开发GUI框架(我做过验证了)