September 18th, 2024

More on the mysterious [default_interface] attribute in Windows Runtime classes

There is this mysterious attribute [default_interface] attribute that sometimes gets applied to Windows Runtime classes. What does this attribute mean, and when should you use it?

In the Windows Runtime, the “default interface” for a runtime class is the interface that is used at the ABI level to represent the object. We discussed default interfaces some time ago, so I’ll consider that page background reading.

Normally, the default interface for a class is the autogenerated interface that has the same name as the runtime class (with an “I” in front). But what if there is no interface to autogenerate?

runtimeclass WidgetManager
{
    static void DisconnectAllWidgets();
}

This class consists only of static members. (In this case, it’s a static method.) Those static methods go on the activation factory, as we discussed earlier.

There are no instance members: No instance properties, no instance methods, no instance events. There is nothing to put in the autogenerated IWidget interface.

The MIDL compiler finds this an odd state of affairs. You have an object that you can’t do anything with. What’s the point of that? So the compiler says, “Are you sure?”

There are two responses to this situation.

“Oops, sorry. I didn’t mean for there to be any instances of this class. I should have declared this as a static class.” In that case, make your class a static class.

static runtimeclass WidgetManager
{
    static void DisconnectAllWidgets();
}

Another response is “No really, I want there to be instances of this class. I know it looks funny to have an object that you can’t do anything with, but trust me, that’s what I want.” In that case, you add the [default_interface] attribute.

Now, there are cases where the MIDL compiler thinks that you created an object with no methods, but in fact the object does have methods because the methods are implemented by a base class or an implemented interface.

In the case of an interface, you should make that implemented interface be the default interface, so that the default interface is actually useful for something. If you ask for an empty default interface, then the currency for your object is a pointer to an interface that doesn’t do anything, and any operation people want to perform will have to perform an interface query.

runtimeclass ExtraHeadersHttpFilter : IHttpFilter
{
    ExtraHeadersHttpFilter(
        IIterable<IKeyValuePair<String, String> > headers);
}

The MIDL compiler wonders why you have an Extra­Headers­Http­Filter that you can’t do anything with. What it doesn’t realize is that you can do things with it: You can do IHttpFilter things.

You can tell the MIDL compiler to shut up by saying, “Oh, go ahead, make an empty IExtra­Headers­Http­Filter interface.”

// not the best solution
[default_interface]
runtimeclass ExtraHeadersHttpFilter : IHttpFilter
{
    ExtraHeadersHttpFilter(
        IIterable<IKeyValuePair<String, String> > headers);
}

This is not the best solution because it means that passing an Extra­Headers­Http­Filter object as a parameter passes the (empty) IExtra­Headers­Http­Filter interface, which is not directly usable since it has no members. If somebody wants to call the IHttp­Filter.Send­Request­Async method, they will have to perform a Query­Interface to convert the IExtra­Headers­Http­Filter to an IHttp­Filter, call the Send­Request­Async method, and then release the IHttp­Filter interface. Much more efficient would be use IHttp­Filter as the default interface, so that the recipient can just call IHttp­Filter methods immediately.

Therefore, a better fix here is not to add [default_interface] to say “That’s okay, give me an empty default interface.” Instead, you say that IHttpFilter is your default interface.

runtimeclass ExtraHeadersHttpFilter : [default] IHttpFilter
{
    ExtraHeadersHttpFilter(
        IIterable<IKeyValuePair<String, String> > headers);
}

Another case where it looks like you have an empty object, but it really does have members is the case where you derive from another class, and the interesting methods are on the base class.

runtimeclass MyPage : Page
{
    MyPage();
}

The Windows Runtime doesn’t let you name a base class as a default interface, so you are forced to create an empty default interface.

[default_interface]
runtimeclass MyPage : Page
{
    MyPage();
}

Bonus chatter: What goes wrong if I say [default_interface] when my runtime class does contain instance members? Do I get two interfaces, an empty default interface and a second interface that has the instance members?

No. If the runtime class has instance members, then [default_interface] is redundant but not harmful. There was going to be a default interface anyway.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

6 comments

Discussion is closed. Login to edit/delete existing comments.

  • Letao Wang

    In the last example, is there a way to say “the default interface for MyPage is IPage” (or whatever the default interface for Page is)? That seems like the most desirable outcome in that situation.

    • Me Gusta

      No.
      While it is easy to see this syntax and think of C++/C# inheritance, MyPage does not inherit from Page, and by extension, does not implement IPage.
      What the above syntax states is composition. Basically, the instance of MyPage contains an instance of Page. This means that MyPage has to go through Page to use Page's functionality. But there is nothing stopping you from doing something like:
      <code>

      You only really get into this situation when...

      Read more
      • Me Gusta

        Okay, first of all, this post is going to go into wall of text territory. But this is required to provide all of the information that you require to understand what is going on here.
        Secondly, I will remind you that I already mentioned that there is a simple way of effectively doing what you want in this situation.

        MyPage myPage{};
        auto page = myPage.as<Page>();

        I put this outside of a code block this time to make...

        Read more
      • Letao Wang · Edited

        What you describe sounds like "MyPage doesn't implement IPage, and we don't want it to implement IPage, but the projection nevertheless behaves as if it does, and you can still call IPage methods on MyPage". So it effectively does implement IPage from a functional point of view, but IPage is not an accessible interface from a declarative point of view.

        I mean, I understand your point that it's all implemented based on COM which is...

        Read more
      • Me Gusta

        @Letao Wang
        As an answer, this will likely require a huge block of text. It would have to take into account exclusiveto, meaning MyPage potentially couldn't declare IPage as one of the implemented interfaces.
        Also, it seems like you misunderstood something, there may not be an inheritance relationship between MyPage and Page, but MyPage has access to all of Page's functionality. On the projection level, the wrapper classes would generate member functions for all of...

        Read more
      • Letao Wang

        Thanks for the reply. I’m still not clear on why it’s not doable though. If MyPage doesn’t inherit IPage from Page, then declaring IPage to be the default interface for MyPage would be asking for MyPage to implicitly implement IPage, and every IPage method being called on MyPage gets directly forwarded to the underlying Page. This achieves the desirable behavior despite there being no inheritance relationship.