Introduction
The Microsoft PIA for MSHTML is riddled with method signatures that incorrectly declare string parameters as ref ushort, making them unusable.
This article explains how to get and set strings incorrectly declared as ref ushort
.
Background
A PIA (primary interop assembly) is the vendor's official rendering of the interfaces of some COM object(s). It's signed and so forth, so you can't alter it. When a PIA contains incorrect declarations of parameters or marshalling hints, it can be nigh impossible to use the incorrectly declared methods.
You can't dodge the problem with typecasting. The strings in question are declared as POLECHAR
, which is a pointer to the first element of a null terminated array of unicode characters. A unicode character is 16 bits wide and unsigned, and you need a pointer to it, hence ref ushort
.
You could use tlbimp.exe
to import the interfaces and then correct them. All you have to do is replace "ref ushort" with "string". But nothing is ever that easy. The webbrowser control uses the Microsoft PIA and C# strong typing treats your declarations and the PIA equivalents as totally different. You can explicitly typecast, but you'll have to typecast everything. This works, but the code is illegible and unmaintainable (I tried).
So, tblimp.exe
hasn't exactly got it wrong but there's no way to directly typecast a ref ushort
to a string
, and we can't alter the interop assembly, and we can't replace it.
All is now lost... or not. Enter the Marshal
class.
We get the string like this:
string blah = Marshal.PtrToStringUni((IntPtr)lpwszBlah);
And going the other way (if we change it)...
Marshal.FreeHGlobal(lpwszBlah);
lpwszBlah = Marshal.StringToHGlobalUni(blah);
Points of Interest
There are unicode, ANSI and Auto versions of most of the string marshalling methods. Auto selects ANSI or unicode behaviour depending on the underlying platform. In some cases, such as dealing with COM, the string type is platform invariant and the auto versions of these calls will fail on legacy platforms. For exampe, MSHTML uses POLECHAR on all platforms, and Marshal.PtrToStringAuto((IntPtr)lpwszBlah) would incorrectly parse the data as an ANSI string under Windows 9x.
The Marshal class is a point of interest all on its own. How do you think Microsoft does all that wonderful magic conversion? Sure, you hint the process with attributes, but sooner or later the rubber hits the road, and generally Marshal is involved. Microsoft has very thoughtfully made this incredibly handy class directly accessible, and in a departure from tradition even documented it.