创建组件代码

What We’ll Be Working On
我们致力于的组件在这本书中为你的 浏览器控制中的一个特殊模式, 防止用户离开当前域或一组安全域。一旦启用,这种界面锁模式进行密码保护,直到有密码的人把它关闭。它可以使儿童可以安全的使用浏览器,或者现在在特定的服务器进行针对性的浏览。WebLock组件的大部分工作是准备自身, 找到组件要用的XPCOM接口,并连接到Gecko浏览器现有的功能中。

Component Registration
所有的组件,不管是dll,javascript文件或者是其他文件,在使用之前都必须进行注册。注册过程发生在所有的XPCOM程序中,不管是嵌入Gecko 的客户,还是Mozilla, Netscape 7, Compuserve,或者其他使用XPCOM的软件。
注册提供应用程序 需要正确使用组件的信息。
WebLock组件要做许多事情来注册自身。尤其是, 组件库必须包含本章中描述的组件接口的实现。 nsIModule 和 nsIFactory , 实现代码的入口点。一旦你的组件实现了这些接口,其他的注册过程就很简单了。
 The regxpcom Program
注册组件的一个显式的方法就是运行regxpcom程序。不用给regxpcom传入任何参数,该程序把组件注册到默认组件注册表。
我们建议当你在Mozilla或Netscape客户端 测试组件时, 你复制组件到“组件”在客户机的安装目录文件夹。当组件拷到目录下之后,从命令行运行regxpcom, 注册目录中所有组件。

Registration Alternatives
Gecko嵌入应用程序可以提供其他注册XPCOM组件的 方式。XPInstall, 跨平台安装技术,Mozilla用来安装浏览器和其他组件。

Overview of the WebLock Module Source
如我们上一节提到的,组件有层次,每个XPCOM组件有三个主要的层次,从里往外,第一个对象是XPCOM对象,该对象包含业务逻辑。在Weblock组件中, 汇集了各种各样的Gecko服务和防止用户 离开的接受域列表。
外面一层 是 nsIFactory 对象。该对象提供了XPCOM对象的抽象。XPCOM对象 的主要访问器是CreateInstance 。它 将返回与给定CID和IID对 匹配的对象 。
再外一层是nsIModule。该接口是nsIFactory对象的抽象。 可以允许多个nsIFactory对象, 这个接口的关键是 getClassObject 的返回类型不一定是一个nsIFactory。相反,nsIModule可以询问 关于XPCOM对象的实现细节。这是很有用的,如果调用者需要知道组件的信息,比如线程模型,是否是单例模式, 实现语言等。 在这种情况下使用的是nsIClassInfo接口。
创建组件代码_第1张图片
在我们开始观察组件的各个部分以及它们在源代码中实现之前,我们先来看weblock.cpp。WebLock组件的源文件有三个。要在Mozilla中用 WebLock组件,你必须实现一个新的WebLock接口 iWebLock,具体实现网页锁定功能。 您还必须创建WebLockModule   实现必要的nsIModule接口, 你还必须创建WebLockFactory实现nsIFactory并创建一个工厂的实例 给你的客户组件。 这三个接口实现, 组件功能,nsIModule接口,nsIFactory接口, 为客户创建并管理实例,是 你创建一个XPCOM组件需要写的 基本的代码 
WebLock组件源文件的基本结构:
• required includes and constants
• WebLock: public  iWebLock
• WebLockFactory: public  nsIFactory
• WebLockModule: public  nsIModule

Digging In: Required Includes and Constants
我们先来看组件中前面的几行代码,XPCOM源文件开头的包含和定义,告诉你接下来要讨论的数据类型和技术。
例如, MOZILLA_STRICT_API

#include <stdio.h>
// may be defined at the project level
// in the makefile
#define MOZILLA_STRICT_API
#include "nsIModule.h"
#include "nsIFactory.h"
#include "nsIComponentManager.h"
#include "nsIComponentRegistrar.h"
// use classes to handle IIDs
// classes provide methods for comparison: Equals, etc.
static const nsIID kIModuleIID = NS_IMODULE_IID;
static const nsIID kIFactoryIID = NS_IFACTORY_IID;
static const nsIID kISupportsIID = NS_ISUPPORTS_IID;
static const nsIID
kIComponentRegistrarIID = NS_ICOMPONENTREGISTRAR_IID;
// generate unique ID here with uuidgen
#define SAMPLE_CID \
{ 0x777f7150, 0x4a2b, 0x4301, \
{ 0xad, 0x10, 0x5e, 0xab, 0x25, 0xb3, 0x22, 0xaa}}
static const nsCID kSampleCID = SAMPLE_CID;
nsIModule.h 和nsIFactory.h 对于创建模块来说很重要,它们定义模块和工厂接口, 也包含了几个重要的宏。nsIComponentManager.h 和
nsIComponentRegistrar.h, 提供 RegisterFactoryLocation 实现模块和工厂类所需的代码。

