Click to return to the Component Development home page    
Web Workshop  |  Component Development

Design-time Control Tutorial


Jay Massena and Douglas Hodges

Updated: February 12, 1997

Summary

This document is a tutorial that covers what it takes to make an ActiveX™ control a design-time control. It also provides some assistance for HTML editors that want to host design-time controls.

Bgsound Code Fragments

These code fragments illustrate what it takes to turn an MFC control wizard-generated ActiveX control into a design-time control.

IActiveDesigner Interface Declaration

/////////////////////////////////////////////////////////////////////////////
// IActiveDesigner Interface Declaration
//
// {51AAE3E0-7486-11cf-A0C2-00AA0062BE57}
/////////////////////////////////////////////////////////////////////////////
DEFINE_GUID(IID_IActiveDesigner, 0x51aae3e0, 0x7486, 0x11cf, 0xa0, 0xc2, 0x0, 0xaa, 0x0, 0x62, 0xbe, 0x57);

#undef  INTERFACE
#define INTERFACE IActiveDesigner

DECLARE_INTERFACE_(IActiveDesigner, IUnknown)
{
   // IUnknown methods
   //
   STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
   STDMETHOD_(ULONG,AddRef)(THIS) PURE;
   STDMETHOD_(ULONG,Release)(THIS) PURE;

   // IActiveDesigner methods
   //
   STDMETHOD(GetRuntimeClassID)(THIS_ CLSID *pclsid) PURE;
   STDMETHOD(GetRuntimeMiscStatusFlags)(THIS_ DWORD *dwMiscFlags) PURE;
   STDMETHOD(QueryPersistenceInterface)(THIS_ REFIID riidPersist) PURE;
   STDMETHOD(SaveRuntimeState)(THIS_ REFIID riidPersist, REFIID riidObjStgMed, void *pObjStgMed) PURE;
   STDMETHOD(GetExtensibilityObject)(THIS_ IDispatch **ppvObjOut) PURE;
};

const GUID IID_IActiveDesigner = 
      { 0x51aae3e0, 0x7486, 0x11cf, { 0xa0, 0xc2, 0x0, 0xaa, 0x0, 0x62, 0xbe, 0x57 } };
const GUID IID_IPersistTextStream = 
   {0x56223fe3, 0xd397, 0x11cf, { 0xa4, 0x2e, 0x0, 0xaa, 0x0, 0xc0, 0x9, 0x40 } };

Bgsound Class Definition

/////////////////////////////////////////////////////////////////////////////
// CBGSndCtrl class
//    This class implements the BackgroundSound Designer control.
//    This class implements the standard OLE control interfaces by 
//    deriving them from MFC's COleControl class.  This class adds the 
//    implementation of the IActiveDesigner interface to act as an
//    HTML Designer control.
/////////////////////////////////////////////////////////////////////////////
class CBGSndCtrl : public COleControl
{
   DECLARE_DYNCREATE(CBGSndCtrl)

// Constructor
public:
   CBGSndCtrl();

// Overrides
   // ClassWizard generated virtual function overrides
   //{{AFX_VIRTUAL(CBGSndCtrl)
   public:
   virtual void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid);
   virtual void DoPropExchange(CPropExchange* pPX);
   virtual void OnResetState();
   virtual DWORD GetControlFlags();
   //}}AFX_VIRTUAL

// Implementation
protected:
   HBITMAP m_hBitmap;
   
    // control properties
    short m_nLoop;
    CString m_strSource;

    // destruction
   ~CBGSndCtrl();

    virtual HRESULT GetRuntimeText(CString& szHTML);

   DECLARE_OLECREATE_EX(CBGSndCtrl)  // Class factory and guid
   DECLARE_OLETYPELIB(CBGSndCtrl)    // GetTypeInfo
   DECLARE_OLECTLTYPE(CBGSndCtrl) // Type name and misc status

// Message maps
   //{{AFX_MSG(CBGSndCtrl)
      // NOTE - ClassWizard will add and remove member functions here.
      //    DO NOT EDIT what you see in these blocks of generated code !
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()

// Dispatch maps
   //{{AFX_DISPATCH(CBGSndCtrl)
   afx_msg short GetLoop();
   afx_msg void SetLoop(short nNewValue);
   afx_msg BSTR GetSource();
   afx_msg void SetSource(LPCTSTR lpszNewValue);
    virtual BOOL OnSetExtent(LPSIZEL lpSizeL);
   //}}AFX_DISPATCH
   DECLARE_DISPATCH_MAP()

   afx_msg void AboutBox();

// Event maps
   //{{AFX_EVENT(CBGSndCtrl)
   //}}AFX_EVENT
   DECLARE_EVENT_MAP()

