实例化和返回实现类型和接口
我们将以一个名为 MyType 的实现类型作为示例,该实现类型将实现 IStringable 和 IClosable 接口 。你可以直接从 winrt::implements(这不是一个运行时类)派生 MyType 。
#include <winrt/Windows.Foundation.h>
using namespace winrt;
using namespace Windows::Foundation;
struct MyType : implements<MyType, IStringable, IClosable>
{
winrt::hstring ToString(){ ... }
void Close(){}
};
// 或者,也可以从 IDL(这是一个运行时类)生成它。
// MyType.idl
namespace MyProject
{
runtimeclass MyType: Windows.Foundation.IStringable, Windows.Foundation.IClosable
{
MyType();
}
}
// 不能直接分配实现类型。
MyType myimpl; // error C2259: 'MyType': cannot instantiate abstract class
// 但是,可以从 MyType 获得你可以使用或作为具现部分返回的 IStringable 或 IClosable 对象,
// 只需调用 winrt::make 函数模板即可 。 make 将返回实现类型的默认接口 。
IStringable istringable = winrt::make<MyType>();
/*
但是,如果你从 XAML UI 引用类型,则在同一个项目中将会有一个实现类型和一个具现类型。 在这种情况下,make 将返回具现类型的实例 。
我们可以仅使用 istringable(在上面的代码示例中)来调用 IStringable 接口的成员。 但 C++/WinRT 接口(这是具现接口)派生自 winrt::Windows::Foundation::IUnknown 。 因此,你可以对其调用 IUnknown::as(或 IUnknown::try_as)以查询其他也可使用或返回的具现类型或接口 。
*/
// 不应调用 as 或 try_as 的情况是运行时类派生(“可组合类”)。 当实现类型组合另一个类时,
// 请勿调用 as 或 try_as 来执行所组合的类的未经检查的或经检查的 QueryInterface。
// 请改为访问 (this->) m_inner 数据成员,并对其调用 as 或 try_as。
istringable.ToString();
IClosable iclosable = istringable.as<IClosable>();
iclosable.Close();
// 如果你需要访问实现的所有成员并向调用方返回一个,则应使用 winrt::make_self 函数模板 。
// make_self 将返回一个包装实现类型的 winrt::com_ptr 。
// 你可以访问其所有接口的成员(使用箭头运算符),可以将它按原样返回给调用方,
// 或者对它调用 as 并将得到的接口对象返回给调用方 。
winrt::com_ptr<MyType> myimpl = winrt::make_self<MyType>();
myimpl->ToString();
myimpl->Close();
IClosable iclosable = myimpl.as<IClosable>();
iclosable.Close();
MyType 类不是具现类型的一部分,它是实现 。 但是,通过这种方法,你可以直接调用其实现方法,而无需虚拟函数调用的开销。 在上面的示例中,即使 MyType::ToString 在 IStringable 上使用与具现方法相同的签名,我们也还是直接调用非虚拟方法,而不必跨越应用程序二进制接口 (ABI) 。 com_ptr 只包含指向 MyType 结构的指针,因此你还可以通过 myimpl 变量和箭头运算符访问 MyType 的任何其他内部细节 。
假设你有一个接口对象,并且你恰好知道它是实现上的接口,则可以使用 winrt::get_self 函数模板返回到实现 。 同样,这种方法可以避免虚拟函数调用,并让你直接访问实现。
注意如果尚未安装 Windows SDK 版本 10.0.17763.0(Windows 10 版本 1809)或更高版本,则需调用 winrt::from_abi,而不是 winrt::get_self 。
下面是一个示例。 实现 BgLabelControl 自定义控件类中还有另一示例 :
void ImplFromIClosable(IClosable const& from)
{
MyType* myimpl = winrt::get_self<MyType>(from);
myimpl->ToString();
myimpl->Close();
}
// 但只有原始接口对象保留有接口。 如果你想要保留它,则可以调用 com_ptr::copy_from 。
winrt::com_ptr<MyType> impl;
impl.copy_from(winrt::get_self<MyType>(from));
// com_ptr::copy_from ensures that AddRef is called.
实现类型本身不会从 winrt::Windows::Foundation::IUnknown 派生,因此它没有 as 函数 。 即便如此,你也可以访问其所有接口的成员,如上面的 ImplFromIClosable 函数所述。 但如果你那样做,请勿将原始实现类型实例返回给调用方。 而应使用已显示的方法之一,返回一个具现接口或 com_ptr 。
如果你有一个实现类型的实例,并且需要将它传递给期望相应的具现类型的函数,那么你可以这样做,如以下代码示例所示。 实现类型上存在一个转换运算符(前提是实现类型是由 cppwinrt.exe 工具生成的),可以帮助完成此工作。 可以将实现类型值直接传递给期望得到相应具现类型值的方法。 从实现类型成员函数,可以将 *this 传递给期望得到相应具现类型值的方法。
// MyClass.idl
import "MyOtherClass.idl";
namespace MyProject
{
runtimeclass MyClass
{
MyClass();
void MemberFunction(MyOtherClass oc);
}
}
// MyClass.h
...
namespace winrt::MyProject::implementation
{
struct MyClass : MyClassT<MyClass>
{
MyClass() = default;
void MemberFunction(MyProject::MyOtherClass const& oc) { oc.DoWork(*this); }
};
}
...
// MyOtherClass.idl
import "MyClass.idl";
namespace MyProject
{
runtimeclass MyOtherClass
{
MyOtherClass();
void DoWork(MyClass c);
}
}
// MyOtherClass.h
...
namespace winrt::MyProject::implementation
{
struct MyOtherClass : MyOtherClassT<MyOtherClass>
{
MyOtherClass() = default;
void DoWork(MyProject::MyClass const& c){ /* ... */ }
};
}
...
//main.cpp
#include "pch.h"
#include <winrt/base.h>
#include "MyClass.h"
#include "MyOtherClass.h"
using namespace winrt;
// MyProject::MyClass is the projected type; the implementation type would be MyProject::implementation::MyClass.
void FreeFunction(MyProject::MyOtherClass const& oc)
{
auto defaultInterface = winrt::make<MyProject::implementation::MyClass>();
MyProject::implementation::MyClass* myimpl = winrt::get_self<MyProject::implementation::MyClass>(defaultInterface);
oc.DoWork(*myimpl);
}
...