複製鏈接
請複製以下鏈接發送給好友

XPCOM

鎖定
XPCOM是一個跨平台組件模型,它的全稱為Cross Platform Component Object Module。XPCOM實現了一個框架(framework),這個框架中,它允許開發者打破單一整體的軟件項目,而分解為多個更小的模塊化碎片(pieces),這些碎片也即組件(components)。
中文名
XPCOM
概    述
XPCOM是一個跨平台組
簡    介
本文簡單介紹了 XPCOM 和
解決方案
Cross Platform Component

目錄

XPCOM基本信息

XPCOM: Cross Platform Component Object Module 跨平台組件對象模塊。基於Mozilla的瀏覽器現在可以通過使用跨平台組件對象模塊(Cross Platform Component Object Module,XPCOM)組件添加它們自己的功能。XPCOM是一個框架,它允許對項目進行模塊化設計,將整個開發劃分為多個組件。組件然後在運行時期重新裝配,創建出組件的克隆共同創建一個應用程序。

XPCOM簡介

XPCOM簡介
本文簡單介紹了 XPCOM 和組件開發的基本概念和技術. 本文的大部分內容從一個很高的角度介紹了這些概念, 這些概念將在創建一個稱為 WebLock 的 Mozilla 組件過程中逐漸透徹的講述.
XPCOM 解決方案
Cross Platform Component Object Module (XPCOM) 是一個允許開發人員把一個大的工程劃分成小的模塊的框架. 這些小模塊稱為組件, 它們在運行時刻組裝在一起.
XPCOM 的目標是使軟件的不同部分分別開發, 相互獨立. 為了是應用的不同組件之間能夠互操作, XPCOM 把組件的實現與接口(後面討論 接口) 分開. 同時 XPCOM 還提供了加載和操縱這些組件的庫和工具以及服務, 以幫助開發人員編寫跨平台的代碼和組件版本管理; 因此組件可以在不破壞應用或者重新生成應用的同時被替換被更新. 通過使用 XPCOM, 開發人員創建的組件可以在不同的應用中被重用, 或者替換當前應用中的組件以改變應用的功能.
XPCOM 不只提供組件軟件開發的支持, 它同時提供一個開發平台的大多數功能的支持:
組件管理
文件抽象
對象消息傳遞
我們將在後面的章節中詳細討論上面的條目, 但是現在, 僅僅把 XPCOM 想象成一個 組件開發平台, 它提供了上面的這些特性.
Gecko
XPCOM 儘管在許多方面類似於 Microsoft COM, 但 XPCOM 被設計成主要應用於應用層. XPCOM 的最重要的應用是 Gecko, 一個開源的, 遵循標準的, 嵌入式 web 瀏覽器和 工具包.
XPCOM 是訪問 Gecko 庫和嵌入或擴展 Gecko 的方法. 本書着重於後者 - 擴展 Gecko - 但是本書中描述的基本思想對嵌入 Gecko 的開發人員也很重要.
Gecko 應用在許多 internet 程序中, 主要是瀏覽器. 包括 Gateway/AOL Instant AOL device 和 Nokia Media Terminal. Gecko 最近還被應用於的 Compuserve 客户端, AOL for Mac OS X, Netscape 7, 當然還包括 Mozilla 客户端. Gecko 是目前而言主流的開源瀏覽器.
組件
XPCOM 允許把一個大的工程分解為一些小部分. 這些小部分稱為組件, 通常是一些小的可重用的二進制庫(在 Windows 下表現為一個 DLL , Unix 下為一個 DSO ), 這些二進制庫可以包含一個或多個組件. 當多個相關組件被封裝到一個二進制庫, 這個庫也稱為模塊.
把軟件劃分成不同的組件可以使開發和維護工作變得更容易. 除了這個好處, 模塊化組件化的編程還有下面的優勢:
優點 描述
重用 模塊化的代碼可以在其他應用和環境中被重用.
更新 在不需要重新編譯整個應用的情況下更新組件.
性能 代碼按照模塊化組織, 模塊就不必要立刻被加載, 而是以 "lazy" 方式加載, 或者根本不需要加載. 這樣就可以提升應用的整體性能.
維護 甚至在不更新組件的情況下, 按照模塊化的方式設計應用也能夠使你對感興趣的部分的維護變得更容易.
Mozilla 有幾百萬行代碼, 沒有那個人能夠徹底搞明白整個代碼的細節. 面對這樣的一個系統, 最好的方式是把它劃分成一些小的, 更容易管理的部分; 同時使用組件模型來編程, 把相關組件組織到各個模塊中去. 比如説, Mozilla 的網絡庫就包含了 HTTP, FTP, 及其他協議的組件, 這些組件被塞到一個單獨的庫裏. 這個庫就是網絡模塊, 也就是通常所説的 "necko."
但是把事物劃分開來並不一定就好, 有許多事物作為一個整體組織起來才是最好的方式, 或者根本就不能劃分開來. 比如説, 一個小孩就可能不吃沒有果醬的三明志, 對於他來説, 果醬和三明志是缺一不可的整體. 同樣的情形也會存在於某些軟件中, 某些需要緊耦合的僅僅在內部使用的部分就並不需要費力去把它拆開成為小的模塊.
Gecko 中的 HTTP 組件並不會暴露它內部的類, 它是作為一個整體來使用. 組件內部的事物不應該暴露給 XPCOM. 在 Mozilla 早期的開發中, 有些組件的創建並不適當, 但是隨着開發的深入, 這些部分會被逐漸移出 XPCOM.
接口
把軟件劃分成組件通常一個好的解決方法, 但是我們該怎麼做呢? 基本的思路是按照功能進行劃分, 理解這些功能塊之間的如何進行通信. 這些組件之間的通信通道就構成了各個組件的邊界, 這些邊界形式的描述為接口.
接口並不是編程中的一個新的概念. 從我們的第一個 "HelloWorld" 程序開始我們就一直在不知不覺的使用它, 這裏的接口就存在於我們的應用程序和打印程序之間. 應用程序使用 stdio 庫提供的接口來把 "hello world" 字符串打印到屏幕上. XPCOM 與 "HelloWorld" 程序的不同之處在於 XPCOM 會在運行時刻找到這個屏幕打印的功能, 而 XPCOM 事前根本就不需要在編譯的時刻了解 stdio 庫提供了什麼東西.
接口允許開發人員把功能的具體實現封裝在組件內部, 而客户程序不需要了解這個功能是如何實現的, 它只需要使用它就行了.
接口與按照契約(Contract)編程
一個接口在組件與客户程序之間達成契約. 並沒有任何強制措施保證認同這個契約, 但是忽略它會是致命的. 在基於組件的編程中, 組件保證它提供的接口是不變的 - 不同版本的組件都要提供同樣的訪問方法 - 這就與使用它的客户程序達成了一種契約. 從這種角度來説, 基於組件的編程通常也稱為按照契約編程.
接口與封裝
組件邊界之間的抽象對軟件的可維護性與可重用性是致關重要的. 舉個例子來説, 考慮下面一個沒有很好封裝的類. 在下面的例子中, 使用可自由訪問的 public 的初始化方法可能會出問題.
SomeClass類初始化
class SomeClass{ public: // Constructor SomeClass(); // Virtual Destructor virtual ~SomeClass(); // init method void Init(); void DoSomethingUseful();};系統要工作正常, 客户程序員必須注意到組件程序員建立的潛規則. 這就是這個未很好封裝的類的契約: 這是一套關於何時該調用一個方法, 調用這個方法之前應該做什麼的規則. 這個規則可能指出 DoSomethingUseful 方法只能在調用 Init() 之後被使用. DoSomethingUseful 方法可能會做某些檢查工作以保證條件滿足 - Init 已經被調用.
除了在代碼中給出註釋告訴客户程序員關於 Init() 規則之外, 程序員可以使他的契約更明晰. 首先對象的構造函數可以封裝起來, 然後向客户程序員提供一個聲明 DoSomethingUseful 方法的虛基類. 通過這種方式, 構造函數和初始化函數被隱藏起來. 在這種半封裝條件下, 這個類只向客户程序暴露一些良好定義的可調用方法(或者稱為接口). 一旦按照這種方式封裝一個類, 客户程序只能看到的是下面的接口:
SomeInterface封裝
class SomeInterface{ public: virtual void DoSomethingUseful() = 0;};實現類就可以從這個虛基類派生, 實現接口的功能. 客户程序使用類廠(factory pattern)模式來創建對象(參看 類廠), 而封裝類的內部實現. XPCOM 以這種方式把客户程序屏蔽在組件的內部工作之外, 而客户程序也只依賴於提供所需要功能的接口.
nsISupports 基接口
組件與基於接口的編程的兩個最基本的問題是: 一個是組件生存期, 也稱為對象所屬關係; 另一個是接口查詢, 它是在運行時刻確定接口能夠提供哪些接口. 這一節介紹基接口 nsISupports - 它是 XPCOM 中所有接口的父接口, 它提供了上面兩個問題的解決方案.
對象所屬關係
在 XPCOM 中, 由於組件可以實現任意多的不同接口, 接口必須是引用計數的. 組件在內部跟蹤客户程序對它的接口引用了多少次, 當接口計數為零的時候組件就會卸載它自己.
當一個組件創建後, 組件內部有一個整數在跟蹤這個引用計數值. 客户程序實例化組件就會自動對這個引用計數加一; 在組件的整個生存期內, 引用計數或加或減, 總是大於零的. 如果在某個時刻, 所有的客户程序對該組件都失去了興趣, 引用計數減到零, 組件就會卸載自己.
客户程序使用相關接口是一個非常直接的過程. XPCOM 有一些工具讓我們更方便的使用接口, 我們會在後面講述. 如果客户程序在使用接口的時候忘記對接口的引用計數進行相關操作, 就會對組件的維護工作帶來某些問題. 此時, 由於組件的引用計數始終不為零, 它就永遠不會釋放, 從而導致內存泄漏. 引用計數系統就象 XPCOM 的許多其他事物一樣, 是客户與組件之間的契約. 如果遵守這些契約, 就會工作得很正常, 反之不然. 由創建接口指針的函數負責對初始化的接口引用加1, 這個引用也稱為所屬引用.
XPCOM中的指針
XPCOM 中的指針術語指的是接口指針. 它與常規指針相比有細微的差別, 畢竟它們都指向的是某個內存區域. 但是 XPCOM 指針指向的都是從 nsISupports 基接口派生而來的接口實現, 這個基接口包括三個基本的方法: AddRef, Release, 和 QueryInterface.
nsISupports 接口提供了對接口查詢與引用計數基本的支持. 這個接口的成員方法包括: QueryInterface, AddRef, 和 Release. 這些方法提供了從一個對象獲取正確接口的基本方法, 加引用計數, 釋放不再使用的對象. nsISupports 接口的聲明如下:
nsISupports 接口
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);};接口中使用的各種數據類型見 XPCOM 類型一節. nsISupports 接口的實現代碼如下:
nsISupports 接口實現
Sample::Sample(){ // initialize the reference count to 0 mRefCnt = 0;}Sample::~Sample(){}// typical, generic implementation of QINS_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; } // add a reference AddRef(); return NS_OK;}NS_IMETHODIMP_(nsrefcnt) Sample::AddRef() { return ++mRefCnt;}NS_IMETHODIMP_(nsrefcnt) Sample::Release(){ if (--mRefCnt == 0) { delete this; return 0; } // optional: return the reference count return mRefCnt;}對象接口的發現
繼承是面向對象編程中另一個非常重要的話題. 繼承是通過一個類派生另一個類的方法. 當一個類繼承另一個類, 繼承類可以重載基類的缺省動作, 而不需要拷貝基類的代碼, 從而創建更加專有的類. 如下所示:
簡單類繼承
class Shape{ private: int m_x; int m_y; public: virtual void Draw() = 0; Shape(); virtual ~Shape();}; class Circle : public Shape{ private: int m_radius; public: virtual Draw(); Circle(int x, int y, int radius); virtual ~Circle();};Circle 從 Shape 類派生. Circle 本身也是一個 Shape, 但是 Shape 並不一定是 Circle. 在這種情況下, Shape 是基類, Circle 是 Shape 的子類.
在 XPCOM 中, 所有的類都派生自 nsISupports 接口, 這樣所有的對象都提供 nsISupports接口, 但是這些對象是更專有的類, 在運行時刻必須能找到它. 比如説在 簡單類繼承例子中, 如果對象是一個 Circle, 你就可以調用 Shape 類的方法. 就是為什麼在 XPCOM 中, 所有的對象都派生自 nsISupports 接口: 它允許客户程序根據需要發現和訪問不同的接口.
在 C++ 中, 我們可以使用 dynamic_cast<> 來把一個 Shape 對象的指針強制轉化成一個 Circle 指針, 如果不能轉化就拋出異常. 但是在 XPCOM 中, 由於性能開銷和平台兼容性問題, 採用RTTI 的方法是不行的.
XPCOM 中的異常
XPCOM 並不直接支持 C++ 的異常處理. 在 XPCOM 中, 所有的異常必須在組件內部處理, 而不能跨越接口的邊界. 然後接口方法返回一個 nsresult 錯誤值(這些錯誤碼請參考 XPCOM API Reference). 客户程序根據這些錯誤碼進行"異常"處理.
XPCOM 沒有采用 C++ RTTI 機制來實現對象指針的動態轉化, 它使用 QueryInterface 方法來把一個對象指針 cast 成正確的接口指針.
每個接口使用稱為 "uuidgen" 的工具來生成專有ID. 這個 ID 是一個全局唯一的 128-bit 值. 在接口的語境中, 這個 ID 又稱為 IID. (組件語境中, 這個 ID 代表的是一個契約)
當客户程序想查詢一個對象是否支持某個接口, 它把接口的 IID 值傳遞給這個對象的 QueryInterface 方法. 如果對象支持這個接口, 它就會對自己的引用計數加1, 然後返回指向那個專有接口的指針. 反之, 如果不支持這個接口, 它會返回一個錯誤碼.
class nsISupports { public: long QueryInterface(const nsIID & uuid, void **result) = 0; long AddRef(void) = 0; long Release(void) = 0;};QueryInterface 的第一個參數是一個 nsIID 類型的引用, 它封裝了 IID. nsIID 類有三種方法: Equals, Parse, 和 ToString. Equals 在接口查詢中是最重要的, 它用來比較兩個 nsIID 對象是否相同.
在客户以 IID 調用 nsISupports 接口的 QueryInterface 方法時, 我們必須保證返回一個有效的 result 參數(在Using XPCOM Utilities to Make Things Easier 一章中, 我們將看到怎樣更方便的實現一個 nsIID 類). QueryInterface 方法應該支持該組件所有接口的查詢.
在 QueryInterface 方法的實現中, IID 參數與組件支持 nsIID 類進行比較. 如果匹配, 對象的 this 指針轉化為 void 指針, 引用計數加1, 把 void 指針返回給客户程序.
在上面的例子中, 僅僅使用 C 方式的類型轉化就足夠了. 但是在把 void 指針 cast 成接口指針的時候, 還有更多的問題, 因為返回的接口指針必須與 vtable 相對應. 當出現菱形多重繼承的時候, 可能這種接口轉化就會有問題.
XPCOM 的ID
除了上一節中的接口 IID, XPCOM 還使用兩種 ID 來區分類與組件.
XPCOM ID類
nsIID 類實際上是一種 nsID 類. 其他的 nsID, CID 和 IID, 分別指的是一個實體類和一個專有的接口.
nsID 類提供 Equals 類似的方法, 來比較 ID. 請參考 Identifiers in XPCOM 一節, 其中對 nsID 類有更多的討論.
CID
CID 是一個唯一的 128-bit 值, 類似於 IID, 它用於全局標識唯一的類或者組件. nsISupports 的 CID 就象:
00000000-0000-0000-c000 000000000046
由於 CID 比較長, 通常我們都使用#define來定義它:
#define SAMPLE_CID \ { 0x777f7150, 0x4a2b, 0x4301, \ { 0xad, 0x10, 0x5e, 0xab, 0x25, 0xb3, 0x22, 0xaa}} 我們將會看到許多 NS_DEFINE_CID 的定義. 下面的宏用 CID 定義了一個靜態變量:
static NS_DEFINE_CID(kWebShellCID, NS_WEB_SHELL_CID);CID 有時也稱為類 ID. 如果一個類實現了多個接口, 當CID 在該類發佈後, 就與該類的 IID 一起被凍結了.
契約 ID
契約 ID 是一個用於訪問組件的可讀(to humen)的字符串. 一個 CID 或者一個契約 ID 可以用於從一個組件管理器獲取組件. 下面是一個用於 LDAP 操作組件的契約 ID:
"@mozilla org/network/ldap-operation;1"契約 ID 的格式是: 用斜槓分開的組件的域, 模塊, 組件名, 版本號.
與 CID 類似, 契約 ID 指的是組件實現而不是接口. 但是契約 ID 並不像CID那樣,被限定為某個專有實現, 它更通用. 一個契約 ID 只是指明需要實現的一組接口,可以通過任意數量的CID滿足這個需要. 契約 ID 與 CID 的這種不同, 使得組件重寫成為可能.
把代碼劃分成組件, 客户代碼通常使用 new 操作符來構造實例對象:
SomeClass* component = new SomeClass();這種模式或多或少地需要客户對組件有一定的瞭解,至少要知道組件的大小. 類廠設計模式此時可以用來封裝對象的構造過程. 類廠模式的目的是在不暴露對象的實現和初始化的前提下創建對象. 在 SomeClass 例子中, 可以按照類廠模式把 SomeClass 對象的構造和初始化封裝在 New_SomeInterface 函數中:
封裝構造函數
int New_SomeInterface(SomeInterface** ret){ // create the object SomeClass* out = new SomeClass(); if (!out) return -1; // init the object if (out->Init() == FALSE) { delete out; return -1; } // cast to the interface *ret = static_cast<SomeInterface*>(out); return 0; } 類廠本身是一個管理組件實例化的類. 在 XPCOM 中, 類廠要實現 nsIFactory 接口, 它們就象上面的代碼一樣要使用類廠設計模式來封裝對象的構造和初始化.
封裝構造函數 的例子是一個簡單的無狀態的類廠版本, 實際的編程要複雜一些, 一般的類廠都需要保存狀態. 類廠至少應該保存那些對象已經被創建了的信息. 如果類廠管理的實例被存放在一個動態聯接庫中, 還需要知道什麼時候可以卸載這個動態聯接庫. 當類廠保存了這樣的信息, 就可以向類廠查詢一個對象是否已經被創建.
另一個需要保存的信息是關於單件(singleton). 如果一個類廠已經創建了一個單件類型的類, 後續的創建該單件的函數調用將返回已經創建的對象. 儘管有更好的工具和方式來管理單件(在討論 nsIServiceManager 會看到), 開發人員可能仍然需要通過這種方式來保證只有一個單件對象被創建.
廠模式可以完全利用函數來做, 狀態可以保存在全局變量中; 但是使用類的方式來實現廠模式還有更多的好處. 其一是: 我們可以管理從 nsISupports 接口派生而來的類廠本身的生存期. 當我們試圖把多個類廠劃分成一組, 然後確定是否能卸載這一組類廠的時候, 這一點非常重要. 另一個好處是: 類廠可以引入其他需要支持的接口. 在我們後面討論 nsIClassInfo 接口的時候, 我們會看到某些類廠使用這個接口支持信息查詢, 諸如這個對象是用什麼語言寫的, 對象支持的接口等等. 這種派生自 nsISupports 的 "future-proofing" 特性非常關鍵.
XPIDL 與類型庫
定義接口的簡單而強勁的方法是使用接口定義語言(IDL) - 這實際上是在一個跨平台而語言無關開發環境下定義接口的需求. XPCOM 使用的是源自於 CORBA OMG 接口定義語言(IDL)的變體, 稱為 XPIDL, 來定義接口, XPIDL 可以定義接口的方法, 屬性, 常量, 以及接口繼承.
採用 XPIDL 定義接口還存在一些缺陷. 它不支持多繼承, 同時 XPIDL 定義的方法名不能相同,你不能有兩個相同名字但是所接受的參數不同的函數. - 不能像 C++ 語言的成員函數一樣通過參數不同重載, 畢竟接口同時要支持類似於 C 這樣的語言.
void FooWithInt(in int x);void FooWithString(in string x);void FooWithURI(in nsIURI x);然而儘管有這些問題, XPIDL 的功能仍然是非常強大的. XPIDL 能自動生成以 .xpt 為後綴的類型庫, 或者説 typelibs. 類型庫是接口的二進制表示, 它向非 C++ 語言提供接口的訪問與控制. 非 C++ 語言通過類型庫來了解接口支持哪些方法, 如何調用這些方法, 這稱為 XPConnect. XPConnect 是 XPCOM 向類似於 JavaScript 這樣的語言提供組件訪問的 Wrapper. 參看Connecting to Components from the Interface獲取更多關於 XPConnect 的信息.
從其他類型的語言訪問接口, 常常説成是接口被反射(reflected)到這種語言. 每一個被反射的接口必須提供相應的類型庫. 當前可以使用 C, C++, 和 JavaScript 來編寫組件.
使用其他語言編寫組件
在使用其他語言創建組件的時候, 不需要利用 XPCOM 提供給 C++ 程序員的工具(諸如一些宏, 智能指針等等, 我們可以方便的利用到這種語言本身來創建組件. 比如説, 基於 Python 的 XPCOM 組件可以從 JavaScript 來調用.
參看 Resources 獲取更多使用其他語言來創建組件的信息.
所有的 XPCOM 接口都用 XPIDL 語法來定義. xpidl 編譯器會從這個 IDL 文件產生類型庫和 C++ 頭文件. 在Defining the WebLock Interface in XPIDL 一節詳細描述了 XPIDL 語法.
XPCOM 服務
當客户需要某個組件提供的功能的時候, 通常都是實例化一個新的組件對象. 比如説, 客户程序需要某些處理文件, 這裏每個組件就代表一個文件, 客户可能會同時處理多個這樣的組件.
但是在某些情況下對象表示的是一種服務, 這種服務只能有一個拷貝(儘管會有多個服務同時運行). 每次客户程序訪問服務提供的功能時, 客户程序都是與同一個服務實例在打交道. 比如説, 一個用户查詢公司的電話號碼數據庫, 數據庫作為一個對象對用户來説都是同一的. 如若不然的話, 就需要維護兩個數據庫拷貝, 這種開銷將非常大, 而且還存在數據內容不一致的問題. 單件設計模式就是提供系統中需要的這種單點訪問功能.
XPCOM 不僅提供了對組件的支持和管理服務, 它還包含了許多編寫跨平台組件所需要的其他服務. 其中包括: 跨平台文件抽象, 向 XPCOM 開發人員提供同一而強大的文件訪問功能. 目錄服務, 提供應用和特定系統定位信息. 內存管理, 保證所有對象使用同樣的內存分配器. 事件通知機制, 允許傳遞簡單消息. 本教程將在後面展現如何使用這些服務, XPCOM API Reference 一節有完整的接口索引列表.
XPCOM 類型
XPCOM 聲明瞭許多數據類型和簡單宏, 這些東西將在我們後面的例子中看到. 大多數的宏都是簡單的重定義, 下一節我們會描述一些最常用的數據類型.
方法類型
下面的類型用在 XPCOM 方法調用的參數和返回值定義中.
NS_IMETHOD 聲明方法返回值. XPCOM 的方法缺省的返回值聲明.
NS_IMETHODIMP 方法實現返回值. XPCOM 方法函數返回的時候缺省採用這種類型的返回值.
NS_IMETHODIMP_(type) 特定類型的方法實現返回值. 諸如 AddRef 和 Release 的方法不返回缺省類型, 這種返回值的不一致雖然有點不舒服, 但是必需的.
NS_IMPORT 共享庫內部使用的符號局部聲明
NS_EXPORT 共享庫導出的符號聲明.
下面的宏提供對引用計數的基本操作.
NS_ADDREF 調用 nsISupports 對象的 AddRef 方法.
NS_IF_ADDREF 與上一個方法類似, 不同之處在於這個宏在AddRef之前會檢查對象指針是否為空(虛函數指針).
NS_RELEASE 調用 nsISupports 對象的 Release 方法.
NS_IF_RELEASE 與上一個方法類似, 不同之處在於這個宏在調用Release之前會檢查空指針.
狀態碼
下面的宏測試狀態碼.
NS_FAILED 如果傳遞的狀態碼為失敗, 則返回真.
NS_SUCCEEDED 如果傳遞的狀態碼為成功, 則返回真.
變量映射
nsrefcnt 缺省的引用計數類型, 是一個 32-bit 整數.
nsresult 缺省數據類型, 是一個 32-bit 整數.
nsnull 缺省 null 類型.
通用 XPCOM 錯誤碼
NS_ERROR_NOT_INITIALIZED 如果實例未初試化, 返回該值.
NS_ERROR_ALREADY_INITIALIZED 如果實例已初試化, 返回該值.
NS_ERROR_NOT_IMPLEMENTED 如果方法未實現, 返回該值.
NS_ERROR_NO_INTERFACE 如果組件不支持某種類型接口, 返回該值.
NS_ERROR_NULL_POINTER 如果指針指向 nsnull, 返回該值 .
NS_ERROR_FAILURE 如果某個方法失效, 返回該值, 這時一個通用的錯誤值.
NS_ERROR_UNEXPECTED 如果一個未預料的錯誤發生, 返回該值.
NS_ERROR_OUT_OF_MEMORY 如果無法進行內存分配, 返回該值.
NS_ERROR_FACTORY_NOT_REGISTERED 如果一個請求的類型未註冊, 返回該值.