windows C++-使用 C++/WinRT 创作 API(三)

​实例化和返回实现类型和接口

我们将以一个名为 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);
}
...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值