Identifiers in XPCOM
这里已初始化的一系列的nsIID变量用来处理128位的标识符。kIFactoryIID变量提供 Equals()方法,在代码中用于比较。
if (aIID.Equals(NS_GET_IID(nsISupports))) {
*aInstancePtr = (void*)(nsISupports*)this;
NS_ADDREF_THIS();
return  NS_OK ;
}

 最后,SAMPLE_CID是一个CID例子,唯一标识一个组件。XPCOM中所有的128位的类和接口的ID,都是UUID的例子。

Coding for the Registration Process
当XPCOM第一次发现你的组件,做的第一件事情就是尝试加载你的库并查找NSGetModule, 当这个特殊的入口点被调用,它传入XPCOM组件管理器和动态库的位置。组件管理器是XPCOM实现的一个接口,它 封装了对象的创建,并提供所有注册组件的概要信息。磁盘上的位置是通过另外一个接口nsIFile传递的。该接口是XPCOM对文件和目录的抽象。nsIFile对象通常是本地磁盘上的一个文件或目录,当然也可以表示网络上的。
nsresult NSGetModule(nsIComponentManager *servMgr,
nsIFile* location,
nsIModule** result);  
XPCOM成功调用NSGetModule返回的 nsIModule接口的一个实现。当你写XPCOM组件,你 实现nsIModule完成 必要的注册、注销和对象创造,nsIModule有四个方法必须实现。

The Registration Methods
密切相关的两个方法声明如下:
NS_IMETHOD RegisterSelf(nsIComponentManager *aCompMgr,
nsIFile *aLocation,
const char *aLoaderStr,
const char *aType) = 0;
NS_IMETHOD UnregisterSelf(nsIComponentManager *aCompMgr,
nsIFile *aLocation,
const char *aLoaderStr) = 0;
当XPCOM第一次注册组件是调用RegisterSelf,只调用一次。RegisterSelf告知XPCOM组件支持什么。无论在RegisterSelf 做什么,在
UnregisterSelf 都要撤销。
首先调用NSGetModule,它返回nsIModule实现的一个指针,然后XPCOM调用RegisterSelf并传入参数。

The RegisterSelf Method
第一个参数是nsIComponentManager,它 提供了一种管理注册过程的入口,你可以QueryInterface它访问其他组件管理接口。
XPCOM组件管理器的多面性:
三个核心的组件管理接口为: nsIComponentManager,  nsIServiceManager, 和nsIComponentRegistrar。
  1. nsIComponentManager              创建对象和获取实现对象的细节
  2. • nsIServiceManager                   提供了访问单例对象和发现状态
  3. • nsIComponentRegistrar            注册和注销工厂和组件;处理自动注册和发现和枚举注册的组件
