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

XPCOM 中的字符串处理

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (8投票s)

2010年8月16日

CPOL

7分钟阅读

viewsIcon

54522

downloadIcon

349

在 XPCOM 中使用不同类型的字符串。

本文目的

我为那些有 XPCOM 经验并能用 C++ 创建 XPCOM 组件,现在想了解如何处理字符串的程序员编写了本教程。文章简要介绍了使用 XPCOM 对象进行字符串操作,以及如何在 VC 8.0 中使用 XPCOM 客户端的字符串。本文不涵盖 XPCOM 的基础知识或概念。

引言

XPCOM 是一个跨平台的组件对象模型,类似于 Microsoft COM。它有多种语言绑定,允许 XPCOM 组件除了 C++ 之外,还可以用 JavaScript、Java 和 Python 来使用和实现。XPCOM 中的接口使用一种称为 XPIDL 的 IDL 方言来定义。

在本文中,我将介绍从头开始创建简单 XPCOM 对象的方法。这些组件应该能够被 VC++/JavaScript 客户端使用。

作为一项练习,我们将尝试设计一个 XPCOM 组件,该组件将实现不同类型的字符串以进行反转。该组件必须接受一个参数并向用户返回反转后的字符串。

XPCOM 组件中的不同字符串类型

宽字符串和窄字符串类的命名约定

nsAString nsACString
nsString nsCString
nsAutoString nsCAutoString
等等...

字符串可以存储在两种基本格式中:8 位代码单元 (byte/char) 字符串,或 16 位代码单元 (PRUnichar) 字符串。类名中包含大写 "C" 的任何字符串类都包含 8 位字节。这些类包括 nsCStringnsDependentCString 等。不包含 "C" 的任何字符串类都包含 16 位代码单元。

抽象类

每个字符串类都派生自 nsAString/nsACString。此类提供了访问和操作字符串的基本接口。虽然具体类派生自 nsAString,但 nsAString 本身不能被实例化。

这与 Mozilla 在其代码库的其他部分用来描述抽象对象描述的“接口”概念非常相似。对于接口,类名以 "nsI" 开头,其中 "I" 代表 "Interface"。对于字符串,抽象类以 "nsA" 开头,"A" 代表 "Abstract"(抽象)。

有许多抽象类派生自 nsAString。这些抽象子类也不能被实例化,但它们比 nsAString 更详细地描述了字符串。它们保证了抽象类背后的实现提供了超越 nsAString 的特定功能。

下表描述了主要的基类。熟悉它们之后,请参阅附录,了解何时使用哪个类。

  • nsAString/nsACString:所有字符串的抽象基类。它提供了用于赋值、单个字符访问、字符串基本字符操作和字符串比较的 API。此类对应于 XPIDL 的 AString 参数类型。nsAString 不一定是空终止的。
  • nsString/nsCString:基于 nsAString,保证了空终止存储。这允许通过一个方法(.get())访问底层的字符缓冲区。(为了向后兼容,nsAFlatString 是此字符串类的 typedef。)

其余的字符串类继承自 nsAStringnsString。因此,每个字符串类都与 nsAString 兼容。

具体类 - 何时使用哪个类

具体类用于需要实际存储字符串数据的代码。具体类最常见的用途是作为局部变量,以及类或结构体中的成员。

nsAString_internal-graph.png

以下是最常见的具体类列表。熟悉它们之后,请参阅附录,了解何时使用哪个类。

  • nsString / nsCString - 一个空终止的字符串,其缓冲区在堆上分配。字符串对象消失时会销毁其缓冲区。
  • nsAutoString / nsCAutoString - 派生自 nsString,一个拥有 64 个代码单元缓冲区,并且该缓冲区与字符串本身在同一存储空间中。如果将小于 64 个代码单元的字符串赋值给 nsAutoString,则不会分配额外的存储空间。对于较大的字符串,将在堆上分配新缓冲区。
  • nsXPIDLString / nsXPIDLCString - 派生自 nsString;此类支持 getter_Copies() 操作符,可以轻松访问 XPIDL 的 wstring / string 输出参数。此类还支持空值缓冲区的概念,而 nsString 的缓冲区永远不会为空。
  • nsDependentString - 派生自 nsString;此字符串不拥有其缓冲区。它适用于将原始字符串(const PRUnichar*const char*)转换为 nsAString 类型的类。请注意,您必须为 nsDependentString 使用的缓冲区添加空终止符。如果您不想或无法为缓冲区添加空终止符,请使用 nsDependentSubstring
  • nsPrintfCString - 派生自 nsCString;此字符串的行为类似于 nsCAutoString。构造函数接受参数,允许它使用 printf 风格的格式字符串和参数列表来构造一个 8 位字符串。
  • NS_LITERAL_STRING/NS_NAMED_LITERAL_STRING - 这些将文字字符串(例如 "abc")转换为 nsStringnsString 的子类。在支持双字节字符串字面量的平台上(例如 MS VC++ 或 GCC 配合 -fshort-wchar 选项),它们只是 nsDependentString 类的宏。它们比直接用 nsDependentString 包装要快一些,因为它们利用编译器计算长度,并且还隐藏了非字节字符串字面量的混乱的跨平台细节。

