Warm tip: This article is reproduced from serverfault.com, please click

How to add protected methods returning non-Windows Runtime types to a C++/WinRT base class

发布于 2020-11-30 12:44:39

I got the following C++/CX code that works as intended but would like to convert it to C++/WinRT code:

namespace My.Custom.Stuff
{
  [Windows::Foundation::Metadata::WebHostHidden]
  public ref class BaseClass : public Windows::UI::Xaml::Controls::SwapChainPanel
  {
  public:
    BaseClass();
    void PublicMethod();

  protected:
    static ::DirectX::XMFLOAT3 ProtectedMethod();
  };
}


namespace My.Custom.Stuff
{
  [Windows::Foundation::Metadata::WebHostHidden]
  public ref class SubClass sealed : public BaseClass
  {
  public:
    SubClass();
    void UseProtectedMethod()
    {
      ::DirectX::XMFLOAT3 value = ProtectedMethod()
      // ...
    }
  };
}

However, the problem that I encounter is as follows: BaseClass contains a protected method that returns a type that cannot be mapped to a corresponding Windows Runtime type. In C++/CX this is not a problem because ProtectedMethod is not mapped at all. If I use my Windows Runtime component ProtectedMethod is simply not exposed which is what I want.

However, that method should be a member of BaseClass because multiple other classes like SubClass use the method when implementing their public methods. I have come up with the following C++/WinRT MIDL code:

namespace My.Custom.Stuff
{
  unsealed runtimeclass BaseClass : Windows.UI.Xaml.Controls.SwapChainPanel
  {
    BaseClass();
    void PublicMethod();

    // This does not work
    protected DirectX.XMFLOAT3 RgbFromBrush(IInspectable brush);
  }
}



import "BaseClass.idl";


namespace My.Custom.Stuff
{
  runtimeclass SubClass : BaseClass
  {
    SubClass();
    void UseProtectedMethod();
  }
}

The problem is that if I define ProtectedMethod that way it won't work because ::DirectX::XMFLOAT3 is no Windows Runtime type. If I use any other return type that protected method gets mapped. However, it should not be visible when using that Windows Runtime component and of course I should not have to change its return type.

How can I achieve what I do with C++/CX using C++/WinRT?

Edit

Compiling the MIDL code results in something like this:

#include "BaseClass.g.h"


namespace winrt::My::Custom::Stuff::implementation
{
  struct BaseClass : BaseClassT<BaseClass>
  {
    BaseClass() = default;
    // ...
  };
}


namespace winrt::My::Custom::Stuff::factory_implementation
{
  struct BaseClass : BaseClassT<BaseClass, implementation::BaseClass>
  {
  };
}

I thought it might be possible to simply add the protected method as follows:

namespace winrt::My::Custom::Stuff::implementation
{
  struct BaseClass : BaseClassT<BaseClass>
  {
    BaseClass() = default;

  protected:
    static ::DirectX::XMFLOAT3 ProtectedMethod();
  };
}

However, attempting to use ProtectedMethod in SubClass results in the following error:

error C2039: 'ProtectedMethod': is not a member of 'winrt::My::Custom::Stuff::BaseClass'

Here's how I'm using it:

#include "SubClass.g.h"


namespace winrt::My::Custom::Stuff::implementation
{
  struct SubClass : SubClassT<SubClass>
  {
    SubClass() = default;
    void UseProtectedMethod()
    {
      ::DirectX::XMFLOAT3 value = ProtectedMethod();
    }
  };
}


namespace winrt::My::Custom::Stuff::factory_implementation
{
  struct SubClass : SubClassT<SubClass, implementation::SubClass>
  {
  };
}
Questioner
ackh
Viewed
0
Ryan Shepherd 2020-12-03 06:11:59

When converting from C++/CX to C++/WinRT, only public methods should go into your IDL file, as those are the only methods comprising the WinRT API surface.

Methods that are private/protected/internal are not part of the WinRT API surface, and so they should not go into the IDL.

The usage code you posted shouldn't compile, because your C++ definition of implementation::SubClass is missing a template parameter. Since the IDL/WinRT definition has SubClass deriving from BaseClass, you need to supply BaseClass's implementation type to SubClassT, and if you examine the contents of the cppwinrt-generated "SubClass.h", you'll see this happening. Once you declare your implementation of SubClass correctly, you will have access to the protected methods on BaseClass.

I just tried this out successfully, and it looks like this:

BaseClass.idl

namespace RuntimeComponent1
{
    [default_interface]
    unsealed runtimeclass BaseClass
    {
        BaseClass();
        void PublicMethod();
    }
}

BaseClass.h

#pragma once
#include "BaseClass.g.h"

struct non_winrt_type
{

};

namespace winrt::RuntimeComponent1::implementation
{
    struct BaseClass : BaseClassT<BaseClass>
    {
        BaseClass() = default;
        void PublicMethod() {}

    protected:
        non_winrt_type ProtectedMethod()
        {
            return {};
        }
    };
}
namespace winrt::RuntimeComponent1::factory_implementation
{
    struct BaseClass : BaseClassT<BaseClass, implementation::BaseClass>
    {
    };
}

SubClass.idl

import "BaseClass.idl";

namespace RuntimeComponent1
{
    [default_interface]
    runtimeclass SubClass : BaseClass
    {
        SubClass();
        void UseProtectedMethod();
    }
}

SubClass.h

#pragma once
#include "SubClass.g.h"
#include "BaseClass.h"

namespace winrt::RuntimeComponent1::implementation
{
    // Notice how BaseClass is used here.
    // This was copied directly from the generated boilerplate SubClass.h
    struct SubClass : SubClassT<SubClass, RuntimeComponent1::implementation::BaseClass>
    {
        SubClass() = default;

        void UseProtectedMethod()
        {
            auto result = ProtectedMethod();
        }
    };
}
namespace winrt::RuntimeComponent1::factory_implementation
{
    struct SubClass : SubClassT<SubClass, implementation::SubClass>
    {
    };
}