CoGetClassObjectFromURL()

Like CoGetClassObject(), this function will return a class factory interface for a specified CLSID. The object instance will be created if it's already installed locally. If necessary, this routine will also download and install the code from the specified URL. If the URL points to an uncompressed .OCX, .DLL, (.CLASS for Java) or .EXE file, the component is registered locally after download and the object factory instance will be created. If the URL points to a .INF file, the instruction within the .INF file will be followed to either download more URL based files, or install and download more components. If the URL is a compressed .CAB file, the file is decompressed and authenticated (via call to WinVerifyTrust()) with its embedded digital signature; the decompressed contents are then treated as if they are freshly downloaded COM servers or an .INF file as described above. This API is server agnostic, it will work with any web servers, not just the Microsoft IIS.

It's also possible to call without a specified CLSID, in this case, the content type parameter determines the CLSID of the executable to use in interpreting or displaying the specific MIME type. The MIME type to CLSID mapping is kept in the local registry directly under the key:

\HKEY_CLASSES_ROOT\MIME\Database\ContentType

Using the MIME type feature is a way for the system to associate a specific default executable to deal with a specific multimedia data type. For example, if the content is a music file in MIDI .MID format, the associated executable may be the Media Player applet.

The call prototype for CoGetClassObjectFromURL() is:

STDAPI CoGetClassObjectFromURL( REFCLSID rclsid,
                                LPCWSTR szCodeURL,
                                DWORD dwFileVersionMS,
                                DWORD dwFileVersionLS,
                                LPCWSTR szContentTYPE, 
                                LPBINDCTX pBindCtx,
                                DWORD dwClsContext,
                                LPVOID pvReserved,
                                REFIID riid,
                                VOID **ppv );
Parameter Description
rclsid A pointer to the CLSID of the object instance to be created. It can contain NULL, in which case the szContentTYPE will be used to determine what to instantiate.
szCodeURL A wide-string indicating the URL of the code for the COM object. As mentioned above, it can be a .CAB, .INF, .OCX, .DLL, .CLASS or .EXE file.
dwFileVersionMS The most significant word of the version number (usually the major version number).
dwFileVersionLS The least significant word of the version number (usually the minor version number).
szContentType A wide-string containing the MIME content type description string. It's used to determine the CLSID to instantiate in cases where the rclsid parameter contains NULL. For example, szContentType may contain audio/x-wav which will cause the sound recorder applet to be instantiated.
pBindCtx Pointer to a bind context that is passed in. Note that if the client is interested in receiving status information during the download (i.e. if the link is slow), a callback should be registered with the bind context. We'll explain what a bind context is later on in this section.
dwClsContext A value from the CLSCTX enumeration specifying the execution context of the object (i.e. CLSCTX_INPROC_SERVER, CLSCTX_INPROC_HANDLER, or CLSCTX_LOCAL_SERVER).
pvReserved Set this to NULL.
riid Pointer to the IID of the initial interface to ask for, almost always IClassFactory.
ppv An output value, a pointer to an interface pointer which will be set to point at the requested interface from the new object instance.

The possible return values from CoGetClassObjectFromURL() are:

Return Value Description
S_OK The ppv return value now contains the requested interface.
E_NOINTERFACE Cannot obtain the requested interface.
REGDB_E_READREGDB Error encountered when accessing the registry.
CO_E_ERRORINDLL The .DLL or .EXE image has problems.
CO_E_APPDIDNTREG The local server was started but it did not register any class objects.
E_ACCESSDENIED General access failure.
MK_S_ASYNCHRONOUS Asynchronous completion. If the client has registered a callback, it will be called to update status.

Asynchronous and URL Monikers

We'll now explain the pBindCtx parameter and the E_PENDING return value. These parameters are supporting the asynchronous nature of the CoGetClassObjectFromURL() call. Under the hood, the CoGetClassObjectFromURL() call makes use of a COM facility called a URL Moniker to do its magic. The URL Moniker is actually an instance of a concept known as an Asynchronous Moniker defined by the Internet Addendum to the OC96 specifications. The following is the conceptual inheritance tree.

First, we must give a quick explanation of what a Moniker is. Monikers are essentially 'smart name objects' in COM. Any COM object that implements the IMoniker interface is deemed a Moniker. The unique property of a Moniker (usually a relatively small object) is that it knows how to find the object that it names. It also completely hides from the client how it goes about finding the object. By 'binding' to a Moniker, we magically get the object that it names. Monikers provide a level of indirection between the client of an object and the object itself. This level of indirection provides unlimited extensibility because the Moniker isn't obliged to follow any standard rules or protocols when it goes about its binding operation. In the old days, Monikers were used almost exclusively for OLE linking operations. In this context, embedding means to embed an entire object into a document while linking means embedding a Moniker (much smaller object) into the document. It's easy to throw Monikers into compound documents (or any arbitrary file) because IMoniker is derived from IPersistStream, which supports serialization of the Moniker to memory, disk, or even across a network.

Asynchronous Monikers are a relatively new breed introduced to solve the problems produced by operations over a slow Internet link. Unlike its age-old parent, the binding operation on an Asynchronous Moniker can take as long as it wants. Meanwhile, the client invoking the binding operation gets freed to do other things right away. If the client registers a callback function with the Asynchronous Moniker, the Moniker will report back status information as it's performing the binding operation.

