Purpose of the Article
I have written this tutorial for programmers who are just starting out in XPCOM but have good knowledge in Microsoft COM. The article briefly covers writing your own XPCOM object converting MsCOM using VC8.0.This article does not cover XPCOM Basics or concepts.
Project Settings or Development Setup was a big problem for me. Also registering XPCOM is another troublesome issue because xpti.dat and compreg.dat must be deleted from profile directory.
I have also written an article for XPCOM beginners A Simple XPCOM Tutorial.
Introduction
XPCOM is a cross platform component object model, similar to Microsoft COM. It has multiple language bindings, letting the XPCOM components be used and implemented in JavaScript, Java, and Python in addition to C++. Interfaces in XPCOM are defined in a dialect of IDL called XPIDL.
For me, understanding XPCOM has been no less than an odyssey. I believe that every programmer who wishes to understand the basic principles behind XPCOM, must write at least one simple XPCOM object using plain C++. In this article, I present the guidelines for creating simple XPCOM object converting MsCOM. The components should be usable by both VC++/ JavaScript clients.
As an exercise, we will attempt to design a XPCOM component that will implement a hypothetical super-fast addition algorithm. The component must take in two parameters of long data type, and return to the user another long parameter that will be an outcome of our addition algorithm.
How to Create XPCOM from Microsoft COM
Step 1: Development Setup
- Use the right xulrunner SDK for your XULRunner release, I use xulrunner-1.9.2
- Use a Microsoft compiler, I use Visual C++2005
Here is what my folder structure looks like:
XPCOM
- xulrunner-sdk
bin
lib
idl
include
- sample_xpcom (xpcom creation)
Debug
Release
Step 2: Create a VC++ Project
- Start an empty Win32 project in Visual Studio, selecting the "Dynamic Linked Library (DLL)" option.
- Make the following tweaks:
- Add “..\xulrunner-sdk\include” to Additional Include Directories
- Add “..\xulrunner-sdk\lib” to Additional Library Directories
- Add “nspr4.lib xpcom.lib xpcomglue_s.lib” to Additional Dependencies
- Add “XP_WIN;XP_WIN32? to Preprocessor Definitions
- Turnoff precompiled headers (just to keep it simple)
Step 3: Create the IDL File
- XPCOM uses “
include
” to import IDL files. - Define interface attributes.
- All XPCOM interfaces inherit the
nsISupports
interface. So, inherit your interface from “nsISupports
”. - Defining library is not required.
- Use a custom build step for the XPCOM IDL file (exclude from build by MIDL)
Step 4: Compile the IDL File
MsCOM is compiled with MIDL.exe. But XPCOM is compiled with XPIDL.exe. For MsCOM
{path_to_ Vs_Tools }\bin\Midl.exe /I /Oicf {your_idl_file}
- For XPCOM, compile twice with xpidl to create header and typelibrary.
{path_to_ xulrunner-sdk }\bin\xpidl.exe
-m header -I..\ xulrunner-sdk \idl {your_idl_file}
{path_to_ xulrunner-sdk }\bin\xpidl.exe
-m typelib -I..\ xulrunner-sdk \idl {your_idl_file}
A type-library (*.XPT) and a C++ header file (*.H) will be created.
Step 5: Create the Implementation Files (header and CPP)
Header File
- Include interface generated header file.
- Define Component Contract Id. The recommended format of a contract ID is a one-line string as follows:
"@<internetdomain/>/module[/submodule[...]];<version/>
[?<name/>=<value/>[&<name/>=<value/>[...]]]"
- Define class name, same as help string of MSCOM attributes - Define component ID, that is UUID of the interface.
- Define the class just derives the interface.
- Declare COM entry map.
For MsCOM:
BEGIN_COM_MAP(YourClassName)
COM_INTERFACE_ENTRY(InterfaceName)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
But for XPCOM:
NS_DECL_ISUPPORTS
NS_DECL_ISAMPLE
- Declare methods and properties.
CPP File
- Include header file.
- Implement com Entry map.
- Define methods.
Step 6: Create Module File
- Define Module file which contains the XPCOM component.
- RGS file is not necessary, because XPCOM doesn’t use Windows registry.
- def file is not necessary.
Step 7: Register the DLL
Using the Code
Now start the example:
This is the IDL file for MsCOM.
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(668C357B-5FB9-4743-8FE8-591B40ECE9A2),
dual,
nonextensible,
helpstring("ISampleAdd Interface"),
pointer_default(unique)
]
interface ISampleAdd : IDispatch
{
[id(1), helpstring("method Add")] HRESULT Add([in] LONG FisrtNumber, [in]
LONG SecondNumber, [out,retval] LONG* ResultValue);
};
Change this IDL like below:
#include "nsISupports.idl"
[scriptable, uuid(658ABC9E-29CC-43E9-8A97-9D3C0B67AE8B)]
interface ISample : nsISupports
{
long Add(in long a, in long b);
};
Compile the IDL:
- For MsCOM, we can compile like:
MidL /I /Oicf Sample_mscom.idl
Compiling with midl creates 5 files:
Sample_mscom.h | Contains the C++ style interface declarations. |
dlldata.c | Contains code for proxy DLL. Useful when invoking the object on a different process/computer. |
Sample_mscom.tlb | Binary file, with a well defined format that completely describes our interface IAdd along with all its methods. This file is to be distributed to all the clients of our COM component. |
Sample_mscom_p.c | Contains marshalling code for proxy DLL. Useful while invoking the object on a different process/computer. |
Sample_mscom_i.c | Contains the interface IIDs |
- But for XPCOM, we need to compile with xpidl. It creates one file in each compilation.
Here we have to compile like:
{path_to_ xulrunner-sdk }\bin\xpidl.exe
-m header -I ..\ xulrunner-sdk \idl ISample.idl
{path_to_ xulrunner-sdk }\bin\xpidl.exe
-m typelib -I ..\ xulrunner-sdk \idl ISample.idl
Here we will get two files:
ISample.h | Contains the C++ style interface declarations. |
ISample.xpt | Binary file, with a well defined format that completely describes our interface IAdd along with all its methods. This file is to be distributed to all the clients of our XPCOM component. |
Header File
#include "ISample.h"
#define SAMPLE_COMPONENT_CONTRACTID "@cn.ibm.com/XPCOM/sample;1"
#define SAMPLE_COMPONENT_CLASSNAME "Sample XPCOM Interface Layer"
#define SAMPLE_COMPONENT_CID { 0x658abc9e, 0x29cc, 0x43e9,
{ 0x8a, 0x97, 0x9d, 0x3c, 0x0b, 0x67, 0xae, 0x8b } }
class CSample : public ISample
{
public:
NS_DECL_ISUPPORTS
NS_DECL_ISAMPLE
CSample();
virtual ~CSample();
int Add();
};
CPP File
#include "Sample.h"
NS_IMPL_ISUPPORTS1(CSample, ISample)
CSample::CSample()
{
}
CSample::~CSample()
{
}
NS_IMETHODIMP CSample::Add(PRInt32 a, PRInt32 b, PRInt32 *_retval)
{
*_retval = a + b;
return NS_OK;
}
Module File
#include "nsIGenericFactory.h"
#include "Sample.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(CSample)
static nsModuleComponentInfo components[] =
{
{
SAMPLE_COMPONENT_CLASSNAME,
SAMPLE_COMPONENT_CID,
SAMPLE_COMPONENT_CONTRACTID,
CSampleConstructor,
}
};
NS_IMPL_NSGETMODULE("sample_module", components)
Note: An easy and simple way of learning this is to just use the projects. Build these projects and also you can get help from my "A Simple XPCOM Tutorial". If there are any suggestions, requests or problems, please inform me.
History
- 26.07.2010 Initial release