Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Convert Microsoft COM to XPCOM

4.84/5 (15 votes)
26 Jul 2010CPOL4 min read 42.5K   458  
Step by step XPCOM creation from Microsoft COM

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/>[&amp;<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

  • Copy your XPT and DLL files to the Firefox components directory.
  • MsCOM is registered with REGSVR32.exe.
    "regsvr32 {yourMsCOM dll} "
  • But XPCOM is registered with REGXPCOM.exe.
  • Delete xpti.dat and compreg.dat from your profile directory (and if there exists "components.list" file, add "yourXPCOM DLL" at the end). Firefox will regenerate them on next restart or we can use regxpcom.exe like this:
    "regxpcom -x { FireFoxDir }\bin {FireFoxDir}\components\{yourXPCOM dll}"

Using the Code

Now start the example:

This is the IDL file for MsCOM.

C++
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);
};
    
/*
Definition of library which is not required for xpcom
*/

Change this IDL like below:

C++
#include "nsISupports.idl"

[scriptable, uuid(658ABC9E-29CC-43E9-8A97-9D3C0B67AE8B)]
// here I used scpiptable to support scripting languages to access this component
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.hContains the C++ style interface declarations.
    dlldata.cContains code for proxy DLL. Useful when invoking the object on a different process/computer.
    Sample_mscom.tlbBinary 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.cContains marshalling code for proxy DLL. Useful while invoking the object on a different process/computer.
    Sample_mscom_i.cContains 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.hContains the C++ style interface declarations.
    ISample.xptBinary 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

C++
#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:
    /* Use this macro when declaring classes that implement this interface. */
    NS_DECL_ISUPPORTS 
    NS_DECL_ISAMPLE
    	
    CSample();
    virtual ~CSample();

    //additional member functions
    int Add();
};

CPP File

C++
#include "Sample.h"

//This macro automatically adds the nsISupports entry
NS_IMPL_ISUPPORTS1(CSample, ISample)

CSample::CSample()
{
    /* member initializers and constructor code */
}

CSample::~CSample()
{
    /* destructor code */
}

/* long Add (in long a, in long b); */
NS_IMETHODIMP CSample::Add(PRInt32 a, PRInt32 b, PRInt32 *_retval)
{
    *_retval = a + b;
    	
    return NS_OK;
}

Module File

C++
#include "nsIGenericFactory.h"
#include "Sample.h"

// This results in a function called CSampleConstructor that 
// can be used in the nsModuleComponentInfo structure.
NS_GENERIC_FACTORY_CONSTRUCTOR(CSample)

static nsModuleComponentInfo components[] =
{
    {
	    SAMPLE_COMPONENT_CLASSNAME,
	    SAMPLE_COMPONENT_CID,
	    SAMPLE_COMPONENT_CONTRACTID,
	    CSampleConstructor,
    }
};

// Implements the nsIModule interface with the module name of name and 
// the component list in components.
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)