当前位置: 首页 > 工具软件 > XPCOM > 使用案例 >

使用XPCOM工具

尹雅健
2023-12-01
本章内容包括
• “Generic XPCOM Module Macros”
• “String Classes in XPCOM”
• “Smart Pointers”
• “weblock2.cpp”

XPCOM Macros
Generic XPCOM Module Macros

入门指南对于建立通用的组件代码很有用的。但是只有少数地方的代码对于WebLock组件是独一无二的,而且需要大量的输入。要写一个不同的组件库,你可以复制章节结尾的清单,做很小的改动,然后粘贴到一个新的项目。为了避免这种冗余的工作,调节编写通用代码,减少输入,XPCOM提供generic module  macros扩展模块代码。由于这些宏扩展到“通用”的实现,可能不会像你在编写自己的实现那么有灵活性。但是它使开发变得更快。

宏模块包括一组 宏,这些宏定义导出的NSGetModule入口点, 为你的实现类 创建一个通用的工厂。这些宏可以很好的管理 组件的实现代码,
让你更多的去关注组件的实际逻辑。

NS_IMPL_NSGETMODULE  (name, components)                  用模块名称_name 和组件名称  _components.实现了nsIModule 接口
NS_IMPL_NSGETMODULE_WITH_CTOR  (name, components, ctor)  同上,但是可以指定当创建模块时调用 一个特殊函数
NS_IMPL_NSGETMODULE_WITH_DTOR  (name, components, dtor)  同上,但是可以指定当析构模块时调用一个特殊函数
NS_IMPL_NSGETMODULE_WITH_CTOR_DTOR  (name, components, ctor, dtor)  同上,但是可以分别指定当创建模块和析构模块时调                                                                                                                                         用特殊函数

Module Implementation Macros
一般情况下用NS_IMPL_NSGETMODULE , 不需要任何回调,但所有的宏遵循相同的一般模式, 所有这些宏的数组结构由_components参数表示, 每个结构描述的是 在XPCOM注册 CID 。 这些宏的第一个参数是一个任意的字符串的模块名称, 在调试环境中, 当组件库加载或卸载时, 这个字符串将打印到屏幕上。 你应该选择一个对跟踪信息有帮助的名称, 结构 所需的四个部分 包含以下信息:
• 可读的类名
• CID
•  contract ID 
一个给定对象的构造函数
static const nsModuleComponentInfo components[] =
{
{ "Pretty Class Name",
CID,
CONTRACT_ID,
Constructor
},
....
}

在上面的虚构的清单中 需要注意 的事情是 它可以支持多个组件在一个模块。Gecko的 网络库在单一的nsModuleComponentInfo声明了超过50个组件。
nsModuleComponentInfo的第一个元素是组件名, 虽然目前内部使用它不是那么多, 这个名字应该是描述模块的。
第二项是 CID,通常的做法是把CID定义为宏,然后在组件列表中用CID的宏,许多CID的格式如下:
#define NS_IOSERVICE_CID \
{ /* 9ac9e770-18bc-11d3-9337-00104ba0fd40 */ \
0x9ac9e770, \
0x18bc, \
0x11d3, \
{0x93, 0x37, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \
}
下一个是Contract ID字符串, 这通常也在头文件用 #define进行定义。 这三个条目是 RegisterFactoryLocation方法 所需的参数, 当你使用这些实现宏, 你必须声明一个 对象的 构造函数。 这使你不必编写一个工厂对象。

Factory Macros.
工厂宏使得很容易写工厂实现,给定一个类名ConcreteClass, 工厂的宏的声明是:
NS_GENERIC_FACTORY_CONSTRUCTOR(ConcreteClass)

由此产生一个名为ConcreteClassConstructor的函数,该函数可以用在nsModuleComponentInfo结构中,

#include "nsIGenericFactory.h"
static const nsModuleComponentInfo components[] =
{
{ "Pretty Class Name",
SAMPLE_CID,
"@company.com/sample"
SampleConstructor
}
}
NS_IMPL_NSGETMODULE(nsSampleModule, components)

Common Implementation Macros
每个XPCOM对象都实现nsISupports,但是一遍又一遍地写这个实现是乏味的。可以使用XPCOM提供实现宏, 除非你有非常特别的要求要管理引用计数或处理接口的发现。 不用 自己 实现nsISupports, NS_IMPL_ISUPPORTS1可以为每个对象扩展AddRef,Release和QueryInterface的实现,
NS_IMPL_ISUPPORTS1(classname, interface1)
如果你实现多个接口, 你可以简单的改变宏的“1”为 你支持接口的数量,
NS_IMPL_ISUPPORTS2(classname, interface1, interface2)
NS_IMPL_ISUPPORTSn(classname, interface1, …, interfacen)
这些宏自动添加nsISupports条目, 所以你不需要这样做:
NS_IMPL_ISUPPORTS2(classname, interface1, nsISupports)
仔细看看上面的例子, 注意,它使用的是接口实际名称而不是一个IID, 在宏观内部,借,接口名称扩展为NS_GET_IID() ,这个宏 从接口生成的头的文件 提取IID, 当用XPIDL写一个接口, 头文件包含他们的静态声明IID。任何由XPIDL生成的接口,你都可以用 NS_GET_IID()获取接口的IID,
// returns a reference to a shared nsIID object.
static const nsIID iid1 = NS_GET_IID(nsISupports);
// constructs a new nsIID object
static const nsIID iid2 = NS_ISUPPORTS_IID;
为了使用NS_IMPL_ISUPPORTSn, 你必须确保在你的类定义一个nsrefcnt 类型,名字为mRefCnt的成员变量。 当你可以使用另一个宏, 何必多此一举。

Declaration Macros
NS_DECL_NSISUPPORTS 为你声明了AddRef ,  Release , 和QueryInterface,也定义了NS_IMPL_ISUPPORTS 需要的 mRefCnt 。 此外, NS_DECL_附加任何接口 全部大写 名称,将 为你 声明接口的 所有方法。例如, NS_DECL_NSIFOO 声明 nsIFoo的的所有方法,nsIFoo存在于由XPIDL编译器生成nsIFoo.h文件中。 考虑下面的真正的类:
class myEnumerator : public nsISimpleEnumerator
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISIMPLEENUMERATOR
myEnumerator();
virtual ~myEnumerator() {}
};
nsISimpleEnumerator只包含了构造函数 和析构函数并没有其他函数,而是使用了 NS_DECL_宏。
使用这些声明宏不仅节省了大量写代码的时间, 如果你改变IDL文件,也 还可以节省时间。 因为c++头文件将自动包含更新的 方法 列表。

