XPCOM(跨平台组件对象模型)是一种类似于Microsoft COM的技术,它支持多种语言绑定,使得XPCOM组件不仅可以在C++中使用,还可以在JavaScript、Java和Python中实现和使用。XPCOM的接口定义在一种叫做XPIDL的IDL方言中。对于希望理解XPCOM背后基本原理的程序员来说,至少应该使用纯C++编写一个简单的XPCOM对象。本文提供了创建简单XPCOM对象的指导,这些对象可以被VC++/JavaScript客户端使用。
对于刚开始接触XPCOM的程序员来说,项目设置或开发环境的搭建可能是一个难题。同样,注册XPCOM也是一个问题,因为必须从个人资料目录中删除xpti.dat和compreg.dat。
将尝试设计一个XPCOM组件,该组件将实现一个假设的超快速加法算法。该组件必须接受两个长整型参数,并返回一个长整型参数,作为加法算法的结果。
使用适合XULRunner版本的xulrunner SDK,使用的是xulrunner-1.9.2。使用Microsoft编译器,使用的是Visual C++2005。
文件夹结构如下:
XPCOM
- xulrunner-sdk
- bin
- lib
- idl
- include
- sample_xpcom (xpcom创建)
- Debug
- Release
在Visual Studio中启动一个空的Win32项目,选择"Dynamic Linked Library (DLL)"选项。进行以下调整:
XPCOM使用“include”来导入IDL文件。定义接口属性。所有XPCOM接口都继承自nsISupports接口。因此,接口应该从nsISupports继承。不需要定义库。使用自定义构建步骤为XPCOM IDL文件(通过MIDL排除构建)
MsCOM使用MIDL.exe编译。但XPCOM使用XPIDL.exe编译。对于MsCOM:
{path_to_Vs_Tools}\bin\Midl.exe /I /Oicf {your_idl_file}
对于XPCOM,使用xpidl编译两次以创建头文件和类型库:
{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}
将创建一个类型库(*.XPT)和一个C++头文件(*.H)。
头文件:
对于MsCOM:
BEGIN_COM_MAP(YourClassName)
COM_INTERFACE_ENTRY(InterfaceName)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
但对于XPCOM:
NS_DECL_ISUPPORTS
NS_DECL_ISAMPLE
声明方法和属性。
CPP文件:
定义包含XPCOM组件的模块文件。不需要RGS文件,因为XPCOM不使用Windows注册表。不需要.def文件。
将XPT和DLL文件复制到Firefox组件目录。MsCOM使用REGSVR32.exe注册。
regsvr32 {yourMsCOM dll}
但XPCOM使用REGXPCOM.exe注册。从个人资料目录中删除xpti.dat和compreg.dat(如果存在components.list文件,请在末尾添加yourXPCOM DLL)。Firefox将在下次重启时重新生成它们,或者可以使用regxpcom.exe:
regxpcom -x {FireFoxDir}\bin {FireFoxDir}\components\{yourXPCOM dll}
现在开始示例:
这是MsCOM的IDL文件。
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);
};
将此IDL更改为:
C++
#include "nsISupports.idl"
[scriptable, uuid(658ABC9E-29CC-43E9-8A97-9D3C0B67AE8B)]
// 这里使用了scriptable,以支持脚本语言访问此组件
interface ISample : nsISupports
{
long Add(in long a, in long b);
};
编译IDL:
对于MsCOM,可以这样编译:
MIDL /I /Oicf Sample_mscom.idl
使用MIDL编译会创建5个文件:
但对于XPCOM,需要使用xpidl编译。每次编译都会创建一个文件。
{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
这里将得到两个文件:
头文件:
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:
/*
使用此宏声明实现此接口的类。 */
NS_DECL_ISUPPORTS
NS_DECL_ISAMPLE
CSample();
virtual ~CSample();
// 其他成员函数
int Add();
};
CPP文件:
C++
#include "Sample.h"
// 此宏自动添加nsISupports入口
NS_IMPL_ISUPPORTS1(CSample, ISample)
CSample::CSample()
{
/*
成员初始化器和构造函数代码 */
}
CSample::~CSample()
{
/*
析构函数代码 */
}
/*
long Add (in long a, in long b); */
NS_IMETHODIMP CSample::Add(PRInt32 a, PRInt32 b, PRInt32 *_retval)
{
*_retval = a + b;
return NS_OK;
}
模块文件:
C++
#include "nsIGenericFactory.h"
#include "Sample.h"
// 这将导致一个名为CSampleConstructor的函数
// 可以在nsModuleComponentInfo结构中使用。
NS_GENERIC_FACTORY_CONSTRUCTOR(CSample)
static nsModuleComponentInfo components[] =
{
{
SAMPLE_COMPONENT_CLASSNAME,
SAMPLE_COMPONENT_CID,
SAMPLE_COMPONENT_CONTRACTID,
CSampleConstructor,
}
};
// 使用模块名称和components中的组件列表实现nsIModule接口。
NS_IMPL_NSGETMODULE("sample_module", components)