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. |
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. |