NS_IMPL_ISUPPORTSn               为带有n个接口的类实现nsISupports
NS_DECL_ISUPPORTS                 声明nsISupports的方法包括mRefCnt
NS_INIT_ISUPPORTS                   初始化mRefCnt为零。必须在类构造函数中调用
NS_GET_IID                                  返回一个给定名称的接口的IID。接口必须由XPIDL生成。

weblock2.cpp
使用了宏的weblock1.cpp代码如下:
#include "nsIGenericFactory.h"
#define SAMPLE_CID \
{ 0x777f7150, 0x4a2b, 0x4301, \
{ 0xad, 0x10, 0x5e, 0xab, 0x25, 0xb3, 0x22, 0xaa}}
class Sample: public nsISupports {
public:
Sample();
virtual ~Sample();
NS_DECL_ISUPPORTS
};
Sample::Sample()
{
// note: in newer versions of Gecko (1.3 or later)
// you don’t have to do this:
NS_INIT_ISUPPORTS();
}
Sample::~Sample()
{
}
NS_IMPL_ISUPPORTS(Sample, nsISupports);
NS_GENERIC_FACTORY_CONSTRUCTOR(Sample);
static const nsModuleComponentInfo components[] =
{
{ "Pretty Class Name",
SAMPLE_CID,
"@company.com/sample"
SampleConstructor
}
};
NS_IMPL_NSGETMODULE(nsSampleModule, components)

String Classes in XPCOM
字符串通常看成是 线性序列的字符, 在c++中,字符串“XPCOM”, 由连续的6个字节组成,其他类型的字符串比如 “宽”字符串,使用两个字节来表示每个字符, 通常用于处理Unicode字符串。

然而,XPCOM的字符串类不仅仅是限于以空字符结尾的字符序列。它们是相当复杂的,因为它们支持Gecko布局引擎和其他管理大型块数据子系统。该字符串类可以支持把字符序列分解为多个片段。

XPCOM的所有字符串类派生自两个抽象类之一, nsAString   和nsACString 。 前一个处理双字节字符, 后者往往在更一般的情况下使用。
但这两个类都定义了字符串的功能。在以下章节, 你可以看到这些类作为参数传递的许多XPCOM的接口。

Using Strings

解释所有的字符串类工作超出了本书的范围,但我们 向你展示WebLock组件如何使用字符串。 首先要注意的是,字符串类本身是不冻结的,这意味着 当你可以避免的时候 不应该与它们连接。 连接字符串库到一个组件会增加 超过100 k内存消耗,这 在很多情况下是不可接受的。对于WebLock, 字符串类只需要包装现有的字符串数据, 获得付出很小的代价获得高级功能。WebLock的字符串类不需要附加,连接,搜索,
任何其他字符串数据上的实际工作,它们只需要表示 char*和 其他数据,然后传给 需要接收 nsACString参数的 方法。  

