virtual int foo(int bar) = 0;
Pure virtual interfaces provide an easy mechanism for passing function tables between modules that may reside in separate, possibly dynamically loaded, libraries. Each interface is assigned a unique Interface Identifier, or IID.
Interface interrogation is performed using the QueryInterface() method. The caller passes in an ID and a pointer to a address to place the resulting interface. If the query is successful, QueryInterface() will return NS_OK. If the object does not support the given interface, it will return NS_NOINTERFACE.
Reference counting is performed using the AddRef() and Release()
methods. An objects reference count generally starts at zero. AddRef()
increments that reference count, and Release() decrements it.
If a call to Release() causes the reference count to hit zero,
the object will generally free itself. A successful QueryInterface()
will call AddRef() on the requested interface before returning.
Both AddRef() and Release() return the resulting reference
count.
| /*
* The nsISupports interface */ class nsISupports {
|
All Mozilla interfaces inherit from nsISupports. Inheriting from nsISupports allows any interface to be interrogated about other interfaces that its instance may support, and insures that reference counting facilities are always available. The IID for nsISupports is defined as NS_ISUPPORTS_IID.
QueryInterface() has several important characteristics that must be maintained. If you perform a QueryInterface() on interface A and obtain interface B, you must be able to perform a QueryInterface() B and obtain interface A. If interfaces A and B are both implemented by the same instance, performing a QueryInterface() for nsISupports on either should return the exact same interface. This means that even though interface B inherits from nsISupports, performing a QueryInterface() on it may not return the same interface. This important behavior is the only reliable mechanism for determining whether interfaces A and B are implemented by the same object.For simple objects, maintaining these behaviors is easy. Aggregation, as we will see later, can complicate things.
On the other hand, objects are allowed a certain degree of flexibility in their implementations of AddRef() and Release(). They can maintain a single reference count for the entire object, or individual reference counts for each interface. A static object would chose to ignore reference counts altogether. However, a poor implementation of these functions can have negative results, such as memory leaks or inadvertent access of freed objects.
| /*
* The nsIFactory interface */ class nsIFactory: public nsISupports {
|
| class NSRepository {
public: // Finds a factory for a specific class ID static nsresult FindFactory(const nsCID &aClass, nsIFactory **aFactory); // Creates a class instance for a specific class ID
// Manually registry a factory for a class
// Manually registry a dynamically loaded factory for a class
// Manually unregister a factory for a class
// Manually unregister a dynamically loaded factory for a
class
// Unload dynamically loaded factories that are not in use
|
There are several ways a factory can make its way into the repository. The most direct is through RegisterFactory(). RegisterFactory() supports two different registration mechanisms. The first takes a class ID and a pointer to a factory. This mechanism can be used on factories that are linked into the executable. The second takes a class ID and the path to a dynamically loadable library. This mechanism can be used both inside an executable at run-time and externally using the aPersist flag to tell the repository to store the class ID/library relationship in its permenant store.
| struct nsID {
PRUint32 m0; PRUint16 m1, m2; PRUint8 m3[8]; }; |
{221ffe10-ae3c-11d1-b66c-00805f8a2676}
To initialize an ID struct you declare them like this:
ID = {0x221ffe10, 0xae3c, 0x11d1,
{0xb6, 0x6c, 0x00, 0x80, 0x5f,
0x8a, 0x26, 0x76}};
Why the b66c couplet gets broken up and grouped with the last set of bytes is probably a footnote somewhere. On Windows you can use the programs uuidgen and guidgen, which ship with Visual C++, to generate IDs.
| #include "nsISupports.h"
// {57ecad90-ae1a-11d1-b66c-00805f8a2676}
/*
class nsISample: public nsISupports {
|
nsSample.h defines the class ID (CID) for our sample class. Note that
one interface can have a number of classes that implement it, so there
is not necessarily a one-to-one mapping from IIDs to CIDs. It also defines
the function for retrieving our class factory. Notice it does not contain
a class declaration.
| #include "nsIFactory.h"
// {d3944dd0-ae1a-11d1-b66c-00805f8a2676}
extern nsresult GetSampleFactory(nsIFactory **aResult); |
nsSample.cpp contains both the declaration and implementation of our
sample class, and the declaration and implementation of our class factory.
| #include "nsISample.h"
#include "nsSample.h" static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
/*
class nsSample: public nsISample {
public:
// nsISupports methods
// nsISample method
/*
class nsSampleFactory: public nsIFactory {
public:
// nsISupports methods
// nsIFactory methods
NS_IMETHOD_(void) LockFactory(PRBool aLock);
/*
nsSample::nsSample()
nsSample::~nsSample()
NS_IMETHOD nsSample::QueryInterface(const nsIID &aIID,
// Always NULL result, in case of failure
if (aIID.Equals(kISupportsIID)) {
if (aResult != NULL) {
AddRef();
nsRefCount nsSample::AddRef()
nsRefCount nsSample::Release()
/*
nsSampleFactory::nsSampleFactory()
nsSampleFactory::~nsSampleFactory()
NS_IMETHODIMP nsSampleFactory::QueryInterface(const nsIID &aIID,
// Always NULL result, in case of failure
if (aIID.Equals(kISupportsIID)) {
if (*aResult == NULL) {
AddRef(); // Increase reference count for caller
NS_IMETHODIMP(nsRefCount) nsSampleFactory::AddRef()
NS_IMETHODIMP(nsRefCount) nsSampleFactory::Release()
NS_IMETHODIMP nsSampleFactory::CreateInstance(nsISupports *aOuter,
*aResult = NULL; nsISupports inst = new nsSample(); if (inst == NULL) {
nsresult res = inst->QueryInterface(aIID, aResult); if (res != NS_OK) {
return res;
void nsSampleFactory::LockFactory(PRBool aLock)
nsresult GetSampleFactory(nsIFactory **aResult)
*aResult = NULL; nsISupports inst = new nsSampleFactory(); if (inst == NULL) {
nsresult res = inst->QueryInterface(kIFactoryIID, aResult); if (res != NS_OK) {
return res;
|
main.cpp is a simple program that creates an instance of our sample
class and disposes of it. Because it obtains the class factory directly,
it doesn't use the CID for class.
| #include "nsISample.h"
#include "nsSample.h" static NS_DEFINE_IID(kISampleIID, NS_ISAMPLE_IID); int main(int argc, char *argv[])
nsISample *sample; nsresult res = factory->CreateInstance(NULL, kISampleIID,
if (res == NS_OK) {
return 0;
|
| // Returns the factory associated with the given class ID
extern "C" NS_EXPORT nsresult NSGetFactory(const nsCID &aCID, nsIFactory **aFactory); // Returns whether the DLL can be unloaded now.
|
NSCanUnload() is an optional, but useful function. If implemented, it allows the NSRepository to free up memory by unloading DLLs it is no longer using when FreeLibraries() is called. The implementation takes into consideration two things when deciding whether or not a DLL can be unloaded: Whether any of its factories are currently in use, and whether anyone has locked the server. If NSCanUnload() is not implemented, the DLL will not be unloaded.
The following example turns nsSample.cpp into a file that can
be compiled into a DLL. The differences are highlighted in blue.
There really aren't that many.
| #include <iostream.h>
#include "pratom.h" #include "nsRepository.h" #include "nsISample.h" #include "nsSample.h" static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
/*
static PRInt32 gLockCnt = 0;
/*
class nsSample: public nsISample {
public:
// nsISupports methods
// nsISample method
/*
class nsSampleFactory: public nsIFactory {
public:
// nsISupports methods
// nsIFactory methods
NS_IMETHOD_(void) LockFactory(PRBool aLock);
/*
nsSample::nsSample()
nsSample::~nsSample()
NS_IMETHODIMP nsSample::Hello() {
return NS_OK;
NS_IMETHODIMP nsSample::QueryInterface(const nsIID &aIID,
// Always NULL result, in case of failure
if (aIID.Equals(kISupportsIID)) {
if (aResult != NULL) {
AddRef();
NS_IMETHODIMP nsSample::AddRef()
NS_IMETHODIMP nsSample::Release()
/*
nsSampleFactory::nsSampleFactory()
nsSampleFactory::~nsSampleFactory()
NS_IMETHODIMP nsSampleFactory::QueryInterface(const nsIID &aIID,
// Always NULL result, in case of failure
if (aIID.Equals(kISupportsIID)) {
if (*aResult == NULL) {
AddRef(); // Increase reference count for caller
NS_IMETHODIMP_(nsrefcnt) nsSampleFactory::AddRef()
NS_IMETHODIMP_(nsrefcnt) nsSampleFactory::Release()
NS_IMETHODIMP nsSampleFactory::CreateInstance(nsISupports *aOuter,
*aResult = NULL; nsISupports *inst = new nsSample(); if (inst == NULL) {
nsresult res = inst->QueryInterface(aIID, aResult); if (res != NS_OK) {
return res;
/*
void nsSampleFactory::LockFactory(PRBool aLock)
extern "C" NS_EXPORT nsresult NSGetFactory(const
nsCID &aCID,
*aResult = NULL; nsISupports *inst; if (aCID.Equals(kSampleCID)) {
if (inst == NULL) {
nsresult res = inst->QueryInterface(kIFactoryIID, (void **) aResult); if (res != NS_OK) {
return res;
extern "C" NS_EXPORT PRBool NSCanUnload()
|
| #include "nsRepository.h"
#include "nsISample.h" #include "nsSample.h" static NS_DEFINE_IID(kISampleIID, NS_ISAMPLE_IID);
int main(int argc, char *argv[])
nsresult res = NSRepository::CreateInstance(kSampleCID,
if (res == NS_OK) {
return 0;
|
A DLL can export two additional functions for self registration and
unregistration.
| extern "C" NS_EXPORT nsresult NSRegisterSelf(const char *path);
extern "C" NS_EXPORT nsresult NSUnregisterSelf(const char *path); |
| IFoo *GetFoo()
{ IFoo *res = mFoo; res->AddRef(); return res; } |
| nsresult GetFoo(IFoo **aFooRes)
{ if (aFooRes == NULL) { return NS_ERROR_NULL_POINTER; } *aFooRes = mFoo; (*aFooRes)->AddRef(); return NS_OK;
|
| nsresult TweekFoo(IFoo *aFoo1, IFoo *aFoo2) {
IFoo local = aFoo; if (aFoo->Bar() == NS_OK) {
return local->Boff();
|
| nsresult RefreshFoo(IFoo **aFoo)
{ if (aFoo == NULL || *aFoo == NULL) { return NS_ERROR_NULL_PARAMETER; } if ((*aFoo)->Stale()) { (*aFoo)->Release(); *aFoo = mFoo; (*aFoo)->AddRef(); } return NS_OK; } |
| mFoo->AddRef();
TweekFoo(mFoo); mFoo->Release(); |
Microsoft provides an extensive support infrastructure for COM. This technology is built into Windows, but not most other platforms. The technology can be licensed from Microsoft, but for obvious reasons we are not going to be doing that. In house equivalents to the important elements of this technology will be developed as needed.