IOleCommandTarget, CGID_MSHTML and ActiveX controls

If you write an application that hosts the WebBrowser Control, and you want the control to do something, you can send commands to mshtml via the IOleCommandTarget interface.

However, if you are an ActiveX control and you want to send CGID_MSHTML commands, you may try something like this:

    ...
IOleCommandTarget *pCommandTarget = NULL;
hr = _punkSite->QueryInterface(IID_IOleCommandTarget, (void **)&pCommandTarget);
if (SUCCEEDED(hr))
{
hr = pCommandTarget->Exec(&CGID_MSHTML, IDM_FOO, 0, NULL, NULL);
pCommandTarget->Release();
}
...

If you have tried this, you will see that it fails for CGID_MSHTML commands.  It fails because your control is hosted by an object inside of mshtml.dll that delegates all incoming commands to the nearest DocHostUIHandler.  For IE, that is implemented by an object in shdocvw.dll (or ieframe.dll for IE7+).  That object does not recognize CGID_MSHTML commands.

In order for your call to be routed correctly, you need to get an object "above" the object that is hosting your control.  To do this, you can first ask the client site for an IServiceProvider:

    ...
IServiceProvider *pServiceProvider = NULL;
hr = _punkSite->QueryInterface(IID_IServiceProvider, (void **)&pServiceProvider);
if (SUCCEEDED(hr))
{
IOleCommandTarget *pCommandTarget = NULL;
hr = pServiceProvider->QueryService(SID_SContainerDispatch, IID_IOleCommandTarget, &pCommandTarget);
if (SUCCEEDED(hr))
{
hr = pCommandTarget->Exec(&CGID_MSHTML, IDM_FOO, 0, NULL, NULL);
pCommandTarget->Release();
}
pServiceProvider->Release();
}

By asking for the ContainerDispatch's command target, you get the correct target for MSHTML commands.