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