<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>voyce &#187; unmanaged</title>
	<atom:link href="http://www.voyce.com/index.php/tag/unmanaged/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.voyce.com</link>
	<description>Programming and debugging tidbits</description>
	<lastBuildDate>Wed, 11 Aug 2010 03:56:45 +0000</lastBuildDate>
	
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Getting .NET type information in the unmanaged world</title>
		<link>http://www.voyce.com/index.php/2008/12/01/getting-net-type-information-in-the-unmanaged-world/</link>
		<comments>http://www.voyce.com/index.php/2008/12/01/getting-net-type-information-in-the-unmanaged-world/#comments</comments>
		<pubDate>Mon, 01 Dec 2008 15:48:33 +0000</pubDate>
		<dc:creator>ian</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[COM]]></category>
		<category><![CDATA[Debugging]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[ccw]]></category>
		<category><![CDATA[IDispatch]]></category>
		<category><![CDATA[invoke]]></category>
		<category><![CDATA[type]]></category>
		<category><![CDATA[unmanaged]]></category>

		<guid isPermaLink="false">http://www.voyce.com/?p=55</guid>
		<description><![CDATA[One of the tools that I write and maintain displays type information for COM objects hidden behind &#8220;handles&#8221; in Excel spreadsheets. The underlying objects can either support an interface that allows them to be richly rendered to XML, or the viewer will fall-back to using metadata and displaying the supported interfaces and their properties and methods. It [...]]]></description>
			<content:encoded><![CDATA[<p>One of the tools that I write and maintain displays type information for COM objects hidden behind &#8220;handles&#8221; in Excel spreadsheets. The underlying objects can either support an interface that allows them to be richly rendered to XML, or the viewer will fall-back to using metadata and displaying the supported interfaces and their properties and methods. It will also invoke parameterless property getters &#8211; making the assumption that doing so won&#8217;t change the state of the object &#8211; and display the returned value. This is a useful way of getting some visibility on otherwise completely opaque values.</p>
<p>In order to obtain the type information about the COM objects, the tool uses type libraries, and the associated <a href="http://msdn.microsoft.com/en-us/library/ms221549.aspx">ITypeLib </a>and <a href="http://msdn.microsoft.com/en-us/library/ms221696.aspx">ITypeInfo</a> interfaces, which, with a little effort, can be used to iterate over all the coclasses, interfaces and functions in the library. But the difficulty lies in obtaining a type library when all you&#8217;re given is an already-instantiated object. In theory, COM allows you to know no more about an object than what interfaces it supports. But in practice, there are a variety of ways you can circumvent this and get to the type information.</p>
<p>For unmanaged COM objects you can use the information in the registry (or SxS configuration) and obtain the server (DLL) that contain a TLB embedded as a resource, or the type library filename itself. I won&#8217;t go into that now, there&#8217;s plenty of information about the location of these common registry keys elsewhere on the internet.</p>
<p>But for managed COM objects &#8211; well, COM callable wrappers (CCWs) &#8211; you have a different problem: registry scraping will never work and there may not even be an associated type library. The InprocServer32 registry entry always points to mscoree.dll, which obviously doesn&#8217;t have an embedded type library, and unless you&#8217;ve registered the assembly with /tlb (which is a pain) then you won&#8217;t have entries under HKEY_CLASSES_ROOT\Typelib and a TLB file to load.</p>
<p>So, if you&#8217;re in the unmanaged world, and all you&#8217;ve got is a pointer to a live CCW, what can you do?</p>
<p>Well, the easiest thing is to use <a href="http://msdn.microsoft.com/en-us/library/ms687303(VS.85).aspx">IProvideClassInfo</a>. This is supported by all CCWs, and provides a way to get an auto-generated (by the CLR) ITypeInfo implementation for the managed class. In fact, this is what I actually used to implement the solution eventually, but along the way I discovered some other interesting aspects of the CCW.</p>
<p>There is another interface that it supports: _Object, the unmanaged version of System.Object, which supports basic .NET functionality such as ToString and GetType. I couldn&#8217;t find it declared anywhere in the Platform or .NET SDK headers, so I put together a version that I could use from C++:</p>
<div style="font-size: 10pt; background: white; color: black; font-family: Lucida Sans Typewriter;">
<p style="margin: 0px;"><span style="color: #0000ff;">struct</span> <span style="color: #0000ff;">__declspec</span>(<span style="color: #0000ff;">uuid</span>(<span style="color: #800000;">&#8220;{65074F7F-63C0-304E-AF0A-D51741CB4A8D}&#8221;</span>)) Object : <span style="color: #0000ff;">public</span> IDispatch</p>
<p style="margin: 0px;">{</p>
<p style="margin: 0px;"><span style="color: #0000ff;">public</span>:</p>
<p style="margin: 0px;"><span style="color: #008000;">// We don&#8217;t actually call these methods, doing so seems to return</span></p>
<p style="margin: 0px;"><span style="color: #008000;">// COR_E_INVALIDOPERATION. Instead we just use the IDispatch::Invoke</span></p>
<p style="margin: 0px;"><span style="color: #008000;">// and use the DISPID of the methods.</span></p>
<p style="margin: 0px;"><span style="color: #0000ff;">virtual</span> HRESULT STDMETHODCALLTYPE ToString(BSTR *) = 0;</p>
<p style="margin: 0px;"><span style="color: #0000ff;">virtual</span> HRESULT STDMETHODCALLTYPE Equals(VARIANT, VARIANT_BOOL *) = 0;</p>
<p style="margin: 0px;"><span style="color: #0000ff;">virtual</span> HRESULT STDMETHODCALLTYPE GetHashCode(<span style="color: #0000ff;">long</span> *) = 0;</p>
<p style="margin: 0px;"><span style="color: #0000ff;">virtual</span> HRESULT STDMETHODCALLTYPE GetType(mscorlib::_Type **) = 0;</p>
<p style="margin: 0px;">};</p>
</div>
<p><!--EndFragment-->Despite the presence of the virtual functions in this &#8220;interface&#8221;, we&#8217;re not actually going to call them. Instead we&#8217;ll call through the IDispatch that it derives from. It may be possible to use them directly, but see the comment describing what happens when I tried it. Calling via IDispatch may seem slightly odd, because the object itself claims not to support it (QueryInteface returns E_NOINTERFACE).</p>
<p>The methods on the _Object interface have well-known DISPIDs:</p>
<table border="0">
<tbody>
<tr>
<td>ToString</td>
<td>0&#215;00000000</td>
</tr>
<tr>
<td>Equals</td>
<td>0&#215;60020001</td>
</tr>
<tr>
<td>GetHashCode</td>
<td>0&#215;60020002</td>
</tr>
<tr>
<td>GetType</td>
<td>0&#215;60020003</td>
</tr>
</tbody>
</table>
<p>So we can use that to invoke the GetType method:</p>
<div style="font-size: 10pt; background: white; color: black; font-family: Lucida Sans Typewriter;">
<p style="margin: 0px;">DISPPARAMS parms;</p>
<p style="margin: 0px;">parms.cArgs = 0;</p>
<p style="margin: 0px;">parms.cNamedArgs = 0;</p>
<p style="margin: 0px;">_variant_t vType;</p>
<p style="margin: 0px;">hr = pObject-&gt;Invoke(0&#215;60020003, IID_NULL, 0, DISPATCH_METHOD, &amp;parms, &amp;vType, NULL, NULL);</p>
</div>
<p><!--EndFragment-->And we get back a <a href="http://msdn.microsoft.com/en-us/library/system.runtime.interopservices._type.aspx">_Type</a> interface that allows us to navigate around the type information in the same way as we can with System.Type! Just #import mscorlib.tlb and you get all the interfaces you need to e.g. iterate over all the interfaces implemented by a type, and invoke a function on them:</p>
<div style="font-size: 10pt; background: white; color: black; font-family: Lucida Sans Typewriter;">
<p style="margin: 0px;"><span style="color: #0000ff;">#import</span> <span style="color: #800000;">&lt;mscorlib.tlb&gt;</span> rename(<span style="color: #800000;">&#8220;ReportEvent&#8221;</span>,<span style="color: #800000;">&#8220;xReportEvent&#8221;</span>)</p>
<p style="margin: 0px;">&#8230;</p>
<p style="margin: 0px;">mscorlib::_TypePtr t(V_UNKNOWN(&amp;vType));</p>
<p style="margin: 0px;">CComSafeArray&lt;LPUNKNOWN&gt; saInterfaces(t-&gt;GetInterfaces());</p>
<p style="margin: 0px;">&#8230;</p>
<p style="margin: 0px;">mscorlib::_TypePtr tInterface((LPUNKNOWN)saInterfaces.GetAt(n));</p>
<p style="margin: 0px;">&#8230;</p>
<p style="margin: 0px;">result = tInterface-&gt;InvokeMember(_bstr_t(<span style="color: #800000;">&#8220;Function&#8221;</span>),</p>
<p style="margin: 0px;">(mscorlib::BindingFlags)</p>
<p style="margin: 0px;">(mscorlib::BindingFlags_GetProperty +</p>
<p style="margin: 0px;">mscorlib::BindingFlags_InvokeMethod +</p>
<p style="margin: 0px;">mscorlib::BindingFlags_Public +</p>
<p style="margin: 0px;">mscorlib::BindingFlags_Instance +</p>
<p style="margin: 0px;">mscorlib::BindingFlags_IgnoreCase),</p>
<p style="margin: 0px;">NULL, _variant_t(punk), NULL, NULL, NULL, NULL);</p>
</div>
<p>So this turns out to be quite nice: you can get rich managed type information even if you&#8217;re running in the unmanaged world.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.voyce.com/index.php/2008/12/01/getting-net-type-information-in-the-unmanaged-world/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
