XPCOM 中的字符串处理






4.78/5 (8投票s)
在 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 位字节。这些类包括 nsCString
、nsDependentCString
等。不包含 "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
。)
其余的字符串类继承自 nsAString
或 nsString
。因此,每个字符串类都与 nsAString
兼容。
具体类 - 何时使用哪个类
具体类用于需要实际存储字符串数据的代码。具体类最常见的用途是作为局部变量,以及类或结构体中的成员。
以下是最常见的具体类列表。熟悉它们之后,请参阅附录,了解何时使用哪个类。
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")转换为nsString
或nsString
的子类。在支持双字节字符串字面量的平台上(例如 MS VC++ 或 GCC 配合 -fshort-wchar 选项),它们只是nsDependentString
类的宏。它们比直接用nsDependentString
包装要快一些,因为它们利用编译器计算长度,并且还隐藏了非字节字符串字面量的混乱的跨平台细节。
还有一些具体类是作为辅助例程等的副作用产生的。您应该避免直接使用这些类。让字符串库为您创建类。
背景 | 类 | 注释 |
---|---|---|
局部变量 |
|
|
类成员变量 |
|
|
方法参数类型 |
|
使用抽象类作为参数。使用 const nsAString& 作为“输入”参数,使用 nsAString& 作为“输出”参数。 |
检索“输出” string /wstring |
|
使用 getter_Copies() 。类似于 nsString / nsCString 。 |
包装字符缓冲区 |
|
包装 const char* / const PRUnichar* 缓冲区。 |
文字字符串 |
|
类似于 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
#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 客户端项目
#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:初次发布。