将 Microsoft COM 转换为 XPCOM






4.84/5 (15投票s)
从 Microsoft COM 到 XPCOM 的逐步创建
文章目的
我为刚开始接触 XPCOM 但对 Microsoft COM 有深入了解的程序员编写了本教程。本文简要介绍了如何编写自己的 XPCOM 对象,并使用 VC8.0 将 MsCOM 转换为 XPCOM。本文不涵盖 XPCOM 的基础知识或概念。
项目设置或开发环境对我来说曾是一个大问题。此外,XPCOM 的注册也是一个棘手的问题,因为必须从配置文件目录中删除 xpti.dat 和 compreg.dat。
我还为 XPCOM 初学者写过一篇名为 A Simple XPCOM Tutorial 的文章。
引言
XPCOM 是一个跨平台组件对象模型,类似于 Microsoft COM。它具有多种语言绑定,允许 XPCOM 组件除了 C++ 之外,还可以用 JavaScript、Java 和 Python 进行使用和实现。XPCOM 中的接口定义在一种称为 XPIDL 的 IDL 方言中。
对我而言,理解 XPCOM 无疑是一次漫长的探索。我认为,任何希望理解 XPCOM 基本原理的程序员,都必须至少用纯 C++ 编写一个简单的 XPCOM 对象。在本文中,我将介绍将 MsCOM 转换为简单 XPCOM 对象并使其能够被 VC++/JavaScript 客户端使用的指导方针。
作为一项练习,我们将尝试设计一个 XPCOM 组件,它将实现一个假想的超快速加法算法。该组件必须接受两个 long 类型的数据参数,并向用户返回另一个 long 类型参数,该参数将是我们加法算法的结果。
如何从 Microsoft COM 创建 XPCOM
第一步:开发环境设置
- 请使用适合您的 XULRunner 版本的 xulrunner SDK,我使用的是 xulrunner-1.9.2。
- 请使用 Microsoft 编译器,我使用的是 Visual C++ 2005。
这是我的文件夹结构:
XPCOM
- xulrunner-sdk
bin
lib
idl
include
- sample_xpcom (xpcom creation)
Debug
Release
第二步:创建 VC++ 项目
- 在 Visual Studio 中启动一个空的 Win32 项目,并选择“动态链接库 (DLL)”选项。
- 进行以下调整:
- 将 “..\xulrunner-sdk\include” 添加到附加包含目录。
- 将 “..\xulrunner-sdk\lib” 添加到附加库目录。
- 将 “nspr4.lib xpcom.lib xpcomglue_s.lib” 添加到附加依赖项。
- 将 “XP_WIN;XP_WIN32” 添加到预处理器定义。
- 关闭预编译头(为了简单起见)。
第三步:创建 IDL 文件
- XPCOM 使用 “
include
” 来导入 IDL 文件。 - 定义接口属性。
- 所有 XPCOM 接口都继承自
nsISupports
接口。因此,请让您的接口继承自 “nsISupports
”。 - 定义库不是必需的。
- 为 XPCOM IDL 文件使用自定义生成步骤(从 MIDL 构建中排除)。
第四步:编译 IDL 文件
-
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)。
第五步:创建实现文件(头文件和 CPP)
头文件
- 包含生成的接口头文件。
- 定义组件契约 ID。组件契约 ID 的推荐格式如下一行字符串:
“@<internetdomain/>/module[/submodule[...]];<version/> [?<name/>=<value/>[&<name/>=<value/>[...]]]”
- 定义类名,与 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 文件不是必需的。
第七步:注册 DLL
- 将您的 XPT 和 DLL 文件复制到 Firefox 的 components 目录。
- 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}"
Using the Code
现在启动示例。
这是 MsCOM 的 IDL 文件。
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
*/
将此 IDL 修改如下:
#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);
};
编译 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 组件的所有客户端。 |
头文件
#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 文件
#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;
}
模块文件
#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)
注意:学习此内容的简便方法是直接使用项目。构建这些项目,您还可以从我的 “A Simple XPCOM Tutorial” 中获得帮助。如果您有任何建议、请求或问题,请告知我。
历史
- 2010 年 7 月 26 日 首次发布