nsEmbedString and nsEmbedCString
在本教程中使用的字符串是 nsEmbedString 和nsEmbedCString,他们分别实现抽象类  nsAString 和  nsACString。 第一个例子展示了一个nsEmbedCString被用来传递给需要nsACString参数的方法。
// in IDL: method(in ACString thing);
char* str = "How now brown cow?";
nsEmbedCString data(str);
rv = object->Method(data);
在下一个示例中, 该方法将设置字符串的值,
// in IDL: attribute ACString data;
nsEmbedCString data;
method->GetData(data);
// now to extract the data from the url class:
const char* aStringURL = data.get();
注意,调用url.get()后aStringURL 指向的内存 是URL字符串对象所有的。 如果你需要在 字符串对象的生命周期过了之后继续使用 这个字符串数据,你必须进行复制。

Smart Pointers
到目前为止你看到的所有的接口都是引用计数的。 漏一个引用而不释放的对象, 如以下代码所示,可以是一个 重大的问题。
{
nsISupports* value = nsnull;
object->method(&value);
if (!value) return;
...
if (NS_FAILED(error))
return; // <------------ leaks |value|
...
NS_RELEASE(value); // release our reference
}
方法返回一个nsISupports接口指针,在返回之前 已经被引用。 如果你 通过过早返回 处理一个错误条件, value 指向 没有被删除而 有内存泄露。 在这个例子很容易修改, 但在实际代码, 这在“goto”结构中容易出现或者 在深嵌套中过早返回。
有多个需要被释放的接口指针, 当跳出一个块的作用域。 这就需要一个 可以帮助开发人员的 工具。 在XPCOM, 这个工具是nsCOMPtr,
或智能指针类,当你处理接口指针是 这可以为你节省大量时间和简化你的代码。 使用智能指针, 上面的代码可以简化为:
{
nsCOMPtr<nsISupports> value;
object->method(getter_AddRefs(value));
if (!value) return;
...
if (NS_FAILED(error))
return;
...
}
风格和语法 可能不熟悉, 但智能指针是值得学习和使用的,因为它们简化了 引用 管理。 nsCOMPtr是c++ 模板类,几乎就像原始指针, 可以比较和测试,等等。 当你把它们传给getter,你必须做点特别的事情,不管怎么样, 你必须 用函数 getter_AddRefs 包装 变量,如上面的示例。
你不能为nsCOMPtr调用nsISupports 的AddRef 和Release方法。 但是这个限制是可取的,因为 nsCOMPtr已经 替你 处理了引用计数。 如果由于某种原因需要调整引用计数, 您必须把nsCOMPtr赋值给一个新的变量然后调用AddRef 。 这是一个常见的模式,你在 函数中 有一个本地nsCOMPtr,你必须给它返回一个引用。 如下所示:
SomeClass::Get(nsISupports** aResult)
{
if (! aResult)
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsISupports> value;
object->method(getter_AddRefs(value));
*aResult = value.get();
NS_IF_ADDREF(*aResult);
return  NS_OK ;
}
该方法所做的第一件事是检查调用者是否传入一个有效的地址,如果不是, 它不再继续。接着, 它调用另一个在这种情况下是假定存在的对象的方法, 你可以调用nsCOMPtr的. get()方法,并 把它用作 原始指针 返回。 这种原始指针可以赋值给一个变量,然后用NS_IF_ADDREF更新引用。不管怎么样,小心 . get()的返回结果。 你不应该在这个结果调用Release,因为它可能会导致崩溃。 nsCOMPtr 显式地释放对象,你可以赋值指针为0。
智能指针的另一个不错的功能, 你可以很容易QueryInterface它们。 有两个接口代表一个文件系统上的文件,nsIFile 和  nsILocalFile , 他们都是通过对象实现, 虽然我们还没有正式介绍这两个接口, 接下来的代码示例展示了如何简单切换这两个接口,
SomeClass::DoSomething(nsIFile* aFile)
{
if (! aResult)
return NS_ERROR_NULL_POINTER;
nsresult rv;
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(aFile, &rv);
如果QueryInterface成功, localFile 将非空, rv 将为   NS_OK。 如果QueryInterface失败, localFile  将为空,rv将 设置为一个特定的错误代码对应于失败的原因, 在这个构造中, 结果代码rv是一个可选参数, 如果你不关心错误代码,您可以简单地把它从函数调用去掉。 从现在开始, 我们将 在WebLock尽可能多的 使用nsCOMPtr,




































 类似资料: