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