Consider the following code for COM interop with internet shortcuts:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("CABB0DA0-DA57-11CF-9974-0020AFD79762")]
public interface IUniformResourceLocatorW
{
void SetUrl([MarshalAs(UnmanagedType.LPWStr)] string pcszUrl, int dwInFlags);
void GetUrl([MarshalAs(UnmanagedType.LPWStr)] out StringBuilder ppszUrl);
void InvokeCommand(IntPtr purlici);
}
[ComImport]
[Guid("FBF23B40-E3F0-101B-8488-00AA003E56F8")]
public class InternetShortcut
{
}
The following works as expected:
var ishort = new InternetShortcut();
((IPersistFile)ishort).Load("MyLink.url", 0);
((IUniformResourceLocatorW)ishort).GetUrl(out url);
However:
IUniformResourceLocatorW.SetUrl
(which I am not using), IUniformResourceLocatorW.GetUrl
throws a COMException
(HResult 0x80041001).IUniformResourceLocatorW.SetUrl
and IUniformResourceLocatorW.GetUrl
(that is place the former below the latter) the same exception is thrownIUniformResourceLocatorW.InvokeCommand
the code runs fine.It's as if the order has to be preserved "up to" the invoked method. Is this behavior by design? documented somewhere? I'm asking because some COM interfaces are composed of many methods with possibly many supporting types and I'd rather avoid defining what I don't need if possible.
Order is very, very, very important. An interface pointer in COM is a pointer to a table of addresses of functions. The first three are function pointers to the IUnknown method implements (AddRef, Release, QueryInterface). The 4th is the address of the server's SetUrl() method, 5th is GetUrl(), etc.
By removing SetUrl() from the declaration, the CLR thinks it should call the 4th function in the table when your program calls GetUrl(). So doesn't call GetUrl() at all, it ends up calling SetUrl() instead. Which gets the completely wrong arguments, pcszUrl will probably be an empty string and dwInFlags contains random junk. Kaboom! when SetUrl fails on some kind of WMI call with garbage values.
You cannot remove methods from the interface. You can use a placeholder, like DoNotCall()
.
Otherwise a strong lesson in the kind of DLL Hell you can get from modifying interfaces.
User contributions licensed under CC BY-SA 3.0