// Interface maps
   DECLARE_INTERFACE_MAP();


public:
//$$$$$$$$$$$$$$$$$$$$$$$DESIGNER SPECIFIC$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 
    BEGIN_INTERFACE_PART(ActiveDesigner, IActiveDesigner)
      STDMETHOD(GetRuntimeClassID)(CLSID *pclsid);
      STDMETHOD(GetRuntimeMiscStatusFlags)(DWORD *dwMiscFlags);
      STDMETHOD(QueryPersistenceInterface)(REFIID riidPersist);
      STDMETHOD(SaveRuntimeState)(REFIID riidPersist, REFIID riidObjStgMed, void *pObjStgMed);
      STDMETHOD(GetExtensibilityObject)(IDispatch **ppvObjOut);
   END_INTERFACE_PART(ActiveDesigner)
//$$$$$$$$$$$$$$$$$$$$$END DESIGNER SPECIFIC$$$$$$$$$$$$$$$$$$$$$$$$$$

// Dispatch and event IDs 
    enum {
   //{{AFX_DISP_ID(CBGSndCtrl)
   dispidLoop = 1L,
   dispidSource = 2L,
   //}}AFX_DISP_ID
   };
};

IActiveDesigner Interface Implementation

STDMETHODIMP_(ULONG) CBGSndCtrl::XActiveDesigner::AddRef(void)
{
   METHOD_MANAGE_STATE(CBGSndCtrl, ActiveDesigner)
   return (ULONG)pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) CBGSndCtrl::XActiveDesigner::Release(void)
{
   METHOD_MANAGE_STATE(CBGSndCtrl, ActiveDesigner)
   return (ULONG)pThis->ExternalRelease();
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::QueryInterface(REFIID iid,
   LPVOID far* ppvObj)
{
   METHOD_MANAGE_STATE(CBGSndCtrl, ActiveDesigner)
   return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::GetRuntimeClassID(CLSID *pclsid)
{
    *pclsid = CLSID_NULL;
    return S_FALSE;
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::GetRuntimeMiscStatusFlags(DWORD    *pdwMiscFlags)
{
   if (!pdwMiscFlags)
      return E_INVALIDARG;
   *pdwMiscFlags = NULL;
   return E_UNEXPECTED;
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::QueryPersistenceInterface(REFIID riid)
{
   if (riid == IID_IPersistHTMLStream)
      return S_OK;
   return S_FALSE;
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::SaveRuntimeState
(
   REFIID riidPersist,
   REFIID riidObjStgMed,
   void  *pObjStgMed
)
{
   HRESULT  hr;
   CString  cstrText;
   BSTR     bstrText;

   METHOD_MANAGE_STATE(CBGSndCtrl, ActiveDesigner)

   if (riidPersist != IID_IPersistTextStream)
      return E_NOINTERFACE;

   if (riidObjStgMed != IID_IStream)
      return E_NOINTERFACE;

   hr = pThis->GetRuntimeText(cstrText);
   if (SUCCEEDED(hr))
   {
      bstrHtml = cstrHtml.AllocSysString();
      if (bstrText)
      {
         hr = ((IStream *)pObjStgMed)->Write(bstrText, 
            SysStringByteLen(bstrText) + sizeof(OLECHAR), NULL);
         SysFreeString(bstrText);
      }
      else
         hr = E_OUTOFMEMORY;
   }

   return hr;
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::GetExtensibilityObject(IDispatch    **ppvObjOut)
{
   if (!ppvObjOut)
      return E_INVALIDARG;
   return this->QueryInterface(IID_IDispatch, (void **)ppvObjOut);
}


GetRuntimeText Helper Function

/////////////////////////////////////////////////////////////////////////////
// CBGSndCtrl::GetRuntimeText - Generates the run-time text for the control
//
// GetRuntimeText() is called whenever the container needs to update the
// run-time text after property changes or other state changes are made.

HRESULT CBGSndCtrl::GetRuntimeText(CString& strText)
{
    // our run-time Text template
   CString strTemplate = _T("<BGSOUND SRC=\"%s\" LOOP=%d>");

    // format our string with our properties
   strText.Format(strTemplate, m_strSource, m_nLoop);

   return S_OK;
}

Tagging a Web Design-time Control with CATID

DEFINE_GUID(CATID_WebDesigntimeControl, 0x73cef3dd, 0xae85, 0x11cf, 0xa4, 0x6, 0x0,  0xaa, 0x0, 0xc0, 0x9, 0x40);

////////////////////////////////////////////////////////////////////////////
// HandleComponentRegisration - registers the controls in this libary as
//   design-time controls.
HRESULT HandleComponentRegistration(BOOL fRegister)
{
   if (fRegister)
      return RegisterControlCategory(CATID_WebDesigntimeControl,
                                      L"Web Design-time Control",
                                      (REFCLSID)CBGSndCtrl::guid);
   else
      return UnRegisterControlCategory(CATID_WebDesigntimeControl, 
                                             (REFCLSID)CBGSndCtrl::guid);
}

/////////////////////////////////////////////////////////////////////////////
// RegisterControlCategory : Register controls with catid

HRESULT RegisterControlCategory(CATID catid, WCHAR* wszDesc, REFCLSID rClsid)
{
    ICatRegister* pcr = NULL ;
    HRESULT hr = S_OK ;

    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
                          NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister,
                                 (void**)&pcr);
    if (FAILED(hr))
        return hr;

    hr = CreateComponentCategory(pcr, catid, wszDesc);
    if (SUCCEEDED(hr))
   {
      CATID rgcatid[1] ;
      rgcatid[0] = catid;

      hr = pcr->RegisterClassImplCategories(rClsid, 1, rgcatid);
   }

    pcr->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////////////////
// UnRegisterControlCategory : Unregister controls with catid

HRESULT UnRegisterControlCategory(CATID catid, REFCLSID rClsid)
{
    ICatRegister* pcr = NULL ;
    HRESULT hr = S_OK ;

    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
                          NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister,
                                 (void**)&pcr);
    if (FAILED(hr))
        return hr;

   CATID rgcatid[1] ;
   rgcatid[0] = catid;

   hr = pcr->UnRegisterClassImplCategories(rClsid, 1, rgcatid);

    pcr->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////////////////
// CreateComponentCategory : Create a COM Server Category

HRESULT CreateComponentCategory(ICatRegister* pcr, CATID catid, WCHAR* wszCatDescription)
{
    HRESULT hr = S_OK ;

    // Make sure the HKCR\Component Categories\{..catid...} key is registered
    CATEGORYINFO catinfo;
    catinfo.catid = catid;
    catinfo.lcid = 0x0409 ; // english

    // Make sure the provided description is not too long.
    // Only copy the first 127 characters if it is
    int len = wcslen(wszCatDescription);
    if (len>127)
        len = 127;
    wcsncpy(catinfo.szDescription, wszCatDescription, len);
    // Make sure the description is null terminated
    catinfo.szDescription[len] = '\0';

    hr = pcr->RegisterCategories(1, &catinfo);

    return hr;
}

Hosting Code Fragments

These code fragments show how a hosting container can perform some common operations associated with design-time controls. We assume that the container already has containership support for ActiveX™ controls.

Persisting a Web Design-time Control

/////////////////////////////////////////////////////////////////////////////
// The following sample code fragment demonstrates how the container saves the 
// run-time text of a design-time control:

HRESULT SaveRuntimeText( ... IUnknown* pUnk ... )
{
   IActiveDesigner *pActiveDesigner;
   HGLOBAL         hGlobal = NULL;
   IStream         *pStream = NULL;
   OLECHAR         *pstr;
   HRESULT         hr;

   hr = pUnk->QueryInterface( IID_IActiveDesigner, (void**)&pActiveDesigner );
   if ( SUCCEEDED(hr) && pActiveDesigner )
   {
      hr = pActiveDesigner->QueryPersistenceInterface( IID_IPersistTextStream );
      if ( SUCCEEDED(hr) )
      {
         hGlobal = GlobalAlloc( GMEM_MOVEABLE | GMEM_NODISCARD, 0 );
         if ( hGlobal )
         {
            hr = CreateStreamOnHGlobal( hGlobal, FALSE, &pStream );
            if ( SUCCEEDED(hr) )
            {
            hr = pActiveDesigner->SaveRuntimeState( IID_IPersistTextStream,
                 IID_IStream, pStream );
            if ( SUCCEEDED(hr) )
            {
               OLECHAR chZero = 0;
                  ULONG   cbWritten;

                      // Make sure the text has a null terminator
                      pStream->Write(&chZero, sizeof(chZero), &cbWritten);

                      pstr = (OLECHAR *)GlobalLock( hGlobal );
                      if ( pstr )
                      {
                         TCHAR      *pch;

                         pch = MyOLE2T( pstr );

                         //... save out run-time text  ...

                         GlobalUnlock( hGlobal );
                      }
                }
                pStream->Release();
            }
            GlobalFree( hGlobal );
         }
      }
      pActiveDesigner->Release();
   }
   return NOERROR;
}



Back to topBack to top

Did you find this material useful? Gripes? Compliments? Suggestions for other articles? Write us!

© 1999 Microsoft Corporation. All rights reserved. Terms of use.