还有一些具体类是作为辅助例程等的副作用产生的。您应该避免直接使用这些类。让字符串库为您创建类。

背景 注释
局部变量

nsAutoString

nsCAutoString

 
类成员变量

nsString

nsCString

 
方法参数类型

nsAString

nsACString

使用抽象类作为参数。使用 const nsAString& 作为“输入”参数,使用 nsAString& 作为“输出”参数。
检索“输出” string/wstring

nsXPIDLString

nsXPIDLCString

使用 getter_Copies()。类似于 nsString / nsCString
包装字符缓冲区

nsDependentString

nsDependentCString

包装 const char* / const PRUnichar* 缓冲区。
文字字符串

NS_LITERAL_STRING

NS_LITERAL_CSTRING

类似于 nsDependent[C]String,但在构建时预先计算长度。

IDL 字符串类型

字符串库也可以通过 IDL 访问。通过使用特殊定义的 IDL 类型声明属性和方法,字符串类被用作相应方法的参数。

IDL 类型 C++ 类型 目的
字符串 char* 指向 ASCII (7 位) 字符串的原始字符指针,不使用字符串类。高位在 XPConnect 边界处不保证。
wstring PRUnichar* 指向 UTF-16 字符串的原始字符指针,不使用字符串类。
AString nsAString UTF-16 字符串。
ACString nsACString 8 位字符串。在 XPConnect 边界处保留所有位。
AUTF8String nsACString UTF-8 字符串。当值在 XPConnect 边界处使用时,会根据需要转换为 UTF-16。
DOMString nsAString DOM 中使用的 UTF-16 字符串类型。与 AString 相同,但有一些特殊的 XPConnect 异常:当特殊的 JavaScript 值 null 传递给 XPCOM 方法的 DOMString 参数时,它会变成一个空的 DOMString。特殊的 JavaScript 值 undefined 会变成字符串 "undefined"

在 VC++ 中创建 XPCOM 项目

  • 为您的 XULRunner 版本使用正确的 XULRunner SDK,我使用的是 xulrunner-1.9.2
  • 使用 Microsoft 编译器,我使用的是 Visual C++ 2005
  • 使用正确的项目设置

如果您不知道如何为 XPCOM 创建 VC++ 项目,请参阅我的 简单的 XPCOM 教程

现在开始示例

让我们定义一个简单的接口

#include "nsISupports.idl"

[scriptable, uuid(658ABC9E-29CC-43E9-8A97-9D3C0B67AE8B)]
interface ISample : nsISupports
{
    long        Add(in long a, in long b);  
    string        ReverseString(in string s);
    AString        ReverseAString(in AString nStr);
    wstring        ReverseUnichar(in wstring nStr);
    ACString    ReverseACString(in ACString nStr);
};

记住要生成您自己的 GUID。

下一步是将 IDL 编译成类型库 (*.XPT) 和 C++ 头文件 (*.H),我们可以用它们来定义我们的实现对象。我们必须像这样两次使用 XPIDL.EXE

  • {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}

生成的 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
};
#endif
CPP 文件
#include "Sample.h"
#include "nsStringAPI.h"
#include <stdio.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;
}

/*  string ReverseString(in string s); */
NS_IMETHODIMP CSample::ReverseString(const char *s, char **_retval)
{
    char sTemp[256] = {'\0'};
    int nLen = strlen(s);

    *_retval = (char*)NS_Alloc(sizeof(char)*256);
    for (int i = 0; i < nLen; i++)
    {
        sTemp[i] = s[nLen-1-i];
    }

    strcpy(*_retval, sTemp);
    
    return NS_OK;
}

/* AString ReverseAString (in AString nStr); */
NS_IMETHODIMP CSample::ReverseAString(const nsAString & nStr, nsAString & _retval)
{
    nsAutoString strAuto;
    strAuto.Assign(nStr);
    nsAutoString strAutoTemp;

    int nLen = strAuto.Length();
    
    wchar_t wchTemp[256] = {'\0'};
    
    for (int i = 0; i < nLen; i++)
    {
        wchTemp[i] = strAuto[nLen-1-i];
    }
    
    _retval.Assign(wchTemp);
    return NS_OK;    
}

/* wstring ReverseUnichar (in wstring nStr); */
NS_IMETHODIMP CSample::ReverseUnichar(const PRUnichar *nStr, 
                       PRUnichar **_retval NS_OUTPARAM)
{
    nsAutoString strAuto;
    strAuto.Assign(nStr);

    int nLen = strAuto.Length();
    *_retval = (PRUnichar*)NS_Alloc(nLen*sizeof(PRUnichar));

    PRUnichar nStrTemp[256] = {'\0'};

    for (int i = 0; i < nLen; i++)
    {
        nStrTemp[i] = strAuto[nLen-1-i];
    }

    wcscpy(*_retval, nStrTemp);    
        
    return NS_OK;
}

