Figure 3   Registering AtlAxWin

// 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);
};