// This either registers a global class (if AtlAxWinInit is in ATL.DLL)
// or it registers a local class
ATLINLINE ATLAPI_(BOOL) AtlAxWinInit() {
EnterCriticalSection(&_Module.m_csWindowCreate);
WM_ATLGETHOST = RegisterWindowMessage(_T("WM_ATLGETHOST"));
WM_ATLGETCONTROL = RegisterWindowMessage(_T("WM_ATLGETCONTROL"));
WNDCLASSEX wc;
// first check if the class is already registered
wc.cbSize = sizeof(WNDCLASSEX);
BOOL bRet = ::GetClassInfoEx(_Module.GetModuleInstance(),
CAxWindow::GetWndClassName(), &wc);
// register class if not
if(!bRet) {
wc.cbSize = sizeof(WNDCLASSEX);
#ifdef _ATL_DLL_IMPL
wc.style = CS_GLOBALCLASS;
#else
wc.style = 0;
#endif
wc.lpfnWndProc = AtlAxWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = _Module.GetModuleInstance();
wc.hIcon = NULL;
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = CAxWindow::GetWndClassName(); // "AtlAxWin"
wc.hIconSm = NULL;
bRet = (BOOL)::RegisterClassEx(&wc);
}
LeaveCriticalSection(&_Module.m_csWindowCreate);
return bRet;
}
Figure 4 AtlAxWindowProc
static LRESULT CALLBACK
AtlAxWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch(uMsg) {
case WM_CREATE: {
// create control from a PROGID in the title
// This is to make sure drag drop works
::OleInitialize(NULL);
CREATESTRUCT* lpCreate = (CREATESTRUCT*)lParam;
int nLen = ::GetWindowTextLength(hWnd);
LPTSTR lpstrName = (LPTSTR)_alloca((nLen + 1) * sizeof(TCHAR));
// Extract window text to be used as name of control to host
::GetWindowText(hWnd, lpstrName, nLen + 1);
::SetWindowText(hWnd, _T(""));
IAxWinHostWindow* pAxWindow = NULL;
•
•
•
USES_CONVERSION;
CComPtr<IUnknown> spUnk;
// Create CAxHostWindow instance and host the control
HRESULT hRet = AtlAxCreateControl(T2COLE(lpstrName), hWnd, spStream,
&spUnk);
if(FAILED(hRet)) return -1; // abort window creation
hRet = spUnk->QueryInterface(IID_IAxWinHostWindow, (void**)&pAxWindow);
if(FAILED(hRet)) return -1; // abort window creation
// Keep a CAxHostWindow interface in the window's user data
::SetWindowLong(hWnd, GWL_USERDATA, (DWORD)pAxWindow);
•
•
•
// continue with DefWindowProc
}
break;
case WM_NCDESTROY: {
IAxWinHostWindow*
pAxWindow = (IAxWinHostWindow*)::GetWindowLong(hWnd, GWL_USERDATA);
// When window goes away, release the host (and the control)
if(pAxWindow != NULL) pAxWindow->Release();
OleUninitialize();
}
break;
}
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Figure 5 CreateControlEx
STDMETHODIMP CAxHostWindow::CreateControlEx(
LPCOLESTR lpszTricsData,
HWND hWnd,
IStream* pStream,
IUnknown** ppUnk,
REFIID iidAdvise,
IUnknown* punkSink)
{
HRESULT hr = S_FALSE;
// Release previously held control
ReleaseAll();
•
•
•
if (::IsWindow(hWnd)) {
// Route all messages to CAxHostWindow object
SubclassWindow(hWnd);
•
•
•
bool bWasHTML;
// Create control based on lpszTricsData
hr = CreateNormalizedObject(lpszTricsData, IID_IUnknown, (void**)ppUnk,
bWasHTML);
bool bInited = hr == S_FALSE;
// Activate the control.
if (SUCCEEDED(hr)) hr = ActivateAx(*ppUnk, bInited, pStream);
// Try to hook up any sink the user might have given us.
m_iidSink = iidAdvise;
if(SUCCEEDED(hr) && *ppUnk && punkSink)
AtlAdvise(*ppUnk, punkSink, m_iidSink, &m_dwAdviseSink);
// If raw HTML, give HTML control its HTML
•
•
•
// If it's a URL, navigate the Browser control to the URL
•
•
•
if (FAILED(hr) || m_spUnknown == NULL) {
// We don't have a control or something failed so release
ReleaseAll();
•
•
•
}
}
return hr;
}
Figure 6
String Formats Understood by
CreateNormalizedObject
Type |
Examples |
CLSID of Created Object |
HTML |
mshtml: |
CLSID_HTMLDocument |
CLSID |
{7DC59CC5-36C0-11D2-AC05-00A0C9C8E50D} |
Result of CLSIDFromString |
ProgID |
ATLInternals.BullsEye |
Result of CLSIDFromProgID |
URL |
http://www.awl.com res://htmlapp.exe/main.htm |
CLSID_WebBrowser |
Active Document |
D:\Atl Internals\11 Control Containment.doc file://D:\Atl Internals\10 Controls.doc |
CLSID_WebBrowser |
Figure 7 CreateControl and CreateControlEx
template <class TBase = CWindow> class CAxWindowT : public TBase {
public:
•
•
•
HRESULT CreateControl(LPCOLESTR lpszName, IStream* pStream = NULL,
IUnknown** ppUnkContainer = NULL)
{ ATLASSERT(::IsWindow(m_hWnd));
return AtlAxCreateControl(lpszName, m_hWnd, pStream, ppUnkContainer);
}
HRESULT CreateControl(DWORD dwResID, IStream* pStream = NULL,
IUnknown** ppUnkContainer = NULL)
{ // bstrURL == URL of the form "res://<module>/<dwResID>"
ATLASSERT(::IsWindow(m_hWnd));
return AtlAxCreateControl(bstrURL, m_hWnd, pStream, ppUnkContainer);
}
HRESULT CreateControlEx(LPCOLESTR lpszName, IStream* pStream = NULL,
IUnknown** ppUnkContainer = NULL,
IUnknown** ppUnkControl = NULL,
REFIID iidSink = IID_NULL,
IUnknown* punkSink = NULL)
{ ATLASSERT(::IsWindow(m_hWnd));
return AtlAxCreateControlEx(lpszName, m_hWnd, pStream, ppUnkContainer,
ppUnkControl, iidSink, punkSink);
}
HRESULT CreateControlEx(DWORD dwResID, IStream* pStream = NULL,
IUnknown** ppUnkContainer = NULL,
IUnknown** ppUnkControl = NULL,
REFIID iidSink = IID_NULL,
IUnknown* punkSink = NULL)
{ // bstrURL == URL of the form "res://<module>/<dwResID>"
ATLASSERT(::IsWindow(m_hWnd));
return AtlAxCreateControlEx(bstrURL, m_hWnd, pStream, ppUnkContainer,
ppUnkControl, iidSink, punkSink);
}
•
•
•
};
Figure 8 Handling BullsEye Control Events
const UINT ID_BULLSEYE = 1;
class CMainWindow :
public CWindowImpl<CMainWindow, CWindow, CMainWindowTraits>,
public IDispEventImpl<ID_BULLSEYE,
CMainWindow,
&DIID__IBullsEyeEvents,
&LIBID_BullsEyeLib, 1, 0>
{
public:
•
•
•
LRESULT OnCreate(...) {
RECT rect; GetClientRect(&rect);
m_ax.Create(m_hWnd, rect, __T("AtlInternals.BullsEye"),
WS_CHILD | WS_VISIBLE, 0, ID_BULLSEYE);
•
•
•
return (m_ax.m_hWnd ? 0 : -1);
}
BEGIN_SINK_MAP(CMainWindow)
SINK_ENTRY_EX(ID_BULLSEYE, DIID__IBullsEyeEvents, 1, OnRingHit)
SINK_ENTRY_EX(ID_BULLSEYE, DIID__IBullsEyeEvents, 2, OnScoreChanged)
END_SINK_MAP()
void __stdcall OnRingHit(short nRingNumber);
void __stdcall OnScoreChanged(LONG ringValue);
private:
CAxWindow m_ax;
};
Figure 9 CAxHostWindow's IAxWinAmbientDispatch
interface IAxWinAmbientDispatch : IDispatch {
[propput]
HRESULT AllowWindowlessActivation([in]VARIANT_BOOL b);
[propget]
HRESULT AllowWindowlessActivation([out,retval]VARIANT_BOOL* pb);
// DISPID_AMBIENT_BACKCOLOR
[propput, id(DISPID_AMBIENT_BACKCOLOR)]
HRESULT BackColor([in]OLE_COLOR clrBackground);
[propget, id(DISPID_AMBIENT_BACKCOLOR)]
HRESULT BackColor([out,retval]OLE_COLOR* pclrBackground);
// DISPID_AMBIENT_FORECOLOR
[propput, id(DISPID_AMBIENT_FORECOLOR)]
HRESULT ForeColor([in]OLE_COLOR clrForeground);
[propget, id(DISPID_AMBIENT_FORECOLOR)]
HRESULT ForeColor([out,retval]OLE_COLOR* pclrForeground);
// DISPID_AMBIENT_LOCALEID
[propput, id(DISPID_AMBIENT_LOCALEID)]
HRESULT LocaleID([in]LCID lcidLocaleID);
[propget, id(DISPID_AMBIENT_LOCALEID)]
HRESULT LocaleID([out,retval]LCID* plcidLocaleID);
// DISPID_AMBIENT_USERMODE
[propput, id(DISPID_AMBIENT_USERMODE)]
HRESULT UserMode([in]VARIANT_BOOL bUserMode);
[propget, id(DISPID_AMBIENT_USERMODE)]
HRESULT UserMode([out,retval]VARIANT_BOOL* pbUserMode);
// DISPID_AMBIENT_DISPLAYASDEFAULT
[propput, id(DISPID_AMBIENT_DISPLAYASDEFAULT)]
HRESULT DisplayAsDefault([in]VARIANT_BOOL bDisplayAsDefault);
[propget, id(DISPID_AMBIENT_DISPLAYASDEFAULT)]
HRESULT DisplayAsDefault([out,retval]VARIANT_BOOL* pbDisplayAsDefault);
// DISPID_AMBIENT_FONT
[propput, id(DISPID_AMBIENT_FONT)]
HRESULT Font([in]IFontDisp* pFont);
[propget, id(DISPID_AMBIENT_FONT)]
HRESULT Font([out,retval]IFontDisp** pFont);
// DISPID_AMBIENT_MESSAGEREFLECT
[propput, id(DISPID_AMBIENT_MESSAGEREFLECT)]
HRESULT MessageReflect([in]VARIANT_BOOL bMsgReflect);
[propget, id(DISPID_AMBIENT_MESSAGEREFLECT)]
HRESULT MessageReflect([out,retval]VARIANT_BOOL* pbMsgReflect);
// DISPID_AMBIENT_SHOWGRABHANDLES
[propget, id(DISPID_AMBIENT_SHOWGRABHANDLES)]
HRESULT ShowGrabHandles(VARIANT_BOOL* pbShowGrabHandles);
// DISPID_AMBIENT_SHOWHATCHING
[propget, id(DISPID_AMBIENT_SHOWHATCHING)]
HRESULT ShowHatching(VARIANT_BOOL* pbShowHatching);
// IDocHostUIHandler Defaults
•
•
•
};
Figure 10 Persisting Control State
bool CMainWindow::Save(LPCOLESTR pszFileName) {
// Make sure object can be saved
// Note: Our IPersistStream interface pointer could end up holding an
// IPersistStreamInit interface. This is OK since IPersistStream
// is a layout-compatible subset of IPersistStreamInit.
CComQIPtr<IPersistStream> spPersistStream;
HRESULT hr = m_ax.QueryControl(&spPersistStream);
if( FAILED(hr) ) {
hr = m_ax.QueryControl(IID_IPersistStreamInit, (void**)&spPersistStream);
if( FAILED(hr) ) return false;
}
// Save object to stream in a storage
CComPtr<IStorage> spStorage;
hr = StgCreateDocfile(pszFileName,
STGM_DIRECT | STGM_WRITE |
STGM_SHARE_EXCLUSIVE | STGM_CREATE,
0, &spStorage);
if( SUCCEEDED(hr) ) {
CComPtr<IStream> spStream;
hr = spStorage->CreateStream(OLESTR("Contents"),
STGM_DIRECT | STGM_WRITE |
STGM_SHARE_EXCLUSIVE | STGM_CREATE,
0, 0, &spStream);
if( SUCCEEDED(hr) ) {
// Get and store the CLSID
CLSID clsid;
hr = spPersistStream->GetClassID(&clsid);
if( SUCCEEDED(hr) ) {
hr = spStream->Write(&clsid, sizeof(clsid), 0);
// Save the object
hr = spPersistStream->Save(spStream, TRUE);
}
}
}
if( FAILED(hr) ) return false;
return true;
}
Figure 11 Restoring a Persisted Control
bool CMainWindow::Open(LPCOLESTR pszFileName) {
// Open object a stream in the storage
CComPtr<IStorage> spStorage;
CComPtr<IStream> spStream;
HRESULT hr;
hr = StgOpenStorage(pszFileName, 0,
STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE,
0, 0, &spStorage);
if( SUCCEEDED(hr) ) {
hr = spStorage->OpenStream(OLESTR("Contents"), 0,
STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE,
0, &spStream);
}
if( FAILED(hr) ) return false;
// Read a CLSID from the stream
CLSID clsid;
hr = spStream->Read(&clsid, sizeof(clsid), 0);
if( FAILED(hr) ) return false;
RECT rect; GetClientRect(&rect);
OLECHAR szClsid[40]; StringFromGUID2(clsid, szClsid, lengthof(szClsid));
// Create the control's host window
if( !m_ax.Create(m_hWnd, rect, 0, WS_CHILD | WS_VISIBLE, 0,
ID_CHILD_CONTROL) {
return false;
}
// Create the control, persisting from the stream
hr = m_ax.CreateControl(szClsid, spStream);
if( FAILED(hr) ) return false;
return true;
}
Figure 15 CAxDialogImpl
template <class T, class TBase = CWindow>
class ATL_NO_VTABLE CAxDialogImpl : public CDialogImplBaseT< TBase > {
public:
// modal dialogs
int DoModal(HWND hWndParent = ::GetActiveWindow(),
LPARAM dwInitParam = NULL) {
_Module.AddCreateWndData(&m_thunk.cd, (CDialogImplBaseT< TBase >*)this);
return AtlAxDialogBox(_Module.GetResourceInstance(),
MAKEINTRESOURCE(T::IDD), hWndParent,
(DLGPROC)T::StartDialogProc, dwInitParam);
}
BOOL EndDialog(int nRetCode)
{ return ::EndDialog(m_hWnd, nRetCode); }
// modeless dialogs
HWND Create(HWND hWndParent, LPARAM dwInitParam = NULL) {
_Module.AddCreateWndData(&m_thunk.cd, (CDialogImplBaseT< TBase >*)this);
HWND hWnd = AtlAxCreateDialog(_Module.GetResourceInstance(),
MAKEINTRESOURCE(T::IDD), hWndParent,
(DLGPROC)T::StartDialogProc, dwInitParam);
return hWnd;
}
// for CComControl
HWND Create(HWND hWndParent, RECT&, LPARAM dwInitParam = NULL)
{ return Create(hWndParent, dwInitParam); }
BOOL DestroyWindow()
{ return ::DestroyWindow(m_hWnd); }
};
Figure 16 Hosting BullsEye in a Dialog
class CBullsEyeDlg :
public CAxDialogImpl<CBullsEyeDlg>,
public IDispEventImpl<IDC_BULLSEYE, CBullsEyeDlg> {
public:
BEGIN_MSG_MAP(CBullsEyeDlg)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
END_MSG_MAP()
BEGIN_SINK_MAP(CBullsEyeDlg)
SINK_ENTRY(IDC_BULLSEYE, 0x2, OnScoreChanged)
END_SINK_MAP()
// Map this class to a specific dialog resource
enum { IDD = IDD_BULLSEYE };
// Hook up connection points
LRESULT OnInitDialog(...)
{ AtlAdviseSinkMap(this, true); return 0; }
// Tear down connection points
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{ AtlAdviseSinkMap(this, false); return 0; }
// Window control event handlers
LRESULT OnCancel(WORD, UINT, HWND, BOOL&);
// COM control event handlers
VOID __stdcall OnScoreChanged(LONG ringValue);
};