Web Design-time Control Tutorial
Jay Massena and Douglas Hodges

October 6, 1996

Contents

BGSound code fragments
   IActiveDesigner interface declaration
   BGSound class definition
   IActiveDesigner interface implementation
   GetRuntimeText helper function
   Tagging a Web design-time control with CATID
Hosting code fragments
   Persisting a Web design-time control

Summary

This document is a tutorial that covers how to take an ActiveX control and make it a Web design-time control. It also provides some assistance for HTML editors who want to host Web design-time controls. For our Beta 1 release, we will only give a brief description along with the code fragments for the BGSound Web design-time control that is included in the SDK.

BGSound code fragments

These code fragments illustrate what it takes to turn an MFC control wizard generated ActiveX control into a Web 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 } };


UpBack to contents

BGSound class definition



/////////////////////////////////////////////////////////////////////////////
// CBGSndCtrl class
//    this class implements the BackgroundSound Designer control
//    this class implements the standard OLE control interfaces by 
//    deriving 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
   };
};


UpBack to contents

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


UpBack to contents

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

UpBack to contents

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

UpBack to contents

Hosting code fragments

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

UpBack to contents

Persisting a Web design-time control


/////////////////////////////////////////////////////////////////////////////
// The following sample code fragment demonstrates how the container saves the 
// run-time text of a web 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;
}

UpBack to contents © 1997 Microsoft Corporation. All rights reserved. Legal Notices.