<?xml encoding=”UTF-8″>By Balaji Ramamoorthy
When implementing an AutoCAD plugin, its quite easy to retrieve entity properties without having to directly deal with the entity’s COM wrapper. But when implementing a RealDWG host application, this can become necessary to retrieve some of the entity properties.
If you are using .Net languages, using reflection can come very handy. Here is a blog post that can help :
Get ActiveX/COM class properties and methods from .NET
If you are using C++, here are the changes to the DumpDwg sample from the RealDWG SDK. In this code snippet, our objective is to retrieve properties of a custom entity using the IDispatch interface of its COM wrapper. For example, a drawing that includes a “AsdkPoly” entity from “ObjectARX 2015samplesentitypolysampPolySamp”.
<span>// Info on each IDispatch member item</span><span> </span>
<span>struct</span><span> stringdispid</span>
<span>{</span>
CComBSTR bstr;
<span>int</span><span> nLen;</span>
DISPID id;
<span>}</span>;
<span>// DispId map to retrieve properties</span><span> </span>
<span>static</span><span> stringdispid* m_pMap;</span>
<span>static</span><span> <span>int</span><span> m_nMapLen;</span></span>
<span>static</span><span> <span>int</span><span> m_nCount;</span></span>
<span>// Helper method to convert VARIANT to ads_point</span><span> </span>
<span>static</span><span> HRESULT get_PointFromVariant(</span>
VARIANT &variant,
ads_point &pt)
<span>{</span>
<span>if</span><span> (V_VT(&variant) != VT_EMPTY) </span>
<span>{</span>
<span>if</span><span> (V_VT(&variant) == (VT_ARRAY | VT_R8))</span>
<span>{</span>
SAFEARRAY *psa = variant.parray;
<span>long</span><span> lStartIndex = 0;</span>
<span>long</span><span> lEndIndex = 0;</span>
SafeArrayGetLBound(psa, 1, &lStartIndex);
SafeArrayGetUBound(psa, 1, &lEndIndex);
<span>if</span><span> (lEndIndex == 2)</span>
<span>{</span>
AcAxPoint3d tmpPt(variant);
pt[X] = tmpPt[X];
pt[Y] = tmpPt[Y];
pt[Z] = tmpPt[Z];
<span>}</span>
<span>if</span><span> (lEndIndex == 1)</span>
<span>{</span>
AcAxPoint2d tmpPt(variant);
pt[X] = tmpPt[X];
pt[Y] = tmpPt[Y];
pt[Z] = 0.0;
<span>}</span>
<span>return</span><span> S_OK;</span>
<span>}</span>
<span>}</span>
<span>return</span><span> E_INVALIDARG; </span>
<span>}</span>
<span>// Helper method to get the COM wrapper for an entity</span><span> </span>
<span>static</span><span> IAcadBaseObject* get_com_wrapper</span>
(AcDbEntity* entity)
<span>{</span>
AcAxOleLinkManager* manager = AcAxGetOleLinkManager();
<span>if</span><span> (!manager) </span>
<span>return</span><span> NULL; </span>
IUnknown* unknown = manager->GetIUnknown(entity);
<span>if</span><span> (!unknown) </span>
<span>{</span>
CLSID class_id;
<span>if</span><span> (Acad::eOk != entity->getClassID(&class_id)) </span>
<span>return</span><span> NULL; </span>
HRESULT res = CoCreateInstance(
class_id,
NULL,
CLSCTX_ALL,
IID_IUnknown,
(LPVOID*) &unknown);
<span>if</span><span> (FAILED(res)) </span>
<span>return</span><span> NULL; </span>
IAcadBaseObject* base = NULL;
res = unknown->QueryInterface(
<span>__uuidof</span><span> (IAcadBaseObject), </span>
(VOID**) &base);
<span>if</span><span> (FAILED(res)) </span>
<span>return</span><span> NULL; </span>
<span>if</span><span> (!manager->SetIUnknown(entity, unknown)) </span>
<span>return</span><span> FALSE; </span>
base->SetObjectId(entity->objectId());
base->Release();
<span>}</span>
<span>// Caller will release this interface </span><span> </span>
IAcadBaseObject* wrapper = NULL;
HRESULT res = unknown->QueryInterface(
<span>__uuidof</span><span> (IAcadBaseObject), </span>
(VOID**) &wrapper);
<span>if</span><span> (FAILED(res)) </span>
<span>return</span><span> NULL; </span>
<span>return</span><span> wrapper; </span>
<span>}</span>
<span>// Helper method to retrieve the function desc </span><span> </span>
<span>// from the typeinfo and store it in a map </span><span> </span>
<span>static</span><span> HRESULT LoadNameCache(ITypeInfo* pTypeInfo)</span>
<span>{</span>
TYPEATTR* pta;
HRESULT hr = pTypeInfo->GetTypeAttr(&pta);
<span>if</span><span> (SUCCEEDED(hr))</span>
<span>{</span>
m_nCount = 0;
m_nMapLen = pta->cFuncs;
m_pMap = NULL;
<span>if</span><span> (m_nMapLen > 0)</span>
m_pMap = <span>new</span><span> stringdispid[m_nMapLen];</span>
<span>for</span><span> (<span>int</span><span> i=0; i<m_nMapLen; i++)</span></span>
<span>{</span>
FUNCDESC* pfd;
<span>if</span><span> (SUCCEEDED(pTypeInfo->GetFuncDesc(i, &pfd)))</span>
<span>{</span>
CComBSTR bstrName;
<span>if</span><span> (SUCCEEDED(pTypeInfo->GetDocumentation(</span>
pfd->memid,
&bstrName,
NULL,
NULL,
NULL)))
<span>{</span>
<span>if</span><span> (pfd->invkind == DISPATCH_PROPERTYGET)</span>
<span>{</span>
m_pMap[m_nCount].bstr.
Attach(bstrName.Detach());
m_pMap[m_nCount].nLen
= SysStringLen(m_pMap[i].bstr);
m_pMap[m_nCount].id = pfd->memid;
m_nCount++;
<span>}</span>
<span>}</span>
pTypeInfo->ReleaseFuncDesc(pfd);
<span>}</span>
<span>}</span>
pTypeInfo->ReleaseTypeAttr(pta);
<span>}</span>
<span>return</span><span> S_OK;</span>
<span>}</span>
<span>// Retreive properties from an entity using the </span><span> </span>
<span>// IDispatch interface of its COM wrapper</span><span> </span>
HRESULT dumPolyProps(AcDbEntity *pEntity)
<span>{</span>
::CoInitialize(NULL);
IAcadBaseObject *pAcadObj = get_com_wrapper(pEntity);
CComQIPtr<ITypeLib> pTypeLib(pAcadObj);
CComQIPtr<ITypeInfo> pTypeInfo(pAcadObj);
TCHAR full_app_file_name[512] = L<span>""</span><span> ; </span>
<span>int</span><span> nBufferLength = 512;</span>
TCHAR* filePart;
DWORD result;
result = SearchPath(NULL, _T(<span>"asdkcompoly.dbx"</span><span> ), </span>
_T(<span>".dbx"</span><span> ), 512, full_app_file_name, &filePart);</span>
<span>if</span><span> (result && result < (DWORD)nBufferLength)</span>
<span>{</span>
HRESULT hr = LoadTypeLib
(full_app_file_name, &pTypeLib);
<span>if</span><span> (FAILED(hr)) </span>
<span>return</span><span> hr;</span>
UINT iCount = pTypeLib->GetTypeInfoCount();
<span>for</span><span> (UINT i=0; i < iCount ; i++) </span>
<span>{</span>
hr = pTypeLib->GetTypeInfo(i, &pTypeInfo);
<span>if</span><span> (FAILED(hr))</span>
<span>return</span><span> hr;</span>
LoadNameCache(pTypeInfo);
CComQIPtr<IDispatch> pDisp(pAcadObj);
<span>// Retrieve the properties</span><span> </span>
<span>for</span><span> (<span>int</span><span> index = 0; index < m_nCount; index++)</span></span>
<span>{</span>
OLECHAR *sMember = m_pMap[index].bstr;
DISPID dispId;
hr = pDisp->GetIDsOfNames(
IID_NULL,
&sMember,
1,
LOCALE_SYSTEM_DEFAULT,
&dispId);
<span>if</span><span> (SUCCEEDED(hr))</span>
<span>{</span>
<span>unsigned</span><span> <span>int</span><span> puArgErr = 0;</span></span>
VARIANT VarResult;
VariantInit(&VarResult);
EXCEPINFO pExcepInfo;
DISPPARAMS pParams;
memset(&pParams, 0, <span>sizeof</span><span> (DISPPARAMS)); </span>
pParams.cArgs = 0;
hr = pDisp->Invoke(
dispId,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYGET,
&pParams,
&VarResult,
&pExcepInfo,
NULL);
<span>if</span><span> (V_VT(&VarResult) </span>
== (VT_ARRAY | VT_R8))
<span>{</span>
<span>// Convert to ads_point</span><span> </span>
ads_point pt;
get_PointFromVariant(VarResult, pt);
_print(_T(<span>"%s : %lf %lf %lf\n"</span><span> ), </span>
sMember, pt[0], pt[1], pt[2]);
<span>}</span>
<span>else</span><span> <span>if</span><span> (VarResult.vt == VT_DISPATCH)</span></span>
<span>{</span>
_print(_T(<span>"%s : VT_DISPATCH\n"</span><span> ), </span>
sMember);
<span>//IDispatchPtr pDispatch </span><span> </span>
<span>// = VarResult.pdispVal;</span><span> </span>
<span>}</span>
<span>else</span><span> </span>
<span>{</span>
hr = VariantChangeType(
&VarResult,
&VarResult,
0,
VT_BSTR);
_print(_T(<span>"%s : %s\n"</span><span> ), </span>
sMember,
VarResult.bstrVal);
<span>}</span>
VariantClear(&VarResult);
<span>}</span>
<span>}</span>
<span>delete</span><span> [] m_pMap;</span>
m_pMap = NULL;
<span>}</span>
<span>}</span>
CoUninitialize();
<span>return</span><span> S_OK;</span>
<span>}</span>
<span>// Invoke it from the dumpEntity method</span><span> </span>
<span>void</span><span> dumpEntity(AcDbEntity *pEnt)</span>
<span>{</span>
<span>if</span><span> (_tcscmp(p, _T(<span>"AsdkPoly"</span><span> )) == 0)</span></span>
dumPolyProps(pEnt);
<span>// ... Rest of the code </span><span> </span>
<span>}</span>
Here is a screenshot of the retrieved property values (Click on it to enlarge) :

Leave a Reply