The Airsource Blog

BREW interfaces - how they work, and how to use them

BREW makes heavy use of an interface framework derived from COM. It's very useful to understand the internals of how it works, especially if you want to extend any of the built in classes. In this series of articles, I will discuss:

Part 1. The internals of a BREW interface.

In object-oriented terms, ISHELL_CreateInstance can be considered a factory method, that instantiates classes on request, and returns an interface pointer. As arguments to ISHELL_CreateInstance, you pass in a classid - e.g. AEECLSID_WEB, and as a return you get an interface pointer - e.g. an IWeb. If you happened to know what the class instance - i.e. the AEEWeb instance - looked like, you could cast that IWeb directly to an AEEWeb*, and it would work. That's why the following snippet of code works:

    ISHELL* getShell(void)
    {
        // GETAPPINSTANCE() returns an IApplet
        // We can cast this directly to an 
        // AEEApplet (defined in AEEAppGen.h)
        AEEApplet* applet = (AEEApplet*)GETAPPINSTANCE();
        return applet->m_pIShell;
    }

Let's assume, for the moment, that you want to actually use an interface though, for example IWeb. If you write the following code

    IWEB_Release(pIWeb);

You are not calling a function IWEB_Release. IWEB_Release is actually a macro, and I'll expand it below:

    // IWEB_Release(pIWeb);
    AEEGETPVTBL((pIWeb),IWeb)->Release();

which expands to:

    // IWEB_Release(pIWeb);
    // AEEGETPVTBL((pIWeb),IWeb)->Release();
    (*((AEEVTBL(IWeb)**)((void*)(pIWeb))))->Release();

keep expanding...

    // IWEB_Release(pIWeb);
    // AEEGETPVTBL((pIWeb),IWeb)->Release();
    //(*((AEEVTBL(IWeb)**)((void*)pIWeb)))->Release();
    (*(IWebVtbl**)((void*)pIWeb))->Release();

The situation here is illustrated by the diagram below. When we write IWEB_Release(pIWeb) we start with pIWeb, which happens to point an AEEWeb instance. However, we don't really care what the object is. The only contract we have with is that the first word must point to an instance of an IWebVtbl. The IWebVtbl is a struct, which contains pointers to functions, thus defining the interface that IWeb presents. IWeb in memory

AEEWeb.h contains a long and detailed description of the IWeb interface. This could be condensed down to just the following few lines:

    typedef struct _IWeb
    {
        int (*addref)(struct _IWeb*);
        int (*release)(struct _IWeb*);
        ....
    } IWeb;

and you would write code like this

    ISHELL_CreateInstance(pIShell, AEECLSID_WEB, (void**)&pIWeb;);
    pIWeb->release(pIWeb);

The only reason you don't write code like that, is because the BREW coders defined a lot of helpful macros. They are helpful, most of the time, but they do shield you from what's going on under the scenes.

It should now be clear that the structures underlying a BREW interface are relatively simple. In part 2, I'll go into more depth, and show how to create a simple BREW class from scratch.