Developing a COM Client

 < Free Open Study > 



Now that you have implemented your first COM-based in-process server, we need to investigate the necessary COM library calls to access it. Regardless of the client's language, under the hood the same basic sequence of COM library calls is used. Some COM language mappings (such as VB) hide this process so well that the developer has little understanding of what is happening under the hood.

When COM developers make requests to SCM, they do so by calling COM library functions, which (for the most part) are contained in the granddaddy of all COM system DLLs, ole32.dll. This core system file is the gateway between your client code and the Service Control Manager (which by the way is a running service named rpcss.exe).

click to expand
Figure 3-23: SCM, ole32.dll, the client, and your server.

Bootstrapping the COM Runtime

The very first thing COM clients must be sure to do is initialize the COM subsystem. Each and every thread using the COM libraries must make a call to CoInitialize() before making any further requests from the COM runtime. When that thread is finished with the COM subsystem, a complementing call to CoUninitialize() must be made to clean things up:

// Before a COM client can work with the COM runtime (SCM), it must // load and initialize the COM libraries, and unload before termination. void main(void) {      // This parameter is reserved, and should be NULL      CoInitialize(NULL);      CoUninitialize(); }

Activating COM Objects

Once the COM runtime is ready to receive our requests, clients typically make calls to one of two COM activation functions—CoGetClassObject() or CoCreateInstance()—to load a server and create a new COM object. We will examine the use of CoGetClassObject() first, as CoCreateInstance() is simply a helper function, wrapping the call to CoGetClassObject() on your behalf.

CoGetClassObject() tells SCM to locate, load, and retrieve the IClassFactory pointer for a given coclass. From this pointer, we can create an instance of the associated coclass (via CreateInstance()), and go to town. Here is the signature of CoGetClassObject():

// This activation function is used to return the IClassFactory pointer for a given // class factory. Using this interface, the client can then create the corresponding class // object. HRESULT CoGetClassObject(REFCLSID rclsid,                       DWORD dwClsContext,                       COSERVERINFO * pServerInfo,                       REFIID riid,                       LPVOID * ppv);

The first parameter is the CLSID of the coclass you wish to create, for example CLSID_CoHexagon. As we entered this information into the system registry, SCM knows where to find the path to the binary and loads the server. The second parameter is a member from the CLSCTX enumeration, which specifies the class context of the server. You know that COM offers us location transparency, and this parameter allows you to specify if you wish an in-proc, local, or remote version of the server. The values of the CLSCTX can be one of the following:

// The class context allows a COM client to specify which 'locale' they are interested in. enum tagCLSCTX {      CLSCTX_INPROC_SERVER         = 0x1,          // In-proc server.      CLSCTX_INPROC_HANDLER        = 0x2,          // NT service.      CLSCTX_LOCAL_SERVER          = 0x4,          // Local server.      CLSCTX_REMOTE_SERVER         = 0x10,         // Remote server }    CLSCTX; 

You will specify CLSCTX_INPROC_SERVER if you desire in-proc servers, CLSCTX_ LOCAL_SERVER for local servers, or CLSCTX_REMOTE_SERVER for a remote server. You may also combine any of the CLSCTX flags, and SCM will find the server closest to the client. If you specify the predefined CLSCTX_ALL (which is an OR-ing together of INPROC, LOCAL, and REMOTE), you can effectively say to SCM "Just give me the one closest to me." If SCM finds an in-proc version, you will get this version. Next is local, followed by remote.

The third parameter, COSERVERINFO, is a structure that specifies useful information about a remote server machine. Of course, if you are not accessing a remote COM server, you can simply send in NULL for this parameter (which we will do until Chapter 5).

The fourth and fifth parameters should be quite familiar to you by now: the IID of the interface you want from the coclass and a place to put it (void**). Let's write some client code that loads up the Shapes.dll server and returns the IClassFactory pointer for the CoHexFactory coclass (without excessive error checking):

// Client side C++ COM code. // (Notice we need to include the GUID and interface definitions from the server project) #include "iid.h" #include "interfaces.h" void main(void) {      HRESULT hr;      IClassFactory* pCF = NULL;       IDraw* pDraw = NULL;      CoInitialize(NULL);     // Initialize the COM runtime.      // Get the class factory pointer of CoHexagon:      hr = CoGetClassObject(CLSID_CoHexagon, CLSCTX_INPROC_SERVER,                          NULL, IID_IClassFactory, (void**)&pCF);      // Make and render a CoHexagon.      hr = pCF->CreateInstance(NULL, IID_IDraw, (void**)&pDraw);      pDraw->Draw();      // Get IShapeEdit pointer.      IShapeEdit* pSE = NULL;      pDraw ->QueryInterface(IID_IShapeEdit, (void**)&pSE);      pSE -> Invert();      // All done.      pCF ->Release();           // ref count = 0. Destroys class factory.      pDraw -> Release();        // ref count = 1      pSE -> Release();          // ref count = 0. CoHexagon is destroyed.      CoUninitialize();          // Terminate the COM runtime. }

There you have it! We first grab the class object's IClassFactory pointer and create a CoHexagon via CreateInstance(), asking for IDraw. Also note that once we have used the IDraw interface, we can turn right around and ask IDraw for IShapeEdit given the laws of COM identity. Most importantly, call Release() on any acquired interface pointer when you are finished, to allow the coclasses to be destroyed and the server to be unloaded.

Accessing a Coclass Using CoCreateInstance()

Having seen CoGetClassObject() in action, we can now look at CoCreateInstance(). This function is useful if you only require a single instance of the coclass (CoHexagon in this example) and don't want to grab the IClassFactory directly. CoCreateInstance() finds the class object and calls CreateInstance() from the IClassFactory pointer automatically. All you do is pass in the CLSID and IID you are looking for:

// CoCreateInstance() creates the class factory for you automatically. HRESULT CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter,                       DWORD dwClsContext, REFIID riid, LPVOID * ppv);

The only difference from CoGetClassObject() is the second parameter, pUnkOuter. This parameter is used only in COM aggregation. We will not worry about this now, and simply pass in NULL. Because CoCreateInstance() does not provide us direct access to IClassFactory, we can alter the client code using CoCreateInstance(), thus bypassing any reference to the class object:

// Using CoCreateInstance() the client does not need to access or worry about the // IClassFactory interface when creating the coclass. hr = CoCreateInstance(CLSID_CoHexagon, NULL, CLSCTX_INPROC_SERVER,                     IID_IDraw, (void**)&pDraw); pDraw -> Draw(); pDraw-> Release();

So, using this function looks a lot easier than CoGetClassObject(). So why would you not use CoCreateInstance() every time? Realize that when we use CoCreateInstance(), the class object is created and destroyed each and every time. Thus, if you are interested in creating, say, ten CoHexagon objects, CoCreateInstance() creates and destroys the class factory ten times. CoGetClassObject() is far more efficient when you wish to create a batch of objects, as you are directly holding the IClassFactory pointer.

Furthermore, as CoCreateInstance() does not give you back an IClassFactory pointer directly, your client could never lock the server. Whenever you wish to lock a server into memory, you must do so using an IClassFactory pointer, and thus must use CoGetClassObject(). Either way you go, this is typical COM client code.



 < Free Open Study > 



Developer's Workshop to COM and ATL 3.0
Developers Workshop to COM and ATL 3.0
ISBN: 1556227043
EAN: 2147483647
Year: 2000
Pages: 171

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net