Interfacing with XPCOM

John Reimer terminal.node at gmail.com
Sun Nov 30 15:48:47 PST 2008


As the title suggests, this post is about D interfaces, specifically as supported 
in D 1.0.  

Here's some background:

I've been working on porting the SWT Browser package to DWT (on Linux) over 
the last few months.  I'm happy to say that I've mostly succeeded despite 
some rather annoying problems.  The important issues have been ironed out. 
 What remains now is to get down to some heavy debugging sessions.

SWT implements its Browser code based on the mozilla XPCOM interface.  What 
this means is that in order to use the SWT Browser in your project, you need 
to install a mozilla package called XULRunner that provides all the components 
that make up a working browser.  XULRunner is basically the Mozilla Team's 
way of separating the internals of Mozilla Firefox into a shared package 
that serves any number of applications that want to embed browser components. 
 As of Firefox 3.0, the Mozilla Foundation has moved to this XPCOM component 
server system; it seems that they now include the XULRunner package within 
the Firefox installation instead of making it an integrated component of 
the browser.

Obviously this scheme is useful for all sorts of apps that want to embed 
a comprehensive browser.  The only requirement is that development language 
know how to interface to the Mozilla components.  So the key here is that 
a language must be compatible with XPCOM.

This is no problem for D on windows machines because D provides a mechanism 
of direct COM interface support on that platform.  XPCOM happens to be binary 
compatible with the vtable layout of the COM structure, so, on windows, all 
one has to do is alias the XPCOM's base interface nsISupports to the COM 
IUknown.  The D compiler takes care of the rest since it automatically adjusts 
the vtable when it sees a reference to the IUnknown COM type.

But how does one do this on Linux?  Can we use D to interface with XPCOM 
there?  The problem is that this will not work on Linux since COM has no 
meaningful implementation on the platform primarily because all COM components 
are declared extern(Windows) internally by the D compiler.   This would appear 
to mean that Linux is left out in the cold when it comes to interfacing with 
anything like XPCOM.  D interfaces obviously won't work because they are 
constitute a new type, independent of any relationship to any other language 
interface (D intefaces insert a vtable entry at index 0 that references RTTI).

There is, in fact, a solution for Linux:

We can use the COM interface alias technique just as we would on windows.

[code]

alias IUknown nsISupports;

[/code]

Even on linux, the D compiler will create a COM clean vtable with windows 
calling convention for the methods.  You don't actually notice that the compiler 
has done this until you try to implement some classes based on the interface, 
at which point the compiler protests loudly.  Now on linux, we need to get 
rid of the windows calling convention in order to make the interfaces XPCOM 
compatible.  The best way to do this is to directly override the compiler 
by declaring each interface method extern(C); this is something XPCOM wants 
to see.

[code]

interface IUnknown
{
    static const char[] IID_STR = NS_ISUPPORTS_IID_STR;
    static const nsIID IID = NS_ISUPPORTS_IID;

extern(System):
    nsresult QueryInterface( nsIID* uuid, void **result);
    nsrefcnt AddRef();
    nsrefcnt Release();
}

[/code]

I've used extern(System) to demonstrate a quick way to do this for the particular 
platform so the code is portable.  Now we have a simple COM interface with 
extern(C) calling convention that will interface acceptably with Linux XPCOM.

There's still a problem here, however.  Since we are using IUnknown, the 
D compiler is going to secretly force all /class/ methods that implement 
the contract methods to also use extern(Windows).  So within any class that 
implements the interface, we also have to apply the extern(System) decoration:

[code]

class A : nsISupports {
    extern(System) nsresult QueryInterface( nsIID* uuid, void **result) {} 
   
    ...
}

[/code]

But what about the class methods that don't implement the interface?  Well, 
they have to be explicitly declared extern(D) in order to keep the compiler 
from complaining again.

[code]

class A : nsISupports {
    extern(System) nsresult QueryInterface( nsIID* uuid, void **result) {} 
   
    ...
    extern(D) void handleFolderEvent( Event event );
}

[/code]

The above code worked in porting the Browser code to DWT specifically for 
D version 1.0.  But I have to admit that I remain a little uncomfortable 
implementing the fix this way.  It's a hack.  Unfortunately, it's the only 
way I know that simply and effectively solves this problem.  

Here are a couple of thoughts I have:

(1) I'm wondering if the implicit COM support (triggered by the IUnknown 
symbol only) implemented in D is such a good idea.  Would such a feature 
be more correctly supported as a Pragma wherever an interface vtable type 
is augmented in a system specific way?  pragma("COM") {} or something to 
that effect.

(2) As an alternate solution to my trick with COM on linux, there is also 
the option of using extern(C++), except that it only exists in D 2.0.  I 
think using extern(C++) might work in this situation because mozilla XPCOM 
interfaces are often represented in C++ using single inheritance C++ class 
structures.  The vtables should match unless I'm missing some important detail. 
 Porting extern(C++) to D 1.0 should not constitute a spec change, and it 
could mean simpler support for Mozilla XPCOM without ugly workarounds.  Does 
this sound reasonable?

For more information, I've also covered the same material here:

http://www.dsource.org/projects/dwt/wiki/PortingJournal

Here's a link to the Walter's discription of D 2.0 Interfaces and the addition 
of extern(C++) along with its caveats:

http://www.digitalmars.com/d/2.0/cpp_interface.html

Notice that in the above link Walter says COM only works on windows. ;)

-JJR





More information about the Digitalmars-d mailing list