65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的 XPCOM 教程

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (27投票s)

2010 年 6 月 28 日

CPOL

5分钟阅读

viewsIcon

170968

downloadIcon

4008

逐步介绍 C++ 中 XPCOM 的创建和实现。

本文目的

我写这篇教程是给刚开始学习 XPCOM 的程序员。本文简要介绍了如何编写自己的 XPCOM 对象以及如何使用 VC8.0 重用现有的 XPCOM 组件。本文不涵盖 XPCOM 的基础知识或概念。

项目设置和开发环境的搭建对我来说是一个大问题。另外,XPCOM 的注册是另一个棘手的问题,因为必须从配置文件目录中删除 xpti.datcompreg.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
CPP 文件
#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:初次发布。
© . All rights reserved.