简单的 XPCOM 教程






4.93/5 (27投票s)
逐步介绍 C++ 中 XPCOM 的创建和实现。
本文目的
我写这篇教程是给刚开始学习 XPCOM 的程序员。本文简要介绍了如何编写自己的 XPCOM 对象以及如何使用 VC8.0 重用现有的 XPCOM 组件。本文不涵盖 XPCOM 的基础知识或概念。
项目设置和开发环境的搭建对我来说是一个大问题。另外,XPCOM 的注册是另一个棘手的问题,因为必须从配置文件目录中删除 xpti.dat 和 compreg.dat。
引言
XPCOM 是一个跨平台的组件对象模型,类似于 Microsoft COM。它支持多种语言绑定,允许 XPCOM 组件除了 C++ 之外,还可以用 JavaScript、Java 和 Python 使用和实现。XPCOM 中的接口定义在一种称为 XPIDL 的 IDL 方言中。
对我来说,理解 XPCOM 无疑是一场史诗般的旅程。我相信,每个希望了解 XPCOM 基本原理的程序员都必须用纯 C++ 编写至少一个简单的 XPCOM 对象。在本文中,我将从基本原理出发,为创建简单的 XPCOM 对象提供指南。这些组件应该能够被 VC++ / JavaScript 客户端同时使用。
作为一项练习,我们将尝试设计一个 XPCOM 组件,它将实现一个假设的超快速加法算法。该组件必须接受两个 long
数据类型的参数,并向用户返回另一个 long
参数,该参数将是我们加法算法的结果。
如何编写 C++ XPCOM 组件
步骤 1:开发环境设置
- 为您的 XULRunner 版本使用正确的 XULRunner SDK,我使用的是 xulrunner-1.9.2。
- 使用 Microsoft 编译器,我使用的是 Visual C++ 2005。
这是我的文件夹结构
XPCOM
- xulrunner-sdk
bin
lib
idl
include
- sample_xpcom (xpcom creation)
Debug
Release
步骤 2:创建 VC++ 项目
- 在 Visual Studio 中启动一个空的 Win32 项目,并选择“动态链接库 (DLL)”选项。
- 创建一个描述您组件接口的 IDL 文件。
- 对 IDL 文件运行 xpidl 两次,命令如下:
- "xpidl -I < xulrunner-sdk/idl 路径> -m header <您的 IDL 文件名>", 以及
- "xpidl -I < xulrunner-sdk/idl 路径> -m typelib <您的 IDL 文件名>"。
- 进行以下调整:
- 将 "..\xulrunner-sdk\include" 添加到附加包含目录。
- 将 "..\xulrunner-sdk\lib" 添加到附加库目录。
- 将 "nspr4.lib xpcom.lib xpcomglue_s.lib" 添加到附加依赖项。
- 将 "XP_WIN;XP_WIN32" 添加到预处理器定义。
- 关闭预编译头(为保持简单)。
- 在 C++ 命令行附加选项中添加 "/Zc:wchar_t-" 以支持
wchar_t
。 - 为 XPCOM IDL 文件使用自定义生成步骤(从 MIDL 生成中排除)。
步骤 3:创建 XPCOM 组件
XPCOM 组件由三部分组成:
- 使用 IDL 描述的组件接口。接口定义了组件的方法,包括参数和返回类型。
- 使用 C++ 实现的组件。实现部分是方法实际执行工作的地方。
- 组件工厂模块,也用 C++ 编写。工厂负责创建实现实例。
- 构建您的组件。
步骤 4:注册 XPCOM 组件
- 将您的 XPT 和 DLL 文件复制到 Firefox 的组件目录。
- 通常,如果这是作为扩展的一部分安装的,它会自动搜索此目录并找到这些文件。但现在,我们必须强制刷新。从您的配置文件目录中删除 "xpti.dat" 和 "compreg.dat" 文件。在 "components.list" 文件的末尾添加 "<您的 xpcom 组件名>.dll"。
- Firefox 将在下次重启时重新生成它们,或者我们可以使用 regxpcom.exe,如下所示:
"regxpcom -x < xulrunner-sdk >\bin \components\<yourxpcom>.dll"
但我发现这个命令有一些问题。所以,我使用了这种方法:
"regxpcom -x < FireFoxDir >\bin \components\<yourxpcom>.dll"
现在启动示例
让我们定义一个简单的接口:
#include "nsISupports.idl"
[scriptable, uuid(658ABC9E-29CC-43E9-8A97-9D3C0B67AE8B)]
interface ISample : nsISupports
{
long Add(in long a, in long b);
};
请记住生成您自己的 GUID。
下一步是将 IDL 编译成类型库(*.XPT)和 C++ 头文件(*.H),我们可以使用它们来定义我们的实现对象。我们必须像这样使用 XPIDL.EXE 两次:
- {xulrunner-sdk 的路径}\bin\xpidl.exe -m header -I..\ xulrunner-sdk \idl {您的 IDL 文件}
- {xulrunner-sdk 的路径}\bin\xpidl.exe -m typelib -I..\ xulrunner-sdk \idl {您的 IDL 文件}
生成的 H 文件实际上包含了一个(被注释掉的)骨架实现。
您可以复制代码并创建实现 H 和 CPP 文件。它们看起来可能像这样:
#ifndef _SAMPLE_H_
#define _SAMPLE_H_
#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 } }
//658abc9e-29cc-43e9-8a97-9d3c0b67ae8b
class CSample : public ISample
{
public:
NS_DECL_ISUPPORTS
NS_DECL_ISAMPLE
CSample();
virtual ~CSample();
//additional member functions
int Add();
};
#endif
#include "Sample.h"
NS_IMPL_ISUPPORTS1(CSample, ISample)
CSample::CSample()
{
/* 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"
NS_GENERIC_FACTORY_CONSTRUCTOR(CSample)
static nsModuleComponentInfo components[] =
{
{
SAMPLE_COMPONENT_CLASSNAME,
SAMPLE_COMPONENT_CID,
SAMPLE_COMPONENT_CONTRACTID,
CSampleConstructor,
}
};
NS_IMPL_NSGETMODULE("sample_module", components)
如何从 C++ 代码使用 XPCOM 组件
步骤 1:开发环境设置
- 为您的 XULRunner 版本使用正确的 XULRunner SDK,我使用的是 xulrunner-1.9.2。
- 使用 Microsoft 编译器,我使用的是 Visual C++ 2005。
这是我的文件夹结构
XPCOM
- xulrunner-sdk
bin
lib
idl
include
- XULTesting (xpcom implementaion)
Debug
Release
步骤 2:创建 VC++ 项目
- 在 Visual Studio 中启动一个空的 Win32 项目,并选择“控制台应用程序”选项。
- 创建一个包含头文件的 CPP 文件。
- 进行以下调整:
- 将 "..\xulrunner-sdk\include" 添加到附加包含目录。
- 将 "..\xulrunner-sdk\lib, ..\xulrunner-sdk\sdk\bin" 添加到附加库目录。
- 将输出目录更改为 < FireFox 目录 >。
- 将 "nspr4.lib xpcom.lib xpcomglue_s.lib" 添加到附加依赖项。
- 将 "XP_WIN;XP_WIN32" 添加到预处理器定义。
- 在 C++ 命令行附加选项中添加 "/Zc:wchar_t-" 以支持
wchar_t
。 - 关闭预编译头(为保持简单)。
- 现在,构建应用程序。
这是 CPP 文件:
#include "stdafx.h"
#include "nsCOMPtr.h"
#include "nsServiceManagerUtils.h"
#include "../sample_xpcom/ISample.h"
#include "../sample_xpcom/Sample.h"
int _tmain(int argc, _TCHAR* argv[])
{
nsresult rv;
nsCOMPtr<nsiservicemanager> servMan;
// You Can Get the service manager several ways
// Get Service manager : WAY 1
//---------------------------------------
rv = NS_GetServiceManager(getter_AddRefs(servMan));
if (NS_FAILED(rv))
{
printf("ERROR: XPCOM error [%x].\n", rv);
return -1;
}
//-----------End of Getting Way 1 -----------------
// Get Service manager : WAY 2
//--------------------------------------------------
/*
// Initialize XPCOM and check for failure ...
rv = NS_InitXPCOM2(getter_AddRefs(servMan), nsnull, nsnull);
if ( NS_FAILED(rv) )
{
printf("Calling NS_InitXPCOM returns [%x].\n", rv);
return -1;
}*/
//-----------End of Getting Way 2 --------------------
// Get the Component object;
nsCOMPtr<isample> iSample;
rv = servMan->GetServiceByContractID(SAMPLE_COMPONENT_CONTRACTID,
NS_GET_IID(ISample),getter_AddRefs(iSample));
if ( NS_FAILED(rv) )
{
printf("Calling GetServiceByContractID returns [%x].\n", rv);
NS_ShutdownXPCOM(nsnull);
return -1;
}
int nFirstVal, nSecondVal, nResult;
nFirstVal= 5;
nSecondVal = 10;
iSample->Add(nFirstVal, nSecondVal, &nResult);
_tprintf(_T("\nThe Result is : %d\n"), nResult);
// Shutdown XPCOM
// Here also several ways you can follow
// Explicitly Releasing ISample
//NS_RELEASE(iSample);
// Or Shutdown Service manager
// The nsIServiceManager instance that was returned by NS_InitXPCOM2 or nsnull.
NS_ShutdownXPCOM(nsnull);
return 0;
}
下一篇
我的下一篇文章将介绍如何将 MS COM 组件转换为 XPCOM 组件;即,一步一步地从 Microsoft COM 创建 XPCOM 组件。
参考文献
有关 XPCOM 的更多详细信息,最好的参考是:
注意:学习这个的简单方法是直接使用项目并在断点处进行调试。如果您有任何建议、要求或问题,请告知我。
历史
- 2010/06/28:初次发布。