XPCOM与MsCOM转换指南

XPCOM(跨平台组件对象模型)是一种类似于Microsoft COM的技术,它支持多种语言绑定,使得XPCOM组件不仅可以在C++中使用,还可以在JavaScript、Java和Python中实现和使用。XPCOM的接口定义在一种叫做XPIDL的IDL方言中。对于希望理解XPCOM背后基本原理的程序员来说,至少应该使用纯C++编写一个简单的XPCOM对象。本文提供了创建简单XPCOM对象的指导,这些对象可以被VC++/JavaScript客户端使用。

开发设置

对于刚开始接触XPCOM的程序员来说,项目设置或开发环境的搭建可能是一个难题。同样,注册XPCOM也是一个问题,因为必须从个人资料目录中删除xpti.dat和compreg.dat。

创建XPCOM对象

将尝试设计一个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)"选项。进行以下调整:

  • 将“..\xulrunner-sdk\include”添加到Additional Include Directories
  • 将“..\xulrunner-sdk\lib”添加到Additional Library Directories
  • 将“nspr4.lib xpcom.lib xpcomglue_s.lib”添加到Additional Dependencies
  • 将“XP_WIN;XP_WIN32”添加到Preprocessor Definitions
  • 关闭预编译头文件(只是为了保持简单)

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)。

头文件:

  • 包含生成的接口头文件。
  • 定义组件合同ID。合同ID的推荐格式如下:
  • 定义类名,与MSCOM属性的帮助字符串相同 - 定义组件ID,即接口的UUID。
  • 定义类,使其派生自接口。
  • 声明COM入口映射。

对于MsCOM:

BEGIN_COM_MAP(YourClassName) COM_INTERFACE_ENTRY(InterfaceName) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP()

但对于XPCOM:

NS_DECL_ISUPPORTS NS_DECL_ISAMPLE

声明方法和属性。

CPP文件:

  • 包含头文件。
  • 实现com入口映射。
  • 定义方法。

定义包含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个文件:

  • Sample_mscom.h:包含C++风格的接口声明。
  • dlldata.c:包含代理DLL的代码。在不同进程/计算机上调用对象时有用。
  • Sample_mscom.tlb:二进制文件,以定义良好的格式完全描述接口IAdd及其所有方法。此文件需要分发给COM组件的所有客户端。
  • Sample_mscom_p.c:包含代理DLL的封送代码。在不同进程/计算机上调用对象时有用。
  • Sample_mscom_i.c:包含接口IID。

但对于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

这里将得到两个文件:

  • ISample.h:包含C++风格的接口声明。
  • ISample.xpt:二进制文件,以定义良好的格式完全描述接口IAdd及其所有方法。此文件需要分发给XPCOM组件的所有客户端。

头文件:

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)
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485