前一节创建了一个 C++/WinUI 3 应用,接着分析默认生成的框架代码。框架代码中使用了大量 C++ 模板技术和新语言特性,发挥了 C++ 语言零开销抽象的优势,很值得学习。
App 应用入口
重新回到应用入口类 App。
struct App : AppT<App>
{
App();
void OnLaunched(Microsoft::UI::Xaml::LaunchActivatedEventArgs const&);
private:
winrt::Microsoft::UI::Xaml::Window window{ nullptr };
};
应用的入口 App 类继承于一个代码生成的模板 AppT,这里用到的 C++ 奇异模板递归(CRTP)技术和 ATL 一脉相承。
template <typename D, typename ... Interfaces>
struct AppT: public ::winrt::Microsoft::UI::Xaml::ApplicationT<D, ::winrt::Microsoft::UI::Xaml::Markup::IXamlMetadataProvider, Interfaces...>
{
using IXamlType = ::winrt::Microsoft::UI::Xaml::Markup::IXamlType;
void InitializeComponent()
{
if (_contentLoaded)
return;
_contentLoaded = true;
::winrt::Windows::Foundation::Uri resourceLocator{ L"ms-appx:///App.xaml" };
::winrt::Microsoft::UI::Xaml::Application::LoadComponent(*this, resourceLocator);
}
IXamlType GetXamlType(::winrt::Windows::UI::Xaml::Interop::TypeName const& type)
{
return AppProvider()->GetXamlType(type);
}
IXamlType GetXamlType(::winrt::hstring const& fullName)
{
return AppProvider()->GetXamlType(fullName);
}
::winrt::com_array<::winrt::Microsoft::UI::Xaml::Markup::XmlnsDefinition> GetXmlnsDefinitions()
{
return AppProvider()->GetXmlnsDefinitions();
}
private:
bool _contentLoaded{false};
winrt::com_ptr<XamlMetaDataProvider> _appProvider;
winrt::com_ptr<XamlMetaDataProvider> AppProvider()
{
if (!_appProvider)
{
_appProvider = winrt::make_self<XamlMetaDataProvider>();
}
return _appProvider;
}
};
这里的 AppT
是由 C++/WinRT 代码生成器自动生成的,主要逻辑是为ApplicationT
模板类增加了 IXamlMetadataProvider
接口的实现。
这里和 ATL 常用的递归模板不一样的是,模板参数除了传入子类自身外,还可以传入不定长的接口列表。
struct ApplicationT :
implements<D, winrt::Microsoft::UI::Xaml::IApplicationOverrides, composing, Interfaces...>,
impl::require<D, winrt::Microsoft::UI::Xaml::IApplication>,
impl::base<D, Application>,
winrt::Microsoft::UI::Xaml::IApplicationOverridesT<D>
{
using composable = Application;
protected:
ApplicationT()
{
impl::call_factory<Application, IApplicationFactory>([&](IApplicationFactory const& f) { [[maybe_unused]] auto winrt_impl_discarded = f.CreateInstance(*this, this->m_inner); });
}
};
这里 implements
模板约束了接口实现,用来代替 vtable 实现的虚函数重写。impl::require
和 impl::base
提供了静态类型转换。
而构造函数委托给了 impl::call_factory
, 最终将通过 COM 接口调用到 WinRT 运行时中。
程序入口
App
类实现了应用初始化,那 App
实例本身由谁来构造工程文件中并没有找到。这里利用 Visual Studio 的单步执行调试,直接跳转到了 Win32 C++ 程序的入口 wWinMain。
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
{
void (WINAPI *pfnXamlCheckProcessRequirements)();
auto module = ::LoadLibrary(L"Microsoft.ui.xaml.dll");
if (module)
{
pfnXamlCheckProcessRequirements = reinterpret_cast<decltype(pfnXamlCheckProcessRequirements)>(GetProcAddress(module, "XamlCheckProcessRequirements"));
if (pfnXamlCheckProcessRequirements)
{
(*pfnXamlCheckProcessRequirements)();
}
::FreeLibrary(module);
}
}
winrt::init_apartment(winrt::apartment_type::single_threaded);
::winrt::Microsoft::UI::Xaml::Application::Start(
[](auto&&)
{
::winrt::make<::winrt::winui3_test::implementation::App>();
});
return 0;
}
这个函数位于 App.xaml.g.hpp
中,由 XamlTypeInfo.g.cpp
引入,这个文件由工程自动生成,但未显示在 VS 的资源管理器中,可能是被 WinUI 3 工程默认引入的。
这里逻辑比较简单,Check 函数应该是用来检查运行环境的,winrt::init_apartment
初始化了 COM 环境,随后调用 Application.Start()
并构造了 App
类。
MainWindow 主窗口
梳理了 WinUI 3 的初始化流程后,接着分析 MainWindow 的功能。
struct MainWindow : MainWindowT<MainWindow>
{
MainWindow();
int32_t MyProperty();
void MyProperty(int32_t value);
void myButton_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& args);
};
MainWindow
继承自 MainWindowT
并实现了 myButton
的 click 方法。
template <typename D, typename ... I>
struct MainWindowT : public ::winrt::winui3_test::implementation::MainWindow_base<D,
::winrt::Microsoft::UI::Xaml::Markup::IComponentConnector,
I...>
{
using base_type = typename MainWindowT::base_type;
using base_type::base_type;
using class_type = typename MainWindowT::class_type;
void InitializeComponent();
virtual void Connect(int32_t connectionId, IInspectable const& target);
virtual ::winrt::Microsoft::UI::Xaml::Markup::IComponentConnector GetBindingConnector(int32_t connectionId, IInspectable const& target);
void UnloadObject(::winrt::Microsoft::UI::Xaml::DependencyObject const& dependencyObject);
void DisconnectUnloadedObject(int32_t connectionId);
::winrt::Microsoft::UI::Xaml::Controls::Button myButton()
{
return _myButton;
}
void myButton(::winrt::Microsoft::UI::Xaml::Controls::Button value)
{
_myButton = value;
}
protected:
bool _contentLoaded{false};
private:
struct MainWindow_obj1_Bindings;
::winrt::Microsoft::UI::Xaml::Controls::Button _myButton{nullptr};
};
MainWindowT
模板向 MainWindow_base
添加了 Button
控件,并实现了控件连接接口 IComponectConnector
,上一篇中提到的控件点击事件 myButton_Click
的响应也就是通过该接口实现的。
template <typename D, typename... I>
struct __declspec(empty_bases) MainWindow_base : implements<D, winui3_test::MainWindow, composing, I...>,
impl::require<D, winrt::Microsoft::UI::Xaml::IWindow>,
impl::base<D, winrt::Microsoft::UI::Xaml::Window>
{
using base_type = MainWindow_base;
using class_type = winui3_test::MainWindow;
using implements_type = typename MainWindow_base::implements_type;
using implements_type::implements_type;
using composable_base = winrt::Microsoft::UI::Xaml::Window;
hstring GetRuntimeClassName() const
{
return L"winui3_test.MainWindow";
}
MainWindow_base()
{
impl::call_factory<winrt::Microsoft::UI::Xaml::Window, winrt::Microsoft::UI::Xaml::IWindowFactory>([&](winrt::Microsoft::UI::Xaml::IWindowFactory const& f) { [[maybe_unused]] auto winrt_impl_discarded = f.CreateInstance(*this, this->m_inner); });
}
};
MainWindow_base
仅负责了 Window
对象的构造,和 IWindow
接口约束。
winrt::implements 和 impl::call_factory
到这里基本熟悉了 C++/WinRT 对 WinUI 3 对象的包装方式,也就是通过继承 winrt::implements
来实现 IUnknown
、IInspectable
这样的基础设施。和传统 COM 对象不同的是放弃了使用虚函数来定义接口,而是通过模板约束接口,减少对象方法调用开销。
而 C++/WinRT 这些的对象实例都只是一个智能指针,真正的实例应该位于 WinRT 内部,这里智能指针只是代理了实际实例的所有方法。代理的方式则是通过 impl::call_factory
实现,内部还是通过 COM 接口来完成调用。
参考
作者:lyincc
版权:本文采用 「CC BY 4.0」 知识共享许可协议进行许可。