XPCOM Part 3: Setting up XPCOM

http://www.ibm.com/developerworks/webservices/library/co-xpcom3.html

。。。

Enabling XPCOM in an application

Before an application can actually begin using XPCOM components, there are a set of libraries that must be loaded and initialized for the XPCOM framework to operate. Here’s a sample app to do that:

Listing 1. Sample app to load and initialize necessary libraries #include <stdio.h>
#include <nsIServiceManager.h>
#include <nsISomething.h>

int main()
{
static const char szContractId[] =
"Your component’s contract ID goes here";
nsresult rv;

// Initialize XPCOM and check for failure …
rv = NS_InitXPCOM(nsnull, nsnull);
if ( NS_FAILED(rv) )
{
printf("Calling NS_InitXPCOM returns [%x].n", rv);
return -1;
}

// optional autoregistration - forces component manager to check for new components.
(void)nsComponentManager::AutoRegister(nsIComponentManager::NS_Startup, nsnull);

// Create an instance of our component
nsCOMPtr
mysample = do_CreateInstance(szContractId, &rv);
if ( NS_FAILED(rv) )
{
printf("Creating component instance of %s fails.n", szContractId);
return -2;
}

// Do something useful with your component …

// (main body of code goes here)

// Released any interfaces.

// Shutdown XPCOM
NS_ShutdownXPCOM(nsnull);
return 0;
}

The two crucial calls are NS_InitXPCOM and NS_ShutdownXPCOM. The XPCOM core libraries are normally located in the same directory as the application, and an additional subdirectory named "components" is required. Once XPCOM has been initialized, the two big XPCOM components of immediate interest for doing something productive are the Component Manager and the Service Manager. The call to AutoRegister is actually optional, and is really only needed when a new component is installed.

The above example assumes a stand-alone application that does not need browser support. nsISomething is a placebo for some actual interface. Real-world application code that makes use of the interface would be placed where the "main body of code goes here" comment appears above.



Back to top

Component manager

The component manager does just what its name implies. It keeps track of what components are currently installed and what DLL or shared library must be loaded to create a specific component. The components subdirectory mentioned above is where the component manager expects to find any components. It scans this directory in the AutoRegister step above looking for components not already registered and adds a descriptive entry into a private map file. Subsequent requests for components happen much quicker because it already knows from the map which DLL or shared library to load. Components are identified in one of two ways: a 128-bit UUID known as a class ID or CID or a short text name known as a Contract ID.

Note to MSCOM programmers: a contract ID is functionally equivalent to an MSCOM Program ID or ProgID.

Here are some core methods offered by the component manager in IDL. The first one looks up the class ID for a given contract ID. If you plan to create a lot of components of the same class and you only know the component’s contract ID, you can improve performance by calling this method first and using the shorter, faster class ID for subsequent calls to createInstance.

void contractIDToClassID(in string aContractID, out nsCID aClass);

This next method just does the inverse of the above:

string CLSIDToContractID(in nsCIDRef aClass, out string aClassName);

The following method verifies that some component has been registered and is therefore available for use:

boolean isRegistered(in nsCIDRef aClass);

The next two methods do all the grunt work for loading an arbitrary XPCOM component. You get your choice of identifying a component in the first parameter by class ID with createInstance or contractID in createInstanceByContractID. The second parameter aDelegate is only needed when you are doing what is called aggregation and is usually set to nsnull. The third parameter is the interface IID.

voidPtr createInstance(in nsCIDRef aClass, in nsISupports aDelegate, in nsIIDRef aIID);
voidPtr createInstanceByContractID(in string aContractID, in nsISupports aDelegate, in nsIIDRef IID);

The IDL source for all of the component manager methods can be found in nsIComponentManager.idl.



Back to top

Service manager

XPCOM services are referred to in some books as singleton objects. No matter how many times you request a service you will always receive an interface to the same component. You’ve already seen the biggest service of them all – the component manager. How’s this for indirection: the service manager is itself a service. In a nutshell, the service manager takes care of loading and unloading services. When a request is made for a service that is already loaded, the service manager is smart enough to return another pointer to the existing service instead of trying to construct another object. Note how this behavior differs from the component manager which gives you a fresh new component on each request. Services are usually requested using the NS_WITH_SERVICE macro as illustrated in Listing 2.

Listing 2. Service request { // enter scope of service smart pointer …
NS_WITH_SERVICE(nsIMyService, service, kMyServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
service->DoSomething(…); // use my service
} // leaving scope of service smart pointer …

Note that the NS_WITH_SERVICE macro uses nsCOMPtr to create a smart pointer. Some examples of services are listed in Table 2.

Table 2. Examples of services
  • LDAP
  • WebShell
  • JSRuntime
  • Editor
  • EventQueue
  • RDF
  • SMTP
  • IMAP
  • POP3
  • NNTP
  • DNS
  • Error
  • Logging



Back to top

Category manager

The component manager and service manager will fetch a component given its contract or class ID. How would you go about finding components without either of these? The answer to this question is the category manager. The category manager provides a directory of class IDs grouped into categories. When I say "directory" here, think phone book (as in the Yellow Pages) and not disk drive. Using the phone book analogy, if we wanted to find all of the hotels in the area, we could look up "hotels" in the phone book.

Suppose you’ve written a neat editor with lots of word processing features that supports multiple document types through a generic set of interfaces. The document types you chose to support are text, HTML, RTF and PDF – but you’ve also registered your document handlers under the "document handler" category and written your program to always check the document handler category to determine what document types are available.

Your friend decides your program desperately needs to support WordPerfect files so she creates a new document handler component that implements the same set of interfaces, and she registers it under the document handler category. Any user of your program can now download, install and begin using your friend’s new document handler without any extra work on your part.

Components grouped under a category usually have something in common, like a predefined set of interfaces. The category becomes an implied contract for any component registering itself under that category. Categories are a very powerful means of achieving object independence since the code that makes use of a category only cares about the interfaces and can divorce itself from any specific implementation. Despite this power, categories are one of the most under-used features of XPCOM.