你的RegisterSelf方法可能在nsIComponentManager接口参数上调用QueryInterface获取nsIComponentRegistrar 或者 nsIServiceManager。nsIServiceManager 用来获取单例的服务, 如果你要 服务 注册这 可能是有用的,
RegisterSelf的第二个参数是注册组件的位置,当组件需要知道它安装或注册在什么位置的时候, 这个参数是非常有用的。 这个方法只调用一次,所以 如果以后你要使用它的话 你必须持续的位置不变。
接下来的两个参数经常传给nsIComponentRegistrar方法, XPCOM组件用来决定如何处理组件组件。aLoaderStr参数是不透明的,不能被修改,通过nsIFile参数区分从相同位置加载的组件。一个ZIP文档可能存储多个组件,文档中每个组件都有相同的nsIFile参数,aLoaderStr可以用于指出ZIP中的位置。
最后一个参数指定组件使用哪种加载器。 这是 作为一个优化 保留的, 在大多数情况下, 它可以用来扩展XPCOM。
因为XPCOM已经知道内部 加载 什么样文件,并调用RegisterSelf,该注册方法传递该值,可以快速 确定什么样的组件被注册。
nsIComponentRegistrar Methods
调用以下方法告诉XPCOM组件库实现了什么,
NS_IMETHOD RegisterFactoryLocation(const nsCID & aClass,
const char *aClassName,
const char *aContractID,
nsIFile *aFile,
const char *aLoaderStr,
const char *aType) = 0;
最后一个参数跟RegisterSelf参数一样。你需要做的就是把RegisterSelf的参数传给它,只留下前面三个参数。对于任何实现XPCOM接口的类,实现必须有一个类标识符,如果要通过XPCOM与其他部分代码实现共享的话。这个标识符就是CID,唯一标识实现。给定一个 CID 和
IID,你可以引用到XPCOM中任意一个类。
创建组件代码_第2张图片
既然这样,nsISupports有两种实现方式,每个实现有一个CID,接口也有IID。当 指定的实现A, 需要的两个信息是A的CID和A支持的接口的IID。注册代码如下:
NS_IMETHODIMP
SampleModule::RegisterSelf(nsIComponentManager *aCompMgr,
nsIFile* aPath,
const char* registryLocation,
const char* componentType)
{
printf("Hello Mozilla Registration!\n\n");
nsIComponentRegistrar* compReg = nsnull;
nsresult rv = aCompMgr->
QueryInterface(kIComponentRegistrarIID,(void**)& comp
if (NS_FAILED(rv))
return rv;
rv = compReg->RegisterFactoryLocation(kSampleCID,
"Sample Class",
nsnull,
aPath,
registryLocation,
componentType);
compReg->Release();
return rv;
}
注销的逻辑也是一样的,注销时给UnregisterSelf传入CID和文件。

Creating an Instance of Your Component
上面的例子使用了CID, 但是组件注册后, 任何人 如果有ID或CID就可以 使用XPCOM访问你是类。要让其他人访问, 你需要发布 组件的 CID和契约ID以及它支持接口。对于上面的例子, 通过组件管理器 创建Sample对象
nsIComponentManager* compManger; // assume initialized
nsISupports* sample;
compManager->CreateInstance(kSampleCID,
nsnull,
kISupportsIID,
(void**)&sample);
在上面的代码片段, 我们假设组件管理器已经初始化。 在许多情况下,这个值是很容易获得。 不然的话,可以 通过调用NS_GetComponentManager()获得。 调用 CreateInstance 的第一个参数 指定 客户端代码正在寻找的 组件,和 传递给RegisterFactoryLocation 的值 相同, 下一个参数是聚合, WebLock组件不支持。 第三个参数是用于接口与组件交互, 最后一个参数是输出参数, 如果这个方法成功了 将包含一个有效的对象。 CreateInstance 的实现确保结果将支持传入的IID,kISupportsIID。sample的 变量的类型
应该匹配IID传入的 kISupportsIID。
调用CreateInstance, XPCOM浏览 所有注册的组件 找到匹配 给定的CID,然后XPCOM 加载CID相关的组件, 如果还没有加载的话。

最后,在模块上调用GetClassObject方法,此方法你必须在组件代码实现,将返回一CID / IID nsIFactory对象。

为了准备你的组件代码,, 您需要为 在XPCOM注册的每一个 对象 创建一个工厂对象。
在nsIFactory接口 必须实现的主要功能是CreateInstance, 以下是一个简单的实现算法
  1. 创建原始对象
  2. 如果失败,返回一个内存不足的错误代码。
  3. 在新对象调用QueryInterface
  4. 如果失败,输出参数为null并释放新对象。
  5. 从QueryInterface返回nsresult

经常,你不需要先创建对象因为工厂的隐式地知道所支持IID。当并非这种情况,然而这样做进一步从具体类的抽象工厂,如果你有一个工厂

知道每一个支持的IID的基类,然后当 去添加一个新的支持接口, 你 在 工厂和QueryInterface 具体类 实现中  添加这个IID比较
NS_IMETHODIMP
SampleFactory::CreateInstance(
nsISupports *aOuter,
const nsIID & iid,
void * *result)
{
if (!result)
return NS_ERROR_INVALID_ARG;
Sample* sample = new Sample();
if (!sample)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = sample->QueryInterface(iid, result);
if (NS_FAILED(rv)) {
*result = nsnull;
delete sample;
}
return rv;
}

webLock1.cpp
#include <stdio.h>

#define MOZILLA_STRICT_API

#include "nsIModule.h"

#include "nsIFactory.h"

#include "nsIComponentManager.h"

#include "nsIComponentRegistrar.h"

static const nsIID kIModuleIID = NS_IMODULE_IID;

static const nsIID kIFactoryIID = NS_IFACTORY_IID;

static const nsIID kISupportsIID = NS_ISUPPORTS_IID;

static const nsIID kIComponentRegistrarIID = NS_ICOMPONENTREGISTRAR_IID;

#define SAMPLE_CID \

{ 0x777f7150, 0x4a2b, 0x4301, \

{ 0xad, 0x10, 0x5e, 0xab, 0x25, 0xb3, 0x22, 0xaa}}

static const nsCID kSampleCID = SAMPLE_CID;

class Sample: public nsISupports {

private:

nsrefcnt mRefCnt;

public:

Sample();

virtual ~Sample();

NS_IMETHOD QueryInterface(const nsIID &aIID, void **aResult);

NS_IMETHOD_(nsrefcnt) AddRef(void);

NS_IMETHOD_(nsrefcnt) Release(void);

};

Sample::Sample()

{

:mRefCnt(0);

}

Sample::~Sample()

{

}

NS_IMETHODIMP Sample::QueryInterface(const nsIID &aIID,

void **aResult)

{

if (aResult == NULL) {

return NS_ERROR_NULL_POINTER;

}

*aResult = NULL;

if (aIID.Equals(kISupportsIID)) {

*aResult = (void *) this;

}

if (aResult != NULL) {
 return NS_ERROR_NO_INTERFACE;

}

AddRef();

return  NS_OK ;

}

NS_IMETHODIMP_(nsrefcnt) Sample::AddRef()

{

return ++mRefCnt;

}

NS_IMETHODIMP_(nsrefcnt) Sample::Release()

{

if (--mRefCnt == 0) {

delete this;

return 0;

}

return mRefCnt;

}

// factory implementation class for component

class SampleFactory: public nsIFactory{

private:

nsrefcnt mRefCnt;

public:

SampleFactory();

virtual ~SampleFactory();

NS_IMETHOD QueryInterface(const nsIID &aIID, void **aResult);

NS_IMETHOD_(nsrefcnt) AddRef(void);

NS_IMETHOD_(nsrefcnt) Release(void);

NS_IMETHOD CreateInstance(nsISupports *aOuter, const nsIID & iid, void * *result);

NS_IMETHOD LockFactory(PRBool lock);

};

SampleFactory::SampleFactory()

{

mRefCnt = 0;

}

SampleFactory::~SampleFactory()

{

}

NS_IMETHODIMP SampleFactory::QueryInterface(const nsIID &aIID,

void **aResult)

{

if (aResult == NULL) {

return NS_ERROR_NULL_POINTER;

}

*aResult = NULL;

if (aIID.Equals(kISupportsIID)) {

*aResult = (void *) this;

}

else

if (aIID.Equals(kIFactoryIID)) {

*aResult = (void *) this;

}

if (aResult != NULL) {

return NS_ERROR_NO_INTERFACE;

}

AddRef();

return  NS_OK ;

}

NS_IMETHODIMP_(nsrefcnt) SampleFactory::AddRef()

{

return ++mRefCnt;

}

NS_IMETHODIMP_(nsrefcnt) SampleFactory::Release()

{

if (--mRefCnt == 0) {

delete this;

return 0;

}

return mRefCnt;

}

NS_IMETHODIMP

SampleFactory::CreateInstance(nsISupports *aOuter, const nsIID & iid, void * *result)

{

if (!result)

return NS_ERROR_INVALID_ARG;

Sample* sample = new Sample();

if (!sample)

return NS_ERROR_OUT_OF_MEMORY;

nsresult rv = sample->QueryInterface(iid, result);

if (NS_FAILED(rv)) {

*result = nsnull;

delete sample;

}

return rv;

}

NS_IMETHODIMP

SampleFactory::LockFactory(PRBool lock)

{

return NS_ERROR_NOT_IMPLEMENTED;

}

// Module implementation

class SampleModule : public nsIModule

{

public:

SampleModule();

virtual ~SampleModule();

// nsISupports methods:

NS_IMETHOD QueryInterface(const nsIID & uuid, void * *result);

NS_IMETHOD_(nsrefcnt) AddRef(void);

NS_IMETHOD_(nsrefcnt) Release(void);

// nsIModule methods:

NS_IMETHOD GetClassObject(nsIComponentManager *aCompMgr, const nsCID & aClass, const nsIID & aIID,

void * *aResult);

NS_IMETHOD RegisterSelf(nsIComponentManager *aCompMgr, nsIFile *aLocation, const char *aLoaderStr,

const char *aType);

NS_IMETHOD UnregisterSelf(nsIComponentManager *aCompMgr, nsIFile *aLocation, const char *aLoaderStr);

NS_IMETHOD CanUnload(nsIComponentManager *aCompMgr, PRBool *_retval);

private:

nsrefcnt mRefCnt;

};

//----------------------------------------------------------------------

SampleModule::SampleModule()

{

mRefCnt = 0;

}

SampleModule::~SampleModule()

{

}

// nsISupports implemention

NS_IMETHODIMP_(nsrefcnt)

SampleModule::AddRef(void)

{

++mRefCnt;

return mRefCnt;

}

NS_IMETHODIMP_(nsrefcnt)

SampleModule::Release(void)

{

--mRefCnt;

if (mRefCnt == 0) {

mRefCnt = 1; /* stabilize */

delete this;

return 0;

}

return mRefCnt;

}

NS_IMETHODIMP

SampleModule::QueryInterface(REFNSIID aIID, void** aInstancePtr)

{

if ( !aInstancePtr )

return NS_ERROR_NULL_POINTER;

nsISupports* foundInterface;

if ( aIID.Equals(kIModuleIID) )

foundInterface = (nsIModule*) this;

else if ( aIID.Equals(kISupportsIID) )

foundInterface = (nsISupports*) this;

else

foundInterface = 0;

if (foundInterface) {

foundInterface->AddRef();

*aInstancePtr = foundInterface;

return  NS_OK ;

}

*aInstancePtr = foundInterface;

return NS_NOINTERFACE;

}

// Create a factory object for creating instances of aClass.

NS_IMETHODIMP

SampleModule::GetClassObject(nsIComponentManager *aCompMgr,

const nsCID& aClass,

const nsIID& aIID,

void** result)

{

if (!kSampleCID.Equals(aClass))

return NS_ERROR_FACTORY_NOT_REGISTERED;

if (!result)

return NS_ERROR_INVALID_ARG;

SampleFactory* factory = new SampleFactory();

if (!factory)

return NS_ERROR_OUT_OF_MEMORY;

nsresult rv = factory->QueryInterface(aIID, result);

if (NS_FAILED(rv)) {

*result = nsnull;

delete factory;

}

return rv;

}

//----------------------------------------

NS_IMETHODIMP

SampleModule::RegisterSelf(nsIComponentManager *aCompMgr,

nsIFile* aPath,

const char* registryLocation,

const char* componentType)

{

nsIComponentRegistrar* compReg = nsnull;

nsresult rv = aCompMgr->QueryInterface(kIComponentRegistrarIID, (void**)&compReg);

if (NS_FAILED(rv))

return rv;

rv = compReg->RegisterFactoryLocation(kSampleCID,

"Sample Class",

nsnull,

aPath,

registryLocation,

componentType);

compReg->Release();

return rv;

}

NS_IMETHODIMP

SampleModule::UnregisterSelf(nsIComponentManager* aCompMgr,

nsIFile* aPath,

const char* registryLocation)

{

nsIComponentRegistrar* compReg = nsnull;

nsresult rv = aCompMgr->QueryInterface(kIComponentRegistrarIID, (void**)&compReg);

if (NS_FAILED(rv))

return rv;

rv = compReg->UnregisterFactoryLocation(kSampleCID, aPath);

compReg->Release();

return rv;

}

NS_IMETHODIMP

SampleModule::CanUnload(nsIComponentManager *aCompMgr, PRBool *okToUnload)

{

*okToUnload = PR_FALSE; // we do not know how to unload.

return  NS_OK ;

}

//----------------------------------------------------------------------

extern "C" NS_EXPORT nsresult NSGetModule(nsIComponentManager *servMgr,

nsIFile* location,

nsIModule** return_cobj)

{

nsresult rv =  NS_OK ;

// Create and initialize the module instance

SampleModule *m = new SampleModule();

if (!m) {

return NS_ERROR_OUT_OF_MEMORY;

}

// Increase refcnt and store away nsIModule interface to m in return_cobj

rv = m->QueryInterface(kIModuleIID, (void**)return_cobj);

if (NS_FAILED(rv)) {

delete m;

}

return rv;

}



































你可能感兴趣的:(gecko,XPCOM)