Reordering methods in ComImport interfaces throws COMException (0x80041001)

1

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:

  • If I comment out IUniformResourceLocatorW.SetUrl (which I am not using), IUniformResourceLocatorW.GetUrl throws a COMException (HResult 0x80041001).
  • If I switch between IUniformResourceLocatorW.SetUrl and IUniformResourceLocatorW.GetUrl (that is place the former below the latter) the same exception is thrown
  • If I comment out IUniformResourceLocatorW.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.

c#
.net
com
com-interop
asked on Stack Overflow May 31, 2014 by Ohad Schneider

1 Answer

3

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.

answered on Stack Overflow May 31, 2014 by Hans Passant

User contributions licensed under CC BY-SA 3.0