The easiest way to implement COM object wrappers that define static properties for custom objects is to use the Active Template Library (ATL). ATL makes it easy to create COM objects that support IDispatch. The most difficult part is integrating the ObjectARX custom object code with the ActiveX Server DLLs that ATL generates. Once the base object is working, however, it is simple to add properties that will appear in the Property Inspector.
See COM and ActiveX Automation, for information on how to create COM wrappers for your custom objects. Consult the Microsoft developer documentation for instructions on using the Microsoft compiler's interface and wizards to add ATL implementation elements to your interfaces.
If you used a wizard for steps 1–3, you should find get_* and put_* function stubs in your implementation file for each read/write property you defined.
STDMETHODIMP CAsdkSquareWrapper::get_SquareSize(double *pVal){ AcAxObjectRefPtr<AsdkSquare> pSq(&m_objRef, AcDb::kForRead); if (pSq.openStatus() != Acad::eOk) return E_ACCESSDENIED; double size; pSq->squareSideLength(size); *pVal = size; return S_OK; }
In the put_* stub that the wizard created, add the appropriate code to set the property value. Here is the put_SquareSize()function from the AsdkSquareWrapper sample:
STDMETHODIMP CAsdkSquareWrapper::put_SquareSize(double newVal) { AcAxDocLock docLock(m_objRef.objectId(), AcAxDocLock::kNormal); if(docLock.lockStatus() != Acad::eOk && docLock.lockStatus() != Acad::eNoDatabase) return E_ACCESSDENIED; AcAxObjectRefPtr<AsdkSquare> pSq(&m_objRef, AcDb::kForWrite); if (pSq.openStatus() != Acad::eOk) return E_ACCESSDENIED; pSq->setSquareSideLength(newVal); Fire_Notification(DISPID_SQUARESIZE); return S_OK; }
Compile and build your application.
To categorize properties using only built-in categories
You may not want all your properties to show up under the General category. This section demonstrates how to use built-in categories. Note that the CAsdkSquareWrapper class inherits ICategorizeProperties through the IOPMPropertyExtensionImpl interface.
public IOPMPropertyExtensionImpl<CAsdkSquareWrapper>, public IOPMPropertyExpander
Add the following interfaces to the end of the COM interface map:
COM_INTERFACE_ENTRY(IOPMPropertyExtension) COM_INTERFACE_ENTRY(ICategorizeProperties) COM_INTERFACE_ENTRY(IPerPropertyBrowsing) COM_INTERFACE_ENTRY(IOPMPropertyExpander)
Add the declaration for the OPM property map to the public declarations of the COM class:
// IOPMPropertyExtension // BEGIN_OPMPROP_MAP() OPMPROP_ENTRY(0, 0x00000001, PROPCAT_Data, \ 0, 0, 0, "", 0, 1, IID_NULL, IID_NULL, "") OPMPROP_ENTRY(0, 0x00000003, PROPCAT_Geometry, \ 0, 0, 0, "", 0, 1, IID_NULL, IID_NULL, "") END_OPMPROP_MAP()
Add the following two public inline functions to the class:
STDMETHOD(GetCategoryName)( THIS_ /* [in] */ PROPCAT propcat, /* [in] */ LCID lcid, /* [out] */ BSTR* pbstrName) {return S_FALSE;} virtual HINSTANCE GetResourceInstance() { return _Module.GetResourceInstance(); }
Add public declarations for the following functions:
STDMETHOD(GetElementValue)( /* [in] */ DISPID dispID, /* [in] */ DWORD dwCookie, /* [out] */ VARIANT * pVarOut) ; // Used for property expansion (currently variant types) // STDMETHOD(SetElementValue)( /* [in] */ DISPID dispID, /* [in] */ DWORD dwCookie, /* [in] */ VARIANT VarIn) ; // Used for property expansion (currently variant types) // STDMETHOD(GetElementStrings)( /* [in] */ DISPID dispID, /* [out] */ OPMLPOLESTR __RPC_FAR *pCaStringsOut, /* [out] */ OPMDWORD __RPC_FAR *pCaCookiesOut) ; //Used for property expansion (currently variant types) // STDMETHOD(GetElementGrouping)( /* [in] */ DISPID dispID, /* [out] */ short *groupingNumber) ; // Used for property expansion (currently variant types) // STDMETHOD(GetGroupCount)( /* [in] */ DISPID dispID, /* [out] */ long *nGroupCnt) ; STDMETHOD(GetPredefinedStrings)( /* [in] */ DISPID dispID, /* [out] */ CALPOLESTR *pCaStringsOut, /* [out] */ CADWORD *pCaCookiesOut); STDMETHOD(GetPredefinedValue)( /* [in] */ DISPID dispID, /* [out] */ DWORD dwCookie, /* [out] */ VARIANT *pVarOut);
Add the implementation for the function in the CPP source file. These examples are for the AsdkSquare object:
STDMETHODIMP CAsdkSquareWrapper::GetElementValue( /* [in] */ DISPID dispID, /* [in] */ DWORD dwCookie, /* [out] */ VARIANT * pVarOut) { if (pVarOut == NULL) return E_POINTER; AcAxObjectRefPtr<AsdkSquare> pSq(&m_objRef, AcDb::kForRead); if (pSq.openStatus() != Acad::eOk) return E_ACCESSDENIED; if (dispID == DISPID_CENTERPOINT) { AcGePoint3d acgePt; pSq->squareCenter(acgePt); AcAxPoint3d acaxPt(acgePt); ::VariantCopy(pVarOut,&CComVariant(acaxPt[dwCookie])); } return S_OK; } STDMETHODIMP CAsdkSquareWrapper::SetElementValue( /* [in] */ DISPID dispID, /* [in] */ DWORD dwCookie, /* [in] */ VARIANT VarIn) { AcAxDocLock docLock(m_objRef.objectId(), AcAxDocLock::kNormal); if(docLock.lockStatus() != Acad::eOk && docLock.lockStatus() != Acad::eNoDatabase) return E_ACCESSDENIED; AcAxObjectRefPtr<AsdkSquare> pSq(&m_objRef, AcDb::kForRead); if (pSq.openStatus() != Acad::eOk) return E_ACCESSDENIED; if (dispID == DISPID_CENTERPOINT) { AcGePoint3d acgePt; pSq->squareCenter(acgePt); AcAxPoint3d acaxPt(acgePt); acaxPt[dwCookie] = V_R8(&VarIn); pSq->upgradeOpen(); pSq->setSquareCenter(acaxPt); Fire_Notification(DISPID_CENTERPOINT); } return S_OK; } STDMETHODIMP CAsdkSquareWrapper::GetElementStrings( /* [in] */ DISPID dispID, /* [out] */ OPMLPOLESTR __RPC_FAR *pCaStringsOut, /* [out] */ OPMDWORD __RPC_FAR *pCaCookiesOut) { if (dispID == DISPID_CENTERPOINT) { long size; size = 3; pCaStringsOut->pElems = (LPOLESTR *)::CoTaskMemAlloc(sizeof(LPOLESTR) * size); pCaCookiesOut->pElems = (DWORD *)::CoTaskMemAlloc(sizeof(DWORD) * size); for (long i=0;i<size;i++) pCaCookiesOut->pElems[i] = i; pCaStringsOut->cElems = size; pCaCookiesOut->cElems = size; pCaStringsOut->pElems[0] = ::SysAllocString(L"Center X"); pCaStringsOut->pElems[1] = ::SysAllocString(L"Center Y"); pCaStringsOut->pElems[2] = ::SysAllocString(L"Center Z"); } return S_OK; } STDMETHODIMP CAsdkSquareWrapper::GetElementGrouping( /* [in] */ DISPID dispID, /* [out] */ short *groupingNumber) { return E_NOTIMPL; } STDMETHODIMP CAsdkSquareWrapper::GetGroupCount( /* [in] */ DISPID dispID, /* [out] */ long *nGroupCnt) { return E_NOTIMPL; } STDMETHODIMP CAsdkSquareWrapper::GetPredefinedStrings( DISPID dispID, CALPOLESTR *pCaStringsOut, CADWORD *pCaCookiesOut) { return E_NOTIMPL; } STDMETHODIMP CAsdkSquareWrapper::GetPredefinedValue( DISPID dispID, DWORD dwCookie, VARIANT *pVarOut) { return E_NOTIMPL; }