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

问题描述

更新: 我使用了Richard的建议来修正Tag的设置。但是我在使用Tag的吸气剂和在其上使用 .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;
    }
}

这是我得到的错误

1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(8013): error C2440: 'static_cast': cannot convert from 'winrt::impl::producer<D,I,void> *' to 'D *'
1>        with
1>        [
1>            D=`anonymous-namespace'::DerivedController,1>            I=winrt::Windows::Foundation::IInspectable
1>        ]
1>        and
1>        [
1>            D=`anonymous-namespace'::DerivedController
1>        ]
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(8013): note: Types pointed to are unrelated; conversion requires reinterpret_cast,C-style cast or function-style cast
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(8012): note: while compiling class template member function 'D &winrt::impl::produce_base<D,void>::shim(void) noexcept'
1>        with
1>        [
1>            D=`anonymous-namespace'::DerivedController,1>            I=winrt::Windows::Foundation::IInspectable
1>        ]
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(7777): note: see reference to function template instantiation 'D &winrt::impl::produce_base<D,void>::shim(void) noexcept' being compiled
1>        with
1>        [
1>            D=`anonymous-namespace'::DerivedController,1>            I=winrt::Windows::Foundation::IInspectable
1>        ]
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(7737): note: see reference to class template instantiation 'winrt::impl::produce_base<D,void>' being compiled
1>        with
1>        [
1>            D=`anonymous-namespace'::DerivedController,1>            I=winrt::Windows::Foundation::IInspectable
1>        ]
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(7777): note: see reference to class template instantiation 'winrt::impl::produce<D,winrt::Windows::Foundation::IInspectable>' being compiled
1>        with
1>        [
1>            D=`anonymous-namespace'::DerivedController
1>        ]
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(4088): note: see reference to function template instantiation 'D *winrt::get_self<To,winrt::Windows::Foundation::IInspectable>(const I &) noexcept' being compiled
1>        with
1>        [
1>            D=`anonymous-namespace'::DerivedController,1>            To=`anonymous-namespace'::DerivedController,1>            I=winrt::Windows::Foundation::IInspectable
1>        ]
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(2387): note: see reference to function template instantiation 'winrt::com_ptr<D> winrt::impl::as<To,winrt::impl::IUnkNown>(From *)' being compiled
1>        with
1>        [
1>            D=`anonymous-namespace'::DerivedController,1>            From=winrt::impl::IUnkNown
1>        ]
1>note: see reference to function template instantiation 'winrt::com_ptr<D> winrt::Windows::Foundation::IUnkNown::as<`anonymous-namespace'::DerivedController>(void) const' being compiled
1>        with
1>        [
1>            D=`anonymous-namespace'::DerivedController
1>        ]
1>note: see reference to class template instantiation 'winrt::com_ptr<D>' being compiled
1>        with
1>        [
1>            D=Controller
1>        ]
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(10615): note: see reference to class template instantiation 'winrt::com_ptr<winrt::impl::IContextCallback>' being compiled (compiling source file
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(10349): note: see reference to class template instantiation 'winrt::com_ptr<winrt::impl::IServerSecurity>' being compiled
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(10308): note: see reference to class template instantiation 'std::chrono::time_point<winrt::clock,winrt::Windows::Foundation::TimeSpan>' being compiled
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(6462): note: see reference to class template instantiation 'winrt::com_ptr<winrt::impl::IMarshal>' being compiled
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\cppwinrt\winrt/base.h(210): note: see reference to class template instantiation 'std::array<uint8_t,8>' being compiled

控制器的构造函数为:

auto controller = winrt::make<Controller>().as<Controller>();

DerivedController的构造方式为:

DerivedController dController{};

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 ++ / CX中,我曾经能够做到:

ref class Controller {
    Controller() {
        container = ref new UserControl();
        container->Tag = this;
        ...
        ...
    }
}

当我尝试将其转换为C ++ / WinRT时,直接转换将如下所示:

class Controller : public winrt::implements<Controller,winrt::Windows::Foundation::IInspectable> {
    Controller::Controller() {
        winrt::UserControl container;
1===>    container.Tag(this);
        ...
        ...
    }
}

Controller是一个手工编写的类(无idls),其定义如下所示:

class Controller : public winrt::implements<Controller,winrt::Windows::Foundation::IInspectable> 
{
    ...
    ...
    ...
}

但是我在(1)处遇到错误

Error   C2664   'void winrt::impl::consume_Windows_UI_Xaml_IFrameworkElement<D>::Tag(const winrt::Windows::Foundation::IInspectable &) const': cannot convert argument 1 from 'Controller *' to 'const winrt::Windows::Foundation::IInspectable &'
  1. 是否可以通过与ABI的互操作将Com指针设置为Tag?
  2. 还有什么我想念的吗?
  3. 这是正确的方法吗?还是有办法解决

解决方法

  1. 是否可以通过与ABI的互操作将Com指针设置为Tag?

如果要转换代码container->Tag=this;在C ++ / CX中,将其传递给C ++ / WinRT中的相应代码,您可以尝试将*this作为参数传递,而不是使用与ABI的互操作,例如:

class Controller : public winrt::implements<Controller,winrt::Windows::Foundation::IInspectable>
{
    public:
        UserControl container;
        Controller::Controller()
        {
            container.Tag(*this);
        }
        ……
};
  1. 还有什么我想念的吗?

您做得不错,但是也许我们可以注意C ++ / CX与C ++ / WinRT之间的差异。在这里,我们使用*this代替this

  1. 这是正确的方法吗?还是有办法解决?

如果要将对象添加到Tag属性中,则该方法是正确的。您可以尝试以下代码:

Controller cc{};
UserControl uc = cc.container;
auto item1 = uc.Tag();
        
auto item2=  item1.try_as<Controller>();
auto item3 = item2.get();
UserControl uc2 = item3->container;
if (uc = uc2)
{
   int t = 1;
}

更新

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

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;
}

然后调用 from 方法:

Controller cc{};
DerivedController dController{};
DerivedController* aa = dController.from(cc.container);
 
,

您已经掌握了大部分方法,但是现在该是在深入探讨调用as<To>()/try_as<To>()时实际发生的情况的时候了。您可能已经知道它们是QueryInterface的包装。两者之间的唯一区别是as<To>()QueryInterface失败转换为异常,而try_as<To>()则在失败时返回null。 (为简便起见,其余部分仅指as<To>()

因此,现在我们知道我们正在调用QueryInterface,但是它需要一个GUID,而不是模板类型参数。 C ++ / WinRT用类型特征winrt::guid_of<T>为我们翻译了此特征,该特征将uuid与类型相关联。如果类型To是投影接口,或者是用uuid declspec声明的原始ABI接口,则“类型到引导”映射的行为几乎与预期的一样。

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

默认界面

第1步是guid_of<T>返回T的默认接口的向导。接口是它们自己的默认接口-default_interface<T>T相同。对于运行时类,winmd中的默认接口为an attribute,而MIDL3中的默认接口为manually specified,但是典型的情况是Widget的默认接口为IWidget。 / p>

winrt :: implements的默认接口

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

struct Base : implements<Base,IBase> {};
// default_interface<Base> == default_interface<IBase> == IBase

strict Derived1 : implements<Derived1,IDerived,Base> {};
// default_interface<Derived1> == IDerived

struct Derived2 : implements<Derived2,Base,IDerived> {};
// default_interface<Derived2> == IBase

struct Derived1 : implements<Derived1,Base> {};
// default_interface<Derived1> == IBase

记住这一点很重要。

在上面的示例中,请注意Derived2Derived 3Base具有相同的默认接口。 (上面的Derived3甚至更糟,并且有点反模式-Derived3本身没有实现任何接口,因此这里不需要使用implements。)这意味着as<Derived2>()的行为不会像我们想象的那样-C ++ / WinRT将QueryInterface而不是IBase用于IDerived,这使得{{1} }和Base对于Derived而言是无法区分的。如果成功编译,这将导致运行时模棱两可和各种混乱。

但是,as<T>()不会编译,因为最后一个折痕:as<Derived2>()需要返回类型。同样,对于计划的接口/运行时类,这很简单:返回as<To>()。对于原始的ABI接口,这也很简单:返回To

渔获物

但是对于com_ptr<T>,我们需要记住implements的详细信息,返回的指针必须指向请求的接口的vtable(可能在对象的内存布局中偏移)。冒着过于简化的风险,QueryInterface使用每个实现的接口的模板类型implementsproduce<D,I>指向produce<Derived1,IDerived>的{​​{1}}的vtable)来完成此任务。因此,最后一步是Derived1将此ABI偏移量接口指针转换为IDerived指针,以便编译器能够应用正确的偏移量来安全地返回as<To>()指针。

如果我们使用示例代码,则意味着调用produce<To,default_interface<To>>将强制转换为To,这将使您回到as<Derived1>()。但是调用produce<Derived1,IDerived>*会尝试强制转换为Derived1,因为它没有返回到as<Derived2>()的路径,因为produce<Derived2,IBase>*并不是Derived2实现的接口之一。换句话说,C ++ / WinRT将把您从IBase带到Derived2,因为IBase通过Base类型实现Base,但是不需要您从IBaseimplements

结论

如果您的实现类型没有默认接口(或没有唯一的接口),那么尝试将其与IBase一起使用将会很麻烦。

在这一点上,我希望您能理解为什么调用IDerived2并不适合您:它没有自己的默认接口,使其在as<T>()/try_as<T>()时含糊不清来到as<DerivedController>() / Controller。这在C ++ / CX中起作用的原因是它自动生成了ref类的默认接口。如果您希望通过as<T>()访问类型,则它必须具有唯一的默认界面。