ICallFactory with 32-bit and 64-bit type libraries side by side

547 views Asked by At

I have a in-proc COM server for which I want to build 32-bit and 64-bit versions. I can do this with no problem. However, I am encountering some issues when both versions are registered.

I am not using ATL. In my DllRegisterServer function, I am using RegisterTypeLibForUser. I am acquiring the ITypeLib instance for the RegisterTypeLibForUser call by calling LoadTypeLibEx with my DLL's path, and I am using the REGKIND_NONE flag. I am creating my type library using .idl and the MIDL compiler. I am embedding the type library in my .dll as a resource. Given the first two bullets below (where everything works as expected), there doesn't seem to be any issues with the way that I'm doing this.

  • If I register ONLY 32-bit, everything works fine in a 32-bit client, and I get expected failures in a 64-bit client (class not registered).
  • If I register ONLY 64-bit, everything works fine in a 64-bit client, and I get expected failures in a 32-bit client (class not registered).
  • If I register 64-bit followed by 32-bit, everything works fine in a 32-bit client, but I get failures in a 64-bit client. If I then unregister the 32-bit server, the 64-bit client continues to fail. If I re-register the 64-bit server (with or without unregistering), the 64-bit client works.
  • If I register 32-bit followed by 64-bit, everything works fine in a 64-bit client, but I get failures in a 32-bit client. If I then unregister the 64-bit server, the 32-bit client continues to fail. If I re-register the 32-bit server (with or without unregistering), the 32-bit client works.

It appears that when I register both servers, the latter RegisterTypeLibForUser call does something that screws up the registry settings for the previous RegisterTypeLibForUser call.

As for the errors I'm getting:

  • CoCreateInstance always works as long as the correct server is registered. In a 32-bit client, CoCreateInstance works as long as the 32-bit server is registered (even if the 64-bit server is also registered). Ditto for 64-bit client with 64-bit server.
  • In any situation where CoCreateInstance works, I can invoke methods on the object. I can marshal my interfaces between apartments (I'm using the global interface table), and I can invoke methods on the marshaled interfaces.
  • The errors I'm getting are specifically related to ICallFactory in an apartment to which the interfaces have been marshaled. I can query for ICallFactory on the marshaled interface without any problem. But when I call CreateCall with the IID of my asynchronous interface, I receive the error E_NOINTERFACE.
  • As mentioned in the previous list, I do not receive this error as long as the server that was last registered is the one with the same target platform as the client.

I'm trying to dig through my registry now and figure out exactly what is being changed as the registrations are happening, but due to the registry redirector, this isn't so simple. I will update this post as I uncover the registry information.

1

There are 1 answers

9
Michael Gunter On BEST ANSWER

Figured it out.

RegisterTypeLib and RegisterTypeLibForUser always write both 32-bit and 64-bit entries when running on a 64-bit OS (even if the process is 32-bit). This is perfectly acceptable in most cases since it's only the interface and type library metadata that gets written. When the interface key is written, RegisterTypeLib/RegisterTypeLibForUser set ProxyStubClsid32 to the generic, default p/s that is appropriate for the type of interface (dual, oleautomation, etc). However, the generic proxy/stubs do not seem to work with ICallFactory on custom async interfaces. I modified my registration routine to always setup my custom proxy/stub information in both the 32-bit and 64-bit registry keys. This makes sure a later registration doesn't trump this information from a previous registration.

[UPDATE]: In the end, I had to write my own registration routine due to several weaknesses in the available APIS:

  • RegisterTypeLib and RegisterTypeLibForUser do not write the AsynchronousInterface, SynchronousInterface and NumMethods registry entries. This is because there is no information in the compiled type library that explicitly links the synchronous and asynchronous interfaces. My own registration routine matches interfaces by name -- it finds IXxx interfaces with corresponding AsyncIXxx interfaces (by name only, no method comparison) and sets these registry entries as appropriate.
  • RegisterTypeLib and RegisterTypeLibForUser always write both 32-bit and 64-bit registry keys, including but not limited to ProxyStubClsid32. This means that registering a type library by this means will overwrite any ProxyStubClsid32 for the opposite architecture that was previously written by other means. To resolve this, one could just register both type libraries (32 and 64) first and then register the proxy/stub dlls. However:
  • The proxy/stub code generated by the MIDL compiler does not seem to provide any way to register the p/s in the per-user registry. If per-user registration is a requirement, the generated registration routine is of no use.
  • Even if the generated proxy/stub registration routine could be per-user, if the proxy/stub is merged into the com server dll, there is only one registration opportunity -- the single DllRegisterServer in the single dll. If that routine only writes the ProxyStubClsid32 entry on one side of the registry (32- or 64-bit, not both), the subsequent registration of the dll for the other architecture will change the ProxyStubClsid32 for the first architecture.
  • I haven't tested some of the apis like CoRegisterPSClsid. Since the documentation says nothing about per-user registration, I would imagine it's not supported.