Retrieving property values from entity’s COM wrapper

<?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) :

PropCapture


Comments

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading