Introduction
This article explains how to extend COM object properties at run time with properties that are not declared in IDL file.
The method explained here is to use custom interface IRtProperties
, this method is only aplicable if you are the owner of both the COM server and the COM client since both need to know of existance of the IRtProperties
interface.
Usage
Server side
Suppose you have some COM object that will generate custom properties at runtime and you have a COM client that you want to recognize those properties and manage them.
First you have to include the header file IRtProperties.h in your COM object's header.
Than you have to derrive your object from this interface:
class ATL_NO_VTABLE CSomeCOMObject :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CSomeCOMObject , &CLSID_SomeCOMObject >,
public IRtProperties
BEGIN_COM_MAP(CMdnBlockCom)
COM_INTERFACE_ENTRY(ISomeCOMObject )
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IRtProperties)
END_COM_MAP()
Next step is to override all of the interface's functions. Here is the list:
STDMETHOD(GetRtPropertiesCount)(THIS_
LONG* pCount) PURE;
STDMETHOD(GetRtPropertyName)(THIS_
LONG propid,
BSTR* pbstrName) PURE;
STDMETHOD(GetRtPropertyCategory)(THIS_
LONG propid,
BSTR* pbstrName,
PROPCAT* ppropcat) PURE;
STDMETHOD(GetRtPropertyType)(THIS_
LONG propid,
VARTYPE* vt) PURE;
STDMETHOD(IsRtPropertyEnabled)(THIS_
LONG propid,
BOOL* isEnabled) PURE;
STDMETHOD(SetRtPropertyValue)(THIS_
LONG propid,
VARIANT newVal) PURE;
STDMETHOD(GetRtPropertyValue)(THIS_
LONG propid,
VARIANT* pVal) PURE;
STDMETHOD(GetRtPredefinedStrings)(THIS_
LONG propid,
CALPOLESTR *pcaStringsOut,
CADWORD *pcaCookiesOut) PURE;
Explanation
Suppose your runtime properies is an array of PROPERTY declared as follows:
struct PROPERTY{
CString name;
CString value;
}
CArray <PROPERTY,PROPERTY> m_myRtProperties;
GetRtPropertiesCount
: returns the total count of runtime properties.
Example
STDMETHODIMP CSomeCOMObject ::GetRtPropertiesCount(
LONG* pCount)
{
*pCount = m_myRtProperties.GetSize();
return S_OK;
}
GetRtPropertyName
: returns property name by index.
Example
STDMETHODIMP CMdnBlockCom::GetRtPropertyName(
LONG propid,
BSTR* pbstrName)
{
*pbstrName=::SysAllocString(m_myRtProperties[propid].name);
return S_OK;
}
Continue to override all of the functions.
Client side
From the client side when you aquire the IDispatch
or IUnknown
interface of the object quiry it for the IRtProperies
inteface:
hr = pUnk->QueryInterface(IID_IRtProperties, (void**)&irtp);
if (FAILED(hr) || irtp == NULL)
return;
When you have the
IRtProperties
interface just use its functions to manipulate the object's runtime properties.
Example
long propCount;
irtp->GetRtPropertiesCount(&propCount);
CComBSTR bstrName, bstrCategory;
PROPCAT catId;
for (int i = 0; i < propCount; i++)
{
irtp->GetRtPropertyName(i,&bstrName);
irtp->GetRtPropertyCategory(i,&bstrCategory,&catId);
VARTYPE vt;
irtp->GetRtPropertyType(i,&vt);
if (vt == VT_ARRAY)
{
CALPOLESTR castr;
CADWORD cadw;
hr = irtp->GetRtPredefinedStrings(i,&castr,&cadw);
if (hr == S_OK)
{
for (ULONG j = 0; j < castr.cElems; j++)
{
CString szVal(castr.pElems[j]);
int index = otiCombo->AddString(szVal);
otiCombo->CComboBox::SetItemData(j,cadw.pElems[j]);
}
CoTaskMemFree((void *)cadw.pElems);
CoTaskMemFree((void *)castr.pElems);
}
}
BOOL bReadOnly;
irtp->IsRtPropertyEnabled(i,&bReadOnly);
}
if (irtp)
irtp->Release();