locked
How do I implement an interface in a WinRT C++ / CX component that is exported from another C++ / CX component?

    Question

  • Using Visual Studio 2012 RTM.

    I have the following interface and class in a WinRT C++ / CX component:

       public ref class FooEventArgs sealed
       {
          private: int _percent;
    
          public: FooEventArgs(int percent)
          {
             _percent = percent;
          }
    
          public: property int Percent
          {
             int get() { return _percent; }
          }
       };
    
       public interface class IFoo
       {
          event EventHandler<FooEventArgs^>^ FooEvent
          {
             EventRegistrationToken add(EventHandler<FooEventArgs^>^ value);
             void remove(EventRegistrationToken token);
             void raise(Object^ sender, FooEventArgs^ e);
          }
          void Foo(void);
       };
    

    Inside the same dll, I can then create a class that implements the interface with no problems:

       public ref class FooLocalClass sealed : IFoo
       {
          private: event EventHandler<FooEventArgs^>^ _foo;
          public: virtual event EventHandler<FooEventArgs^>^ FooEvent
          {
             EventRegistrationToken add(EventHandler<FooEventArgs^>^ value)
             {
                return _foo::add(value);
             }
             void remove(EventRegistrationToken token)
             {
                _foo::remove(token);
             }
             void raise(Object^ sender, FooEventArgs^ e)
             {
                _foo::raise(sender, e);
             }
          }
          public: virtual void Foo(void)
          {
             return;
          }
       };
    

    However, if I create a new project for a new dll, and add a reference to the .winmd file for the first project, then the following class does not compile:

       public ref class FooExternalClass sealed : IFoo
       {
          private: event EventHandler<FooEventArgs^>^ _foo;
          public: virtual event EventHandler<FooEventArgs^>^ FooEvent
          {
             EventRegistrationToken add(EventHandler<FooEventArgs^>^ value)
             {
                return _foo::add(value);
             }
             void remove(EventRegistrationToken token)
             {
                _foo::remove(token);
             }
             void raise(Object^ sender, FooEventArgs^ e)
             {
                _foo::raise(sender, e);
             }
          }
          public: virtual void Foo(void)
          {
             return;
          }
       };
    

    The compiler gives the errors:

    1>  Class1.cpp

    error C3766: 'WinRTInterfaceConsumer::FooExternalClass' must provide an implementation for the interface method 'void WinRTInterfaceTest::IFoo::raise_FooEvent(Platform::Object ^,WinRTInterfaceTest::FooEventArgs ^)'

    winrtinterfacetest.winmd : see declaration of 'WinRTInterfaceTest::IFoo::raise_FooEvent'

    error C3612: 'WinRTInterfaceConsumer::FooExternalClass': a sealed class cannot have any pure virtual methods

    The following methods must be defined:

    error C2259: 'WinRTInterfaceConsumer::FooExternalClass' : cannot instantiate abstract class

    due to following members:

    Also, I noticed in the Object Browser window that the IFoo interface, when imported into the consumer dll project, has an extra method:

    unknown-type

    raise_FooEvent(Platform::Object^ sender, WinRTInterfaceTest::FooEventArgs^ e)

        Member of

    WinRTInterfaceTest::IFoo

    Wednesday, August 22, 2012 11:21 PM

Answers

  • Hello,

    Only EventName::add and EventName::remove methods are part of the WinRT ABI contract for an event, so when you're explicitly defining EventName::raise in an interface, you're essentially defining an additional method called raise_EventName inside that interface. This is unfortunate, because for classes that implement the interface, the method name is reserved for the implementation of the event named EventName, so there is no syntax in C++/CX to allow you to implement that specific method (not even when using named overrides). 

    Both of these are bugs in the compiler:

    • The fact that we allow specifying the raise method in an interface definition
    • The fact that we allow overriding such raise method inside a local runtime class

    I added them to our internal bug database and they should be addressed in a future release.

    A workaround is to comment the raise method from the event declaration inside the interface:

       public interface class IFoo
       {
          event EventHandler<FooEventArgs^>^ FooEvent
    	  {
             EventRegistrationToken add(EventHandler<FooEventArgs^>^ value);
             void remove(EventRegistrationToken token);
             //void raise(Object^ sender, FooEventArgs^ e);
          }
          void Foo(void);
       };

    Or even simpler:

       public interface class IFoo
       {
          event EventHandler<FooEventArgs^>^ FooEvent;
          void Foo(void);
       };

    Thanks for reporting this issue.

    Marian Luparu
    Visual C++

    • Marked as answer by BullyOwner Friday, August 24, 2012 1:06 AM
    Friday, August 24, 2012 12:56 AM

All replies

  • Thanks for reporting this! This same report was also filed as a connect bug which is now being investigated by the VS team.

    David Lamb

    Thursday, August 23, 2012 11:34 PM
    Moderator
  • Hello,

    Only EventName::add and EventName::remove methods are part of the WinRT ABI contract for an event, so when you're explicitly defining EventName::raise in an interface, you're essentially defining an additional method called raise_EventName inside that interface. This is unfortunate, because for classes that implement the interface, the method name is reserved for the implementation of the event named EventName, so there is no syntax in C++/CX to allow you to implement that specific method (not even when using named overrides). 

    Both of these are bugs in the compiler:

    • The fact that we allow specifying the raise method in an interface definition
    • The fact that we allow overriding such raise method inside a local runtime class

    I added them to our internal bug database and they should be addressed in a future release.

    A workaround is to comment the raise method from the event declaration inside the interface:

       public interface class IFoo
       {
          event EventHandler<FooEventArgs^>^ FooEvent
    	  {
             EventRegistrationToken add(EventHandler<FooEventArgs^>^ value);
             void remove(EventRegistrationToken token);
             //void raise(Object^ sender, FooEventArgs^ e);
          }
          void Foo(void);
       };

    Or even simpler:

       public interface class IFoo
       {
          event EventHandler<FooEventArgs^>^ FooEvent;
          void Foo(void);
       };

    Thanks for reporting this issue.

    Marian Luparu
    Visual C++

    • Marked as answer by BullyOwner Friday, August 24, 2012 1:06 AM
    Friday, August 24, 2012 12:56 AM
  • Yes, when i remove EventName::raise from the interface, it works fine.  That's OK for me b/c I only really needed add and remove anyway.
    Friday, August 24, 2012 1:06 AM