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.
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: