It is possible to register both 32-bit and 64-bit versions of the same COM server on 64-bit machine. This leads to several questions such as how are they registered and which one of the two is used. I will try to answer them below. But first, let’s start with an example.
Example
Let’s say we have the a simple COM local server called COM3264Server.exe. There is just one interface called ICoCOM3264Server. Here is the IDL file:
[ object, uuid(733C70A7-F7EC-4C15-85D2-5CDB14F4110B), dual, nonextensible, pointer_default(unique) ] interface ICoCOM3264Server : IDispatch{ [id(1), helpstring("Says hello")] HRESULT SayHello(void); }; [ uuid(2F25FC66-2380-42FD-8476-8B5917FB1BF1), version(1.0), ] library COM3264ServerLib { importlib("stdole2.tlb"); [ uuid(9268A299-E817-4C5D-8627-C2582B66F16D) ] coclass CoCOM3264Server { [default] interface ICoCOM3264Server; }; };
The implementation of the method SayHello() is straight forward; it just displays a message box with a text that varies between the two architectures, x64 and x86.
STDMETHODIMP CCoCOM3264Server::SayHello(void) { #ifdef _WIN64 ::MessageBox(NULL, _T("Hello from 64-bit COM server!"), _T("COM3264Server"), MB_OK); #else ::MessageBox(NULL, _T("Hello from 32-bit COM server!"), _T("COM3264Server"), MB_OK); #endif return S_OK; }
Registry
When you register the COM server, the 32-bit and 64-bit versions are registered in different parts of the registry. On 64-bit machine, the registry has two views (or modes):
- a native view, for 64-bit application; (e.g. registry path for CLSIDs is HKEY_CLASSES_ROOT\CLSID\)
- a WOW64 view, which enables redirections for 32-bit applications, a process transparent to the user (e.g. registry path for CLSIDs is HKEY_CLASSES_ROOT\Wow6432Node\CLSID\)
Here is the Registry registration of the (native) 64-bit COM server (notice the registry key in the status bar of the editor, and the path to the server executable).
On, the other hand, the 32-bit COM server is registered under the Wow6432 node.
So if both versions are registered in Windows Registry, which one is picked? Well, both the server and the client can specify which architecture to use:
- the COM server can do this via the PreferredServerBitness Registry value
- the client can do this using one of the flags CLSCTX_ACTIVATE_32_BIT_SERVER and CLSCTX_ACTIVATE_64_BIT_SERVER, which overrides the server preference
If neither the client nor the server specifies a preference, then:
- If the computer that hosts the server is running Windows Server 2003 with Service Pack 1 (SP1) or a later system, then COM will try to match the server architecture to the client architecture. In other words, for a 32-bit client, COM will activate a 32-bit server if available; otherwise it will activate a 64-bit version of the server. For a 64-bit client, COM will activate a 64-bit server if available; otherwise it will activate a 32-bit server.
- If the computer that hosts the server is running Windows XP or Windows Server 2003 without SP1 or later installed, then COM will prefer a 64-bit version of the server if available; otherwise it will activate a 32-bit version of the server.
Server Preference
The server can specify its preferred architecture in the PreferredServerBitness value under AppId (available only on 64-bit Windows). This integer value can be:
- 1: Match the server architecture to the client architecture. For example, if the client is 32-bit, use a 32-bit version of the server, if it is available. If not, the client’s activation request will fail.
- 2: Use a 32-bit version of the server. If one does not exist, the client’s activation request will fail.
- 3: Use a 64-bit version of the server. If one does not exist, the client’s activation request will fail.
Here is the value set in Registry to specify the 64-bit architecture.
When you run the client, it launches the 64-bit version and in my example the following window pops-up:
If I change the value to indicate the 32-bit architecture, the other COM server is launched and the displayed message is:
Client Preference
The client code I used so far looked like this:
ICoCOM3264Server* pServer; HRESULT hr = ::CoCreateInstance( CLSID_CoCOM3264Server, NULL, CLSCTX_LOCAL_SERVER|CLSCTX_REMOTE_SERVER, IID_ICoCOM3264Server, (void**)&pServer); if(SUCCEEDED(hr)) { pServer->SayHello(); pServer->Release(); }
However, the 64-bit version of Windows added new flags to the CLSCTX enumeration.
- CLSCTX_ACTIVATE_32_BIT_SERVER: used to activate or connect to a 32-bit version of the server; fail if one is not registered.
- CLSCTX_ACTIVATE_64_BIT_SERVER: used to activate or connect to a 64 bit version of the server; fail if one is not registered.
As I mentioned earlier, if the client uses one of these flags, it overrides the server preference (specified via the PreferredServerBitness Registry value).
In the following example, the client requests the 64-bit COM server:
HRESULT hr = ::CoCreateInstance( CLSID_CoCOM3264Server, NULL, CLSCTX_LOCAL_SERVER|CLSCTX_REMOTE_SERVER|CLSCTX_ACTIVATE_64_BIT_SERVER, IID_ICoCOM3264Server, (void**)&pServer);
And if you run it, no matter what the server specified, the 64-bit COM server is launched.
To read more about the subject see the MSDN links above.
Hi Marius,
What i post here is not a comment, instead a question. This seems to be bigger, but pls help me to resolve the problem.
I have written a WMI provider using c#.net for extracting hardware specific information for which I have used native DLLs. The provider after installtion works fine with 32 bit systems but fails with a 64 bit system when i manually queried WMI data using wmic.
My doubt:
1.When i tried 32 bit provider on 64 bit system, it says provider load failure; with 64 bit provider, I get a BadImageFormatException; provider with Any CPU mode return wrong data. I have used a right gacutil and installutil version.
Findings:
I manually queried using wmic, I found that System32\wbem\wmic.exe returns wrong data but Syswow64\wbem\wmic.exe returns correct data. But the default wmic invocation calls wmic under system32.
Tried these options:
a. I read that 32 bit applications by default accesses syswow64 registry. So in that case EnableSyswow64redirection is true by default. But in my case it always reads from system32\wbem\repository.
b. Tried using “CLSCTX_ACTIVATE_32_BIT_SERVER” while calling CocreateInstance(). But the function returns invalid argument.
c.Also I could not manually edit PreferredServerBitness value. It says I don’t have permission to edit though I’m an Administrator.
4. I tried fetching the same information using vbscript where i create a IwbemLocation object and use. Surprisingly I get correct response.
Conclusion:
So I infer the problem lies only on redirecting to an incorrect repository that is kept as my default. Please advise as where I would have gone wrong. I need the provider to support both 32 bit and 64 bit systems. The provider loads DLLs using DLLImport. If the loading itself is a problem, how do i get response using vbscript?
First of all, this is the kind of topic you should as in a development forum (such as codeguru.com or stackoverflow.com) simply because you have higher chances of getting good answers fast. If you do that, please drop a comment here with the link to that discussion.
Now, 64-bit systems have two repositories for system DLLs: 64-bit DLLs go to Windows\System32 (go figure!) and 32-bit DLLs go to Windows\SysWOW64. I agree the naming is confusion, but it was probably easier to implement it like this. So when a native, i.e. 64-bit, app needs a system DLL, it is picked from System32. When a 32-bit app (running on the WOW64 node) needs that, the DLL is loaded from SysWOW64. Registry also has two views: a native one, for 64-bit apps, and a 32-bit compatibility view, for 32-bit apps. An app however is able to access the other registry view using the flags KEY_WOW64_64KEY and KEY_WOW64_32KEY (see http://msdn.microsoft.com/en-us/library/aa384129%28v=vs.85%29.aspx).
A COM server is registered in the 32- or 64-bit view of the Registry, depending on how it was built. If it’s in the 32-bit view, it won’t be available for the 64-bit apps.
So I understand you have a 32-bit app, and you’re calling wmic.exe and you expect that the 32-bit version is used, though in fact the 64-bit, the native version, is actually used. Is that so?
PS: thank you WordPress for losing my reply and having me typing it twice.
Do you use Platform target: AndyCPU to compile your COM server or x64 for 64 bit COM server and x86 for 32 bit COM server.
Thanks!
I hope I understand your question, so here is the answer: if you build your managed COM server project as Any CPU it will always run as 32-bit on 32-bit windows and as 64-bit on 64-bit Windows. Therefore, if you explicitly need it to run as 32-bit on 64-bit windows, then yes, you need to explicitly target x64/x86. Otherwise it can be Any CPU.
Hi Marius,
Can we access 32 bit com server from 64-bit process?
If the 32-bit COM server is out of proc, yes. If it is in-proc, then no, since 32-bit modules cannot be loaded in 64-bit processes (and the other way around).
Sorry to reply to such an old thread, but I have the exact opposite question of Dila.
Can a 64bit out of proc COM server be accessed by a 32bit application?
Thank in advance for your help!
The answer is the same. As long as the COM server is out of proc, it doesn’t matter if it is 32-bit or 64-bit. It can be accessed from either 32-bit or 64-bit applications. COM is handling all the necessary marshalling for the inter process communication.
Hi Marius! First, I would like to thank you for the post, very clear and helpful!
I’m facing a scenario where I need to change the CLSID of the COM whenever it is compiled in 32 or 64 bits. Using your example, it would be something like this:
#ifdef _WIN64
library COM3264ServerLib
{
importlib(“stdole2.tlb”);
[
uuid(9268A299-E817-4C5D-8627-C2582B66F16D) //CLSID for 64bits
]
coclass CoCOM3264Server
{
[default] interface ICoCOM3264Server;
};
};
#else
library COM3264ServerLib
{
importlib(“stdole2.tlb”);
[
uuid(44a34657-9692-4a47-810f-4e70c2afc83b) //CLSID for 32bits
]
coclass CoCOM3264Server
{
[default] interface ICoCOM3264Server;
};
};
#endif
My questions are: Is it possible to make something like this within the .idl file? If so, is this the best approach to solve the problem?
Thanks in advance!