1

I am migrating an Excel add-in originally written in VB6 to managed code and 64-bit Excel. The add-in interoperates with some legacy 32-bit COM DLL servers through COM surrogates, and an issue has been encountered where only methods and properties in the coclasses' respective default interfaces are available at runtime. Attempts to call methods and properties in any other interfaces exposed by the components results in System.MissingMethodExceptions.

The non-default interfaces are available when the migrated add-in is used with 32-bit Excel and the servers run in-process.

Is there anything that can be done to make the non-default interfaces available to the add-in in 64-bit mode?

R.J. Dunnill
  • 2,049
  • 3
  • 10
  • 21
  • 1
    In general, not by just configuration. Discovering COM rules (proxy/stub, marshaling, threading models, apartments) were not understood (or simply overlooked) by the developer(s) only when you go out-of-process is a common case. The DLL must be modified or you must write a COM-correct 32-bit wrapper (another dll) on top of it so you can use that wrapper from 64-bit. – Simon Mourier Jul 07 '21 at 05:49
  • Are there any resources available? All I can find are vague allusions to custom surrogates. – R.J. Dunnill Jul 07 '21 at 14:06
  • You just need one surrogate: the one you currently use. The wrapper I'm talking about would be a 32-bit DLL, inproc with your 32-bit DLL. Its purpose is just to expose the same (or almost) COM interfaces to the rest of the COM world, but compatible with out-of-process client/surrogates. It's just custom code to write. You would not use the 32-bit DLL directly in COM anymore. – Simon Mourier Jul 07 '21 at 14:21
  • Can you point me to an example? – R.J. Dunnill Jul 07 '21 at 14:27
  • 1
    No as it fully depends on your 32-bit DLL and the exact problems it exhibits. There's nothing magic here, it's not necessarily an easy task. If you have a small existing reproducing project, we can try to do something. – Simon Mourier Jul 07 '21 at 14:39
  • "Developer's Workshop to COM and ATL 3.0" has a chapter devoted to this. – R.J. Dunnill Jul 08 '21 at 07:25
  • Not sure what you're after when you say "this", if you're looking for how to create a COM component or information on how COM works in general, yes there are plenty examples on the web and ATL is certainly what I'd use. But you'll find nothing for fixing your specific case. – Simon Mourier Jul 08 '21 at 07:40
  • The surprisingly simple fix to the problem of getting these 22-year-old components to work out-of-proc was in "Developer's Workshop to COM and ATL 3.0." I'll post it later today. – R.J. Dunnill Jul 08 '21 at 08:03

1 Answers1

1

The solution was to use type library marshaling (also known as universal marshaling), using the system-provided marshaling engine in oleaut32.dll. The universal marshaler can be used with any custom interface, provided that the parameters are variant-compatible.

To use the universal marshaler for an interface, the interface is tagged with oleautomation, as in this sample interface:

[
    object,
    oleautomation, 
    uuid(23D4EC0B-96DA-4D18-82BD-40E3AA0483FD),
    version(1.0),
    dual,
    helpstring("Description of ICustomInterface1"),
    pointer_default(unique)
]
interface ICustomInterface1 : IDispatch
{
    // Methods and properties …
}

Universal marshaling also requires the following Registry entries:

  • HKCR\TypeLib<type library GUID><type library version>

  • HKCR\TypeLib<type library GUID><type library version>\0

  • HKCR\TypeLib<type library GUID><type library version>\0\win32 = “path to type library file”

  • HKCR\TypeLib<type library GUID><type library version>\Flags

  • HKCR\TypeLib<type library GUID><type library version>\HelpDir = “path to help folder”

  • [HKEY_CLASSES_ROOT\Interface<interface GUID> @="Interface name"

  • [HKEY_CLASSES_ROOT\Interface<interface GUID>\ProxyStubClsid32 ="{00020424-0000-0000-C000-000000000046}"

  • HKCR\Interface<interface GUID\TypeLib = type library GUID

With this type information, the universal marshaler creates a proxy-stub at runtime, eliminating the need for a custom proxy-stub DLL.

In our case, given that the components were ATL-based, the Registry entries were created automatically once the interfaces were tagged with ‘oleautomation’ and the components rebuilt. For non-ATL projects, this can be done programmatically with the Win32 API’s LoadTypeEx() (which I haven't tried).

Once this was done, non-default interfaces of our ATL-based components became available out-of-process to our migrated-to-VB.NET Excel COM add-in.

All this is detailed in Chapter 5 of "Developer's Workshop to COM and ATL 3.0."

R.J. Dunnill
  • 2,049
  • 3
  • 10
  • 21
  • This is far from being a general case solution. It's only because your DLL is fully COM Automation compatible and you are lucky (even with oleautomation, it still could fail depending on implementation). Usually, COM objects like that are already configured, compiled and registered properly. It's also lucky because you have ATL (you should have mentioned in the question it was an ATL project you have the source for). For a non ATL project, it's highly unlikely this would work. Since you have the source why don't you just port it to x64? If written properly, it may be just a matter of compiling – Simon Mourier Jul 09 '21 at 06:13
  • Porting the whole stack to x64 isn't in the scope of this release, and it may be several years before management authorizes it. The only reason this add-in was migrated is that 64-bit Office is prevalent and customers are demanding 64-bit compatibility. – R.J. Dunnill Jul 09 '21 at 07:20