/* ACString ReverseACString (in ACString nStr); */
NS_IMETHODIMP CSample::ReverseACString(const nsACString & nStr, 
              nsACString & _retval NS_OUTPARAM)
{
    nsCAutoString strAuto;
    strAuto.Assign(nStr);
    nsCAutoString strAutoTemp;

    int nLen = strAuto.Length();
    
    char chTemp[256] = {'\0'};
    
    for (int i = 0; i < nLen; i++)
    {
        chTemp[i] = strAuto[nLen-1-i];    
    }

    _retval.Assign(chTemp);

    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)

XPCOM 客户端项目

CPP 文件
#include "stdafx.h"
#include "nsCOMPtr.h"
#include "nsXPCOMStrings.h"
#include "nsXPCOM.h"
#include "../sample_xpcom/ISample.h"
#include "../sample_xpcom/Sample.h"
#include "nsServiceManagerUtils.h"
#include "nsStringAPI.h"

int _tmain(int argc, _TCHAR* argv[])
{
    nsresult rv;

    nsCOMPtr<nsiservicemanager> servMan;

    // Get Service manager
    rv = NS_GetServiceManager(getter_AddRefs(servMan));
    if (NS_FAILED(rv))
    {
        printf("ERROR: XPCOM error [%x].\n", rv);
        return -1;
    }

    // Get the Component object;
    nsCOMPtr<isample> iSample;
    rv = servMan->GetServiceByContractID(SAMPLE_COMPONENT_CONTRACTID, 
                   NS_GET_IID(ISample), getter_AddRefs(iSample));    
        
    // Or you can get like this
    //rv = servMan->GetService(NS_GET_IID(CSample), 
    //           NS_GET_IID(ISample), getter_AddRefs(iSample));

    if ( NS_FAILED(rv) )
    {
        NS_ShutdownXPCOM(nsnull);
        return -1;
    }

    int nFirstVal, nSecondVal, nResult;
    nFirstVal= 15; 
    nSecondVal = 10;
    iSample->Add(nFirstVal, nSecondVal, &nResult);

    _tprintf(_T("\nThe Result is : %d\n"), nResult);

    //---------------------------------------------------------------
    //----------------------- Using String types --------------------
    //---------------------------------------------------------------


    // IDL type        C++ Type        Purpose
    // string        char*            Raw character pointer to ASCII (7-bit) 
    //              string, no string classes used.
    //              High bit is not guaranteed across XPConnect boundaries.

    char chMainStr[256] = {'\0'};
    char *chRevStr;
    strcpy(chMainStr, "Test string");
    iSample->ReverseString(chMainStr, (char**)&chRevStr);
    printf("\nThe Main String: %s  The Reverted String : %s.\n", 
           chMainStr, chRevStr);

    //    IDL type    C++ Type    Purpose
    //    wstring        PRUnichar*    
    //               Raw character pointer to UTF-16 string, no string classes used.

    wchar_t wchMainStr[256] = {'\0'};
    wchar_t *wchRevStr;    

    wcscpy(wchMainStr, L"Test wstring");
    iSample->ReverseUnichar(wchMainStr, &wchRevStr);
    wprintf(L"\nThe Main String: %s  The Reverted String : %s.\n", 
            wchMainStr, wchRevStr);

    // IDL type            C++ Type        Purpose
    // AString            nsAString        UTF-16 string.
    nsString nsStringRev;
    nsString nsStringMain(L"Test AString");
    wcscpy(wchMainStr, nsStringMain.get());
        
    iSample->ReverseAString(nsStringMain, nsStringRev);
    wcscpy(wchRevStr, nsStringRev.get());
    wprintf(L"\nThe Main String: %s  The Reverted String : %s.\n", 
            wchMainStr, wchRevStr);

    // IDL type            C++ Type        Purpose
    // ACString            nsACString        8-bit string.
    //                                  All bits are preserved across XPConnect boundaries.
    nsCString nsCStringRev;
    nsCString nsCStringMain("Test ACString");
    strcpy(chMainStr, nsCStringMain.get());
    iSample->ReverseACString(nsCStringMain, nsCStringRev);
    strcpy(chRevStr, nsCStringRev.get());
    
    printf("\nThe Main String: %s  The Reverted String : %s.\n", 
           chMainStr, chRevStr);

    // Shutdown Service manager 
    NS_ShutdownXPCOM(nsnull);

    return 0;
}

参考文献

有关 XPCOM 字符串的更多详细信息,最佳参考是

注意:学习这个的一个简单易行的方法就是直接使用项目并通过断点调试。如果您有任何建议、请求或问题,请告诉我。

历史

  • 2010/08/16:初次发布。
© . All rights reserved.