【问题标题】:Error setting comptr to winrt::UserControl::Tag()将 comptr 设置为 winrt::UserControl::Tag() 时出错
【发布时间】:2021-01-04 21:10:48
【问题描述】:

更新: 我使用 Richard 的建议来修复 Tag 的设置。但是我在使用 getter for 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,I,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,I,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,I,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>            To=`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. 这是正确的方法吗?或者有没有办法解决?

【问题讨论】:

    标签: uwp uwp-xaml c++-winrt cppwinrt


    【解决方案1】:
    1. 是否可以使用与 ABI 的某些互操作将 Com 指针设置为 Tag?

    如果要转换代码container-&gt;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);
     
    

    【讨论】:

    • 嗨,理查德,我更新了问题,并在应用此问题后遇到了一些错误。非常感谢您对此的看法!
    • 嗨@ssn,我修改了答案,请检查一下。
    • 实际上看起来我必须明确地从 IInspectable 派生,而不仅仅是基于此:devblogs.microsoft.com/oldnewthing/20200827-00。我没试过你的方法。
    【解决方案2】:

    您已经了解了大部分情况,但现在是时候深入了解调用as&lt;To&gt;()/try_as&lt;To&gt;() 时实际发生的事情了。您可能已经知道它们是QueryInterface 的包装器。两者之间的唯一区别是as&lt;To&gt;()QueryInterface 失败转换为异常,而try_as&lt;To&gt;() 在失败时返回null。 (为简洁起见,其余部分我将仅参考as&lt;To&gt;()

    所以现在我们知道我们正在调用QueryInterface,但它需要一个GUID,而不是模板类型参数。 C++/WinRT 使用类型特征 winrt::guid_of&lt;T&gt; 为我们翻译它,它将 uuid 与类型相关联。当 To 类型是一个投影接口,或者是一个使用 uuid declspec 声明的原始 ABI 接口时,“类型到 guid”映射的行为与预期的差不多。

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

    默认界面

    第一步是guid_of&lt;T&gt;返回T的默认接口的guid。接口是它们自己的默认接口 - default_interface&lt;T&gt;T 相同。对于runtimeclasses,在winmd中默认接口为an attribute,在MIDL3中可以为manually specified,但典型情况是Widget的默认接口为IWidget

    winrt::implements的默认界面

    第二步,对于从winrt::implements派生的类型TT的默认接口是提供给模板的第二种类型的默认接口(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&lt;Derived2&gt;() 不是不会像我们想象的那样运行 - C++/WinRT 将使用 QueryInterface 替换 IBase,而不是 IDerived,这使得 BaseDerived 无法区分 as&lt;T&gt;()。如果编译成功,这将导致运行时歧义和各种混乱。

    但是,as&lt;Derived2&gt;() 不会编译,因为最后一个问题:as&lt;To&gt;() 需要返回类型。同样,对于预计的接口/运行时类,这很简单:返回 To。对于原始 ABI 接口,这也很简单:返回 com_ptr&lt;T&gt;

    捕获

    但是对于implements,我们需要记住QueryInterface的一个细节,返回的指针必须指向请求接口的vtable(可能在对象的内存布局中偏移)。冒着过度简化的风险,implements 使用模板类型produce&lt;D, I&gt; 为每个实现的接口完成此操作(produce&lt;Derived1, IDerived&gt; 指向Derived1IDerived 的vtable)。所以,最后一步是 as&lt;To&gt;() 将此 ABI 偏移接口指针转换为 produce&lt;To, default_interface&lt;To&gt;&gt; 指针,以便编译器能够应用正确的偏移来安全地取回 To 指针。

    如果我们使用示例代码,这意味着调用as&lt;Derived1&gt;() 将转换为produce&lt;Derived1, IDerived&gt;*,这可以让您回到Derived1。但是调用as&lt;Derived2&gt;() 将尝试转换为produce&lt;Derived2, IBase&gt;*,它没有返回Derived2 的路径,因为IBase 不是Derived2 实现的接口之一。换句话说,C++/WinRT 会将你从IBase 带到Base,因为Base 通过implements 类型实现IBase,但它不会把你从IBase 带到IDerived2

    结论

    如果您的实现类型没有默认接口(或没有唯一接口),您将很难将其与as&lt;T&gt;()/try_as&lt;T&gt;() 一起使用。

    在这一点上,我希望你能明白为什么调用 as&lt;DerivedController&gt;() 对你不太有效:它没有自己的默认界面,因此当涉及到 @987654390 时,它会与 Controller 产生歧义@/QueryInterface。这在 C++/CX 中起作用的原因是它自动为 ref 类生成了一个默认接口。如果您希望您的类型可以通过as&lt;T&gt;() 访问,它需要有一个唯一的默认接口。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-06
      • 2018-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多