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