The difference between class factory and class object
A class factory is an object that implements the
IClassFactory interface, whereas a class object implements a
custom activation interface instead of, or in addition to
IClassFactory. A custom activation interface must inherit
from IUnknown. To instantiate a coclass via this custom
interface, we would do something like this:
IXXXFactory pXXXFactory;
CoGetClassObject(CLSID_XXX, CLSCTX_SERVER, NULL, IID_IXXXFactory,
(void**)&pXXXFactory);
// invoke methods of IXXXFactory to instantiate a coclass
pXXXFactory->...;
Monikers is an example of objects that cannot be created using the
IClassFactory interface (and therefore using the
CoCreateInstance() function), and must be created with
CoGetClassObject() instead.
Embedding or reusing a WebBrowser control using the
Active Template Library (ATL)
What I intend to demonstrate is possibly the simplest way of
embedding a WebBrowser control using ATL (see screenshot above). ATL has the
advantage of being lightweight, but is (at least for me) difficult to
understand, not to mention master. However by studying the code below,
hopefully you will get a better understanding of ATL and also learn how
to get started. Let us start with the main application loop in file
UtBrowser.cpp.
UtBrowser.cpp:
#include "StdAfx.h"
#include "resource.h"
#include "CUtFrame.h"
CComModule _Module;
/////////////////////////////////////////////////////////////////////////////
//
extern "C" int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE /*hPrevInstance*/, LPTSTR lpCmdLine, int /*nShowCmd*/)
{
CUtFrame frame;
HICON hIcon;
HMENU hMenu;
RECT rcPos = { CW_USEDEFAULT, 0, 0, 0 };
MSG msg;
// _Module.Init(NULL, hInstance) no longer works as of ATL7
_Module.Init(NULL, hInstance, &LIBID_ATLLib);
hIcon = LoadIcon(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDI_ICON));
frame.GetWndClassInfo().m_wc.hIcon = hIcon;
hMenu = LoadMenu(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MENU));
frame.Create(GetDesktopWindow(), rcPos, _T("UtBrowser"), 0, 0, (UINT)hMenu);
frame.ShowWindow(SW_SHOWNORMAL);
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
_Module.Term();
return 0;
}
It is obvious what the code does. It instantes the module, loads icon,
loads the menu resource, shows the main window which is implemented by
CUtFrame, and gets into a typical Windows application loop, before
terminating the module. The code for CUtFrame follows:
CUtFrame.h:
#ifndef __CUtFrame_h__
#define __CUtFrame_h__
#include "resource.h" // main symbols
/////////////////////////////////////////////////////////////////////////////
// CUtFrame - Example of generic ActiveX Hosting window
class CUtFrame : public CWindowImpl<
CUtFrame,
CWindow,
CWinTraits<WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN |
WS_CLIPSIBLINGS, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE> >
{
public:
CAxWindow m_wndBrowserContainer;
IWebBrowser2 *m_pBrowserControl;
DECLARE_WND_CLASS_EX(NULL, 0, 0)
BEGIN_MSG_MAP(CUtFrame)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
COMMAND_ID_HANDLER(ID_FILE_EXIT, OnFileExit)
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_ERASEBKGND, OnErase)
END_MSG_MAP()
void OnFinalMessage(HWND /*hWnd*/)
{
::PostQuitMessage(0);
}
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (m_pBrowserControl != NULL) m_pBrowserControl->Release();
return 0;
}
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
RECT rcClient;
HRESULT hresult;
IUnknown *pUnk;
VARIANT v; //just a tmp VARIANT
AtlAxWinInit();
GetClientRect(&rcClient);
m_wndBrowserContainer.Create(m_hWnd, rcClient, _T(""),
WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_HSCROLL|WS_VSCROLL);
// whenever the first argument is a RESID, a browser control will
// automatically be created:
hresult = m_wndBrowserContainer.CreateControlEx(
ID_WEBBROWSER, NULL, NULL, &pUnk, IID_NULL, NULL);
if (FAILED(hresult)) {
OutputDebugString("CreateControlEx() failed.\n");
} else {
if (pUnk != NULL) {
pUnk->QueryInterface(IID_IWebBrowser2, (void**)&m_pBrowserControl);
pUnk->Release();
if (m_pBrowserControl == NULL) {
OutputDebugString("Failed getting IWebBrowser2 from IUnknown.\n");
return 0;
}
} else {
OutputDebugString("CreateControlEx() succeeded but pUnk is NULL.\n");
}
}
v.vt=VT_I4;
v.lVal=navNoHistory;
m_pBrowserControl->Navigate(L"http://www.google.com", &v, NULL, NULL, NULL);
return 0;
}
LRESULT OnErase(UINT /* uMsg */, WPARAM /* wParam */, LPARAM /* lParam */, BOOL& bHandled)
{
return 1;
}
LRESULT OnSize(UINT /* uMsg */, WPARAM /* wParam */, LPARAM /* lParam */, BOOL& /* lResult */)
{
RECT rcClient;
GetClientRect(&rcClient);
m_wndBrowserContainer.MoveWindow(0,0,rcClient.right,rcClient.bottom,TRUE);
return 0;
}
LRESULT OnFileExit(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
DestroyWindow();
return 0;
}
};
#endif
The big picture is clear and simple: we have to host the WebBrowser
control in a suitable container, and all we have to do is to make
CUtFrame such a container. The function that plays the central
role is AtlAxWinInit(). Next you create an intemediary window
that acts as a container to host the WebBrowser control, by using
function (or method to be precise) CAxWindow.Create().
Finally call CAxWindow.CreateControlEx() on the container to
instantiate the WebBrowser control. This function as a side effect
returns an IUnknown pointer to the WebBrowser control. By
calling QueryInterface on the returned IUnknown
pointer, the pointer to the IWebBrowser2 interface is easily
obtained. The rest of the code is self-explanatory. For completeness,
the file StdAfx.h which is pretty standard is given below, but
the file resource.h and the associated icon and menu resource
can easily be authored yourself. That's it, and please say this is easy
:)
StdAfx.h:
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef STRICT
#define STRICT
#endif
#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN
#endif
//#define _ATL_APARTMENT_THREADED
#include <atlbase.h>
extern CComModule _Module; //necessary
#include <atlcom.h>
#include <atlhost.h>
#include <atlctl.h>
On
casting...
Casting is an essential technique in COM programming. Usually beginners
are confused with these casting operators: static_cast<>(), dynamic_cast<>(),
reinterpret_cast<>(). These operators are intended to remove some of the
ambiguity and danger inherent in old style C language casts:
| dynamic_cast |
Used for conversion of polymorphic types. |
| static_cast |
Used for conversion of nonpolymorphic types. |
| const_cast |
Used to remove the const, volatile, and __unaligned attributes. |
| reinterpret_cast |
Used for simple reinterpretation of bits. |
Both of the expressions static_cast<type-id>(expression),
dynamic_cast<type-id>(expression) convert expression
to the type of type-id. Both mechanisms move a pointer throughout a class hierarchy,
but static_cast is based solely on the types present in the expression -- no
run-time type check is made to ensure the safety of the conversion. dynamic_cast
uses run-time type information to check whether a pointer actually points to
a complete object.
class B { ... };
class D : public B { ... };
void f(B* pb) {
D* pd1 = dynamic_cast<D*>(pb);
D* pd2 = static_cast<D*>(pb);
}
If pb really points to an object of type D, then pd1 and pd2 will get the same
value. They will also get the same value if pb == 0. If pb points to an object
of type B and not to the complete D class, then dynamic_cast will know enough
to return zero. However, static_cast relies on the programmer's assertion that
pb points to an object of type D and simply returns a pointer to that supposed
D object. Consequently, static_cast can do the inverse of implicit conversions,
in which case the results are undefined. It is left to the programmer to ensure
that the results of a static_cast conversion are safe. A fine example of usage
of static_cast is provided by the ATL: there's a macro offsetofclass
defined as:
#define offsetofclass(base, derived) \
((DWORD)(static_cast((derived *)8))-8)
The value of 8 has no significance, it just has to be something apart from 0,
because in C++, casting a null pointer always results in a null pointer. The macro
works by
- Treat the number 8 as a pointer to derived.
- Cast the "fake" derived pointer to a base pointer, by
offsetting the pointer.
- Subtract the original number 8 from the offsetted value.
- Finally cast the result to a DWORD (a 32-bit unsigned integer or
the address of a segment and its associated offset).
The reinterpret_cast operator allows any pointer to be converted into any other
pointer type, e.g. char* to int*, or One_class* to Unrelated_class*, which are
inherently unsafe.
Glossary
- ActiveX
- A set of technologies which uses COM to enable software
components to interact with each other in a networked environment, regardless
of the language in which they are created. ActiveX includes both client and
server technologies, e.g. ActiveX Control, ActiveX Documents, ActiveX
Scripting.
- ActiveX control
-
- An in-process server (typically a small object) that can be used in any
OLE container that supports ActiveX control;
- Interacts with its container via a 2-way communication mechanism: the control
fires events to the container whereas the container modifies the properties
and calls the methods of the control. The Control inherits the event-firing
capability from the COleControl class, and exposes its sets of properties
and methods using a dispatch map;
- Can have 2 modes: windowed and windowless. Windowless mode offers smaller
instance object size and lower creation time.
- ActiveX document
-
- A full-scale, conventional document that is hosted by a container;
- Can have more than one page, has complete control over their pages, and
are always in-place active;
- Also controls part of the user interface, merging their menus with that
of the container, occupies the entire editing area of the container and controls
the views and the layout of the printer page;
- Can exploit the complete native functionality of the server that creates
it.
- COM+
- The name of COM when you combine it with Microsoft Transaction
Server (MTS), and Distributed COM (DCOM). COM+ provides a set of middle-tier-oriented
services. In particular, COM+ provides process management and database and object
connection pooling.
- Connection point
- Something that allows an object to "talk back" to its client(s).
Objects that support connection points are called connectable
objects. Each connection point supports the
IConnectionPoint interface. The connectable object exposes its
connection points to the client through the
IConnectionPointContainer interface.
- Distributed Component Object Model (DCOM)
- A protocol based
on the Open Software Foundation's DCE-RPC specification, that enables COM components
to communicate directly over a network in a reliable, secure, and efficient
manner.
- Microsoft Transaction Server (MTS)
- A transaction service in
the Microsoft Windows NT operating system that simplifies the development and
deployment of server-centric applications built using COM. It's essentially
a TP monitor and an object request broker (ORB) combined into an easy-to-use
package.
- Moniker
- An object that identifies another object. Sometimes also known as
intelligent names. Monikers implement the IMoniker
interface. This interface can be thought of as a kind of factory
interface, the method IMoniker::ParseDisplayName() of which can
be used to create other moniker objects. Note:
IMoniker::ParseDisplayName() might or might not call
IParseDisplayName::ParseDisplayName() depending on the implementation.