Uwp 将comptr设置为winrt::UserControl::Tag()时出错

Uwp 将comptr设置为winrt::UserControl::Tag()时出错,uwp,uwp-xaml,c++-winrt,cppwinrt,Uwp,Uwp Xaml,C++ Winrt,Cppwinrt,更新: 我用Richard的建议来修正标签的设置。但是我在使用getter for标记和使用.as/try_as操作符时遇到了一些问题 class DerivedController : public winrt::implements<DerivedController, Controller> { public: DerivedController() {} virtual ~DerivedController() {} static winrt::

更新: 我用Richard的建议来修正标签的设置。但是我在使用getter for标记和使用.as/try_as操作符时遇到了一些问题

class DerivedController : public winrt::implements<DerivedController, Controller> {
 public:
    DerivedController() {}

    virtual ~DerivedController() {}

    static winrt::com_ptr<DerivedController> from(const winrt::FrameworkElement& control) {
        return control ? control.Tag().try_as<DerivedController>() : nullptr;
    }
}
DerivedController过去在c++/cx中是这样的:

ref class DerivedController sealed : public Controller {
    internal : explicit DerivedController(Windows::UI::Xaml::FrameworkElement ^ &control)    
        : Controller(control) {}   

    static DerivedController ^   
        from(Windows::UI::Xaml::FrameworkElement ^ control) {   
            return control ? dynamic_cast<SvgCanvasController ^>(control->Tag) : nullptr;   
        }
}
当我尝试将其转换为C++/WinRT时,直接转换如下:

class Controller : public winrt::implements<Controller, winrt::Windows::Foundation::IInspectable> {
    Controller::Controller() {
        winrt::UserControl container;
1===>    container.Tag(this);
        ...
        ...
    }
}
static DerivedController* from(winrt::Windows::UI::Xaml::FrameworkElement const& control) 
{
    auto con = static_cast<winrt::Windows::UI::Xaml::FrameworkElement>(control);
    if (con != nullptr)
    {
        auto it1=con.Tag();
        auto it2 = it1.try_as<Controller>();
        Controller* cc = it2.get();
        DerivedController* der = static_cast< DerivedController* >(cc);
        return der;
    }
    return nullptr;
}
类控制器:公共winrt::实现{ 控制器::控制器(){ winrt::UserControl容器; 1=>container.Tag(这个); ... ... } } Controller是一个手工编写的类(无IDL),其定义如下:

class Controller : public winrt::implements<Controller, winrt::Windows::Foundation::IInspectable> {
    Controller::Controller() {
        winrt::UserControl container;
1===>    container.Tag(this);
        ...
        ...
    }
}
static DerivedController* from(winrt::Windows::UI::Xaml::FrameworkElement const& control) 
{
    auto con = static_cast<winrt::Windows::UI::Xaml::FrameworkElement>(control);
    if (con != nullptr)
    {
        auto it1=con.Tag();
        auto it2 = it1.try_as<Controller>();
        Controller* cc = it2.get();
        DerivedController* der = static_cast< DerivedController* >(cc);
        return der;
    }
    return nullptr;
}
类控制器:公共winrt::实现 { ... ... ... } 但我在(1)处得到一个错误:

Error C2664'void winrt::impl::consume\u Windows\u UI\u Xaml\u IFrameworkElement::Tag(const winrt::Windows::Foundation::IInspectable&)const':无法将参数1从“Controller*”转换为“const winrt::Windows::Foundation::IInspectable&”
  • 是否可以使用ABI的一些互操作来设置指向标记的Com指针
  • 我还缺什么吗
  • 这是正确的方法吗?还是有办法绕过它
  • 是否可以使用ABI的一些互操作来设置指向标记的Com指针
  • 如果要转换代码
    容器->标记=此
    ;在C++/CX中,您可以尝试将
    *this
    作为参数传递给C++/WinRT中的相应代码,而不是使用与ABI的互操作,如下所示:

    class Controller : public winrt::implements<Controller, winrt::Windows::Foundation::IInspectable> {
        Controller::Controller() {
            winrt::UserControl container;
    1===>    container.Tag(this);
            ...
            ...
        }
    }
    
    static DerivedController* from(winrt::Windows::UI::Xaml::FrameworkElement const& control) 
    {
        auto con = static_cast<winrt::Windows::UI::Xaml::FrameworkElement>(control);
        if (con != nullptr)
        {
            auto it1=con.Tag();
            auto it2 = it1.try_as<Controller>();
            Controller* cc = it2.get();
            DerivedController* der = static_cast< DerivedController* >(cc);
            return der;
        }
        return nullptr;
    }
    
    类控制器:公共winrt::实现 { 公众: 用户控制容器; 控制器::控制器() { container.Tag(*这个); } …… };
  • 我还缺什么吗
  • 您做得很好,但也许我们可以注意C++/CX和C++/WinRT之间的区别。这里我们使用
    *this
    代替
    this

  • 这是正确的方法吗?还是有办法绕过它
  • 如果您希望将对象添加到
    标记
    属性中,则该方法是正确的。您可以尝试以下代码:

    控制器cc{};
    UserControl uc=cc.container;
    auto item1=uc.Tag();
    auto item2=item1.try_as();
    auto item3=item2.get();
    UserControl uc2=item3->container;
    如果(uc=uc2)
    {
    int t=1;
    }
    
    更新:

    您可以使用static_cast实现从控制器指针到DerivedController指针的转换,并将from方法的返回类型更改为DerivedController*如下:

    class Controller : public winrt::implements<Controller, winrt::Windows::Foundation::IInspectable> {
        Controller::Controller() {
            winrt::UserControl container;
    1===>    container.Tag(this);
            ...
            ...
        }
    }
    
    static DerivedController* from(winrt::Windows::UI::Xaml::FrameworkElement const& control) 
    {
        auto con = static_cast<winrt::Windows::UI::Xaml::FrameworkElement>(control);
        if (con != nullptr)
        {
            auto it1=con.Tag();
            auto it2 = it1.try_as<Controller>();
            Controller* cc = it2.get();
            DerivedController* der = static_cast< DerivedController* >(cc);
            return der;
        }
        return nullptr;
    }
    

    您已经了解了大部分方法,但是现在是时候深入了解调用
    as()/try\u as()
    时在引擎盖下实际发生的情况了。您可能已经知道,它们是的包装。两者之间唯一的区别是
    as()
    QueryInterface
    故障转换为异常,而
    try\u as()
    在故障时返回null。(为简洁起见,其余部分我只参考
    as()

    现在我们知道我们调用的是
    QueryInterface
    ,但它使用的是
    GUID
    ,而不是模板类型参数。C++/WinRT为我们翻译了一个类型特征,
    WinRT::guid\u of
    ,它将uuid与类型相关联。当类型
    To
    是一个投影接口,或者是一个用声明的原始ABI接口时,“类型到guid”映射的行为与预期的非常接近

    但是,当类型
    To
    是从
    winrt::implements
    派生的实现类型时会发生什么?这里有两个额外的步骤

    默认接口 步骤1是的
    guid\u返回T的默认接口的guid。接口是它们自己的默认接口-
    default\u接口
    T
    相同。对于RuntimeClass,默认接口在winmd中,也可以在MIDL3中,但典型情况是
    Widget
    的默认接口是
    IWidget

    winrt::implements的默认接口 步骤2是,对于从
    winrt::implements
    派生的
    T
    类型,
    T
    的默认接口是提供给模板的第二种类型(CRTP类型之后的第一种类型)的默认接口。比如说,

    struct Base:实现{};
    //默认接口==默认接口==IBase
    严格Derived1:实现{};
    //默认_接口==IDerived
    struct-Derived2:实现{};
    //默认_接口==IBase
    struct-Derived1:实现{};
    //默认_接口==IBase
    
    记住这一点很重要

    在上面的示例中,请注意
    Derived2
    derived3
    具有与
    Base
    相同的默认界面。(上面的
    Derived3
    更糟糕,一点反模式-
    Derived3
    没有实现它自己的任何接口,因此这里不需要使用
    implements
    )这意味着
    as()
    不会像我们想象的那样运行-C++/WinRT将为
    IBase
    提供
    QueryInterface
    ,而不是
    IDerived
    ,这使得
    Base
    派生的
    as()
    中无法区分。如果编译成功,这将导致运行时歧义和各种混乱

    但是,
    as()
    不会编译,因为最后一个问题:
    as()
    需要返回类型。同样,对于投影接口/运行时类,这很简单:返回
    。对于原始ABI接口,这也很简单:返回
    com\u ptr

    陷阱 但是对于
    实现
    ,我们需要记住
    查询接口
    中的一个细节,返回的指针必须指向请求接口的vtable(它可能在对象的内存布局中偏移)。冒着过分简化的风险,
    实现
    
    Controller cc{};
    DerivedController dController{};
    DerivedController* aa = dController.from(cc.container);