This is a simple tutorial for building XPCOM objects in C++ using Visual Studio. XPCOM is Mozilla’s cross platform component object model, similar to Microsoft’s COM technology. XPCOM components can be implemented in C, C++, and JavaScript, and can be used from C, C++, and JavaScript. That means you can call JavaScript methods from C++ and vice versa. For more information on the workings of XPCOM look elsewhere.
The simplest way to get an XPCOM component built is to use the Gecko SDK. On Windows, the SDK is built using a Microsoft compiler, so you need to use one too. This tutorial uses Microsoft’s free Visual C++ Express and the sample project in the next paragraph. You could try to use a different vendor's compiler, but you are going to need to build the SDK from source code. Not the simplest thing to do and it may be incompatible with production releases of Firefox, Thunderbird, and XULRunner from Mozilla.
For example, XULRunner 1.8.0.4 which has a pre-built SDK at gecko-sdk-win32-msvc-1.8.0.4.zip. Extract the SDK to a folder. The tutorial assumes the folder is called xulrunner-1.8.0.4, but you can call yours whatever you want.
You also need a couple of pre-built libraries (glib-1.2.dll & libIDL-0.6.dll) from the wintools.zip archive. Copy glib-1.2.dll & libIDL-0.6.dll into the bin subfolder of gecko-sdk.
Recap:
Here is what the folder structure looks like:
Visual Studio project and file templates (or wizards) for creating XPCOM modules and components do not currently exist. For now, you can use the included XPCOM project that contains a simple XPCOM component. You can use the project as a starting point and modify the component files to add your own functionality.
To make the project, you start with a standard multi-thread DLL project. Then make the following tweaks:
VC++ Express Project: xpcom-test.zip
A full tutorial of XPCOM is beyond the scope of this posting. Check out the resources at the end of the tutorial for more information on the world of XPCOM. Ok then, on with the basic, oversimplified example. Your XPCOM component is made up of 3 parts:
Let’s specify a simple interface:
#include "nsISupports.idl" [scriptable, uuid(263ed1ba-5cc1-11db-9673-00e08161165f)] interface ISpecialThing : nsISupports { attribute AString name; long add(in long a, in long b); };
Remember to generate your own GUID. The next step is to compile the IDL into a type-library (*.XPT) and a C++ header file (*.H), which we can use to define our implementation object. The blank VC++ project has a BAT file that will create the XPT and the H files. The command executes XPIDL.EXE twice, like this:
{path_to_geckosdk}\bin\xpidl.exe -m header -I..\gecko-sdk\idl {your_idl_file} {path_to_geckosdk}\bin\xpidl.exe -m typelib -I..\gecko-sdk\idl {your_idl_file}
The generated H file actually has a skeleton implementation (commented out). You can take the code and create implementation H and CPP files. They could look like this:
H file:
#ifndef __SPECIALTHING_IMPL_H__ #define __SPECIALTHING_IMPL_H__ #include "comp.h" #include "nsStringAPI.h" #define SPECIALTHING_CONTRACTID "@starkravingfinkle.org/specialthing;1" #define SPECIALTHING_CLASSNAME "SpecialThing" #define SPECIALTHING_CID { 0x245626, 0x5cc1, 0x11db, { 0x96, 0x73, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f } } class CSpecialThing : public ISpecialThing { public: NS_DECL_ISUPPORTS NS_DECL_ISPECIALTHING CSpecialThing(); private: ~CSpecialThing(); protected: /* additional members */ nsString mName; }; #endif
CPP file:
#include "comp-impl.h" NS_IMPL_ISUPPORTS1(CSpecialThing, ISpecialThing) CSpecialThing::CSpecialThing() { /* member initializers and constructor code */ mName.Assign(L"Default Name"); } CSpecialThing::~CSpecialThing() { /* destructor code */ } /* attribute AString name; */ NS_IMETHODIMP CSpecialThing::GetName(nsAString & aName) { aName.Assign(mName); return NS_OK; } NS_IMETHODIMP CSpecialThing::SetName(const nsAString & aName) { mName.Assign(aName); return NS_OK; } /* long add (in long a, in long b); */ NS_IMETHODIMP CSpecialThing::Add(PRInt32 a, PRInt32 b, PRInt32 *_retval) { *_retval = a + b; return NS_OK; }
Lastly, we need to create the module implementation. I put this together from some samples I found on the MDC site:
#include "nsIGenericFactory.h" #include "comp-impl.h" NS_GENERIC_FACTORY_CONSTRUCTOR(CSpecialThing) static nsModuleComponentInfo components[] = { { SPECIALTHING_CLASSNAME, SPECIALTHING_CID, SPECIALTHING_CONTRACTID, CSpecialThingConstructor, } }; NS_IMPL_NSGETMODULE("SpecialThingsModule", components)
Assuming you have the right SDK and setup the include and LIB folders correctly, the project should build your XPCOM component.
In order to test your component in a XULRunner application, you need to “install” the component, “clear” the component registry, and use the component from JavaScript.
function doXPCOM() { try { const cid = "@starkravingfinkle.org/specialthing;1"; var obj = Components.classes[cid].createInstance(); obj = obj.QueryInterface(Components.interfaces.ISpecialThing); } catch (err) { alert(err); return; } var res = obj.add(3, 4); alert('3+4 = ' + res); var name = obj.name; alert('Name = ' + name); obj.name = 'New Name'; name = obj.name; alert('Name = ' + name); }