Introduction
Hello people, and welcome to this article. It's been a while since I've written one, so if there are bad spelling or other errors, please do notify me so I can fix them.
Background
There are already several articles here on Code Project that show the basic usage of ActiveX controls in MFC programs. If you are unfamiliar on how to add an ActiveX control to your MFC project, I recommend the article Using ActiveX Controls Example: Insert Internet Explorer into your Dialogs by Hazem Nasereddin.
As for the reasons of this article, we will come back to them soon.
The problem of wrapping ActiveX controls
By following the up-mentioned article, you can easily add a control to your program, and utilize its basic functionality. However, many controls also sport other interfaces that offer advanced functionality or settings. Some of these interfaces may be hidden, or be non-creatable, so basic querying of them won't work.
For a ground up example, let's continue on where Mr. Nasereddin left off. We have the web browser control in our dialog, and it's working well. We also have the member variable in our dialog class, and the IDispatch
wrapper class for methods available through IWebBrowser2
and its bases. This interface offers a huge amount of things you can do with the control.
However, after navigating to a certain multi-framed web page, we now have an arbitrary need to find our the names of the frames on this page. There would be two ways to do this: either we use the IWebBrowser2
interface to get the document, and search it through there, or we somehow get access to the ITargetFrame2
interface of the browser.
The problem that arises now is that the wrapper class that was automatically generated for us does not offer a method that could be used for getting the ITargetFrame2
interface. So, how should we proceed?
Getting into the depths of IUnknown
Luckily, the wrapper class is derived from CWnd
base class. This class is designed for holding ActiveX controls, and as such, it happens to provide us with a method called CWnd::GetControlUnknown
. This method gives us a copy of the pointer to the created ActiveX object's IUnknown
interface.
IUnknown* pUnk;
pUnk = m_ctrlBrowser.GetControlUnknown();
Now, the pointer returned by GetControlUnknown
is a copy of a pointer to the IUnknown
. This same pointer is constantly being used by the MFC Framework to upkeep the control on your dialog. Needless to say, if you release this interface pointer, MFC ceases to be able to operate on the control, and if it happens to be that it is the last interface pointer on the object, the object will even shut itself down (self-destroy when all interfaces are released).
So, this interface pointer must NOT be released. When you no longer need the pointer, set it to NULL
and forget about it.
The way up from the deep
So, what all nifty things can we do with this pointer, then? Easy enough: we now have complete access to every interface the object contains, whether they were creatable or not. We are operating on a level that was opened for us by the MFC Framework, and putting it short, we have complete access to the object.
Let's start by accessing the ITargetFrame2
interface. Following basic COM function calls, this happens by issuing the following:
ITargetFrame2* pFrame;
HRESULT hr = pUnk->QueryInterface( __uuidof( ITargetFrame2), (void**) &pFrame );
Hey! What is that __uuidof()
call? This nifty function allows you to search all included modules for the GUID of an interface to which you know the name. Most type libraries (or headers generated from type libraries) grant you a short-cut by defining IID_*
variables to identify the interface GUIDs. But not all of them. This function is for those 'not this time' -situations.
Final steps, conclusion
Here we are. We have a valid, working interface pointer that can be used to wreak havoc on our web browser control. The choices on what to do now are up to you. You can use the ITargetFrame2
interface pointer, or you can query for another interface on the browser control that you need.
The key issues of this article were to bring out the steps required in accessing the IUnknown
interface, and through it, all other interfaces of the object. The biggest advantage of this approach is that the IUnknown
pointer returned is not a newly created pointer, but a copy of an existing one. Thus, all interfaces that the object has become available, whether they were creatable or not. Just remember that all other interface pointers you query from the IUnknown
one should be released normally.
For an example of accessing non-creatable interfaces, add a Microsoft RDP Client Control into your dialog. This control offers, by default, a very limited access to its interfaces, and by following this approach, all of its interfaces are available for us. Even the difficult process of automatically logging on to a Terminal Services server.