A URL Moniker is a specific implementation of an Asynchronous Moniker. We know how it does its magic. Since a URL Moniker is a name (URL in this case) that knows how to find the object that it represents (i.e. an ActiveX control stored on the web server), we can deduce that the URL Moniker will be using HTTP to fetch the object over the network. And this is indeed how it's done. Of course, if the URL should be ftp://ftp.abwil.com/objects/atldept.dll, then alternative protocols would be used (again, transparent to the client since the Moniker performs all the magic). Since the going rate for asynchronous modems is 28.8Kbps, we can quickly understand why a URL Moniker must be asynchronous. We certainly don't want to hold the client (and thus the application) in a frozen blocked state while a URL Moniker is finishing its magical binding effort.

The current implementation of URL Monikers uses standard Win32 Internet APIs (formerly called WinINET) to access the actual resources, this allows security proxies to work transparently underneath.

It's quite easy to see that our CoGetClassObjectFromURL() call is using URL Monikers underneath to do its dirty work. This is where the BindCtx requirement comes from. A bind context is just a small COM object that's passed into a Moniker during a bind operation. It functions as a client-supplied instance-identification 'scratch pad'. While not always necessary, you may want to receive status feedback during the asynchronous process, and then there's substantially more work to be done. You can easily create a bind context by making the CreateBindCtx() call. The interface that you grab a bind context object by is, not surprisingly, called IBindCtx. The IBindCtx interface comprises 13 methods altogether, including the three required by IUnknown. We aren't actually interested in any of these methods. There exists a (new!) helper function, called RegisterBindStatusCallback(), which will do our callback registration for us. The usage of RegisterBindStatusCallback() is:

HRESULT RegisterBindStatusCallback( IBindCtx *pbc,
                                    IBindStatusCallback *pbsc,
                                    IBindStatusCallback **ppbscPrevious,
                                    DWORD dwReserved ); 

The parameters required are:

Parameter Description
pbc Pointer to the IBindCtx of our bind context object.
pbsc Pointer to an IBindStatusCallback of a bind status callback object to be registered. We'll discuss how to create this later.
PpbscPrevious An output parameter which will be filled with a pointer to the previously registered IBindStatusCallback instance.
dwReserved Set this to 0.

The possible return values for RegisterBindStatusCallback() are:

Return Value Description
S_OK The operation has completed successfully.
E_INVALIDARG An argument passed was invalid.
E_OUTOFMEMORY The operation can't be performed because memory was exhausted during its execution.

The use of the function is straightforward, except for the IBindStatusCallback pointer that we need. Typically, the ppbscPrevious returned will be NULL since it will be the first IBindStatusCallback that we register for this bind context. Once we've registered an IBindStatusCallback with a bind context, it would last until it's revoked by RevokeBindStatusCallback(), displaced by a new one through another RegisterBindStatusCallback() call, or the bind context itself is destroyed.

The IBindStatusCallback pointer should be pointing to an interface that the client (or an object that the client controls) would implement. The IBindStatusCallback interface consists of a total of 11 methods, including the three IUnknown methods. These methods are:

IBindStatusCallBack Method Name Description
QueryInterface() Standard IUnknown requirement. Make sure that a query for ICodeInstall will return the client’s ICodeInstall interface (see below).
AddRef() Standard IUnknown requirement.
Release() Standard IUnknown requirement.
GetBindInfo() Called by the Moniker to get binding information.
OnStartBinding() A chance for the client to tell the Moniker which events/callback the client is interested in. Also provides the client with an IBinding interface for the client to control downloading.
GetPriority() Obtains the priority of this binding operation.
OnProgress() Reports progress to the client.
OnDataAvailable() Reports the incremental availability of data.
OnObjectAvailable() The requested object interface pointer is passed to the client.
OnLowResource() The Moniker reports a low resource condition during the bind.
OnStopBinding() Notification that the binding operation has completed, and returns error code if any.

 

It's safe to return E_NOTIMPL for the GetPriority(), and OnDataAvailable() members in most cases.

If we're supporting automatic installation of a component (i.e. through registry update or an .INF file), we must also implement the ICodeInstall interface. The Moniker will do a QueryInterface() on IBindStatusCallback for ICodeInstall. The ICodeInstall interface has a total of only 5 methods. The client must implement this interface.

ICodeInstall Method Name Description
QueryInterface() Standard IUnknown requirement.
AddRef() Standard IUnknown requirement.
Release() Standard IUnknown requirement.
NeedVerificationUI() Called when the server needs to display a verification window. The client should supply a parent window in this case.
OnCodeInstallProblem() Called when server encounters code installation problem (i.e. out of disk space). The client may attempt to correct the situation by prompting the user accordingly.

By supporting a callback in our bind context for the asynchronous (URL) moniker, we gain a lot of control over the display of status during the binding process, as well as the ability to customize system-displayed UI and handle error conditions during the code installation process.

There exists also an asynchronous moniker specific function, called CreateAsyncBindCtx() which combines the function of RegisterBindStatusCallback() and CreateBindCtx() into one convenient call.

HRESULT CreateAsyncBindCtx( DWORD dwReserved, 
                            IBindStatusCallback *pbsc, 
                            IEnumFORMATETC *penumfmtetc, 
                            IBindCtx **ppbc );

The parameters required are:

Parameter Description
dwReserved Must be zero.
pbsc Pointer to an IBindStatusCallback of a bind status callback object to be registered.
penumfmtetc Pointer to an IEnumFORMATETC of an enumerator object for format negotiation during binding, should be set to NULL for URL moniker binding through the CoGetClassObjectFromURL() call.
ppbc Pointer to hold the IBindCtx pointer for the newly created bind context object.

The possible return values for CreateAsyncBindCtx() are:

Return Value Description
S_OK The operation has completed successfully.
E_INVALIDARG An argument passed was invalid.
E_OUTOFMEMORY The operation can't be performed because memory was exhausted during its execution.

© 1997 by Wrox Press. All rights reserved.