4

I have a 32bit dll (no source code) that I need to access from 64bit C# application. I've read this article and took a look into the corresponding code from here. I've also read this post. I'm not sure that I'm asking the right question, so please help me.

There are 3 projects: dotnetclient, x86Library and x86x64. The x86x64 has x86LibraryProxy.cpp which loads the x86library.dll and calls the GetTemperature function:

STDMETHODIMP Cx86LibraryProxy::GetTemperature(ULONG sensorId, FLOAT* temperature)
{
    *temperature = -1;
    typedef float (__cdecl *PGETTEMPERATURE)(int);
    PGETTEMPERATURE pFunc;
    TCHAR buf[256];
    HMODULE hLib = LoadLibrary(L"x86library.dll");
    if (hLib != NULL)
    {
        pFunc = (PGETTEMPERATURE)GetProcAddress(hLib, "GetTemperature");
        if (pFunc != NULL)

dotnetclient calls that GetTemperature function and print the result:

static void Main(string[] args)
{
    float temperature = 0;
    uint sensorId = 2;
    var svc = new x86x64Lib.x86LibraryProxy();
    temperature = svc.GetTemperature(sensorId);
    Console.WriteLine($"temperature of {sensorId} is {temperature}, press any key to exit...");

This all works if I build all projects either as x86 or x64. The result for the temperature I get is 20. But, the whole idea was to use 32bit x86x64Lib.dll. That means that dotnetclient should be built as x64 and x86Library and x86x64 as x86, right? If I do this I get -1 as a result.

Should I build x86Library and x86x64 as x86 and dotnetclient as x64? If I do, so what can be the problem that I get -1?

CLARIFICATION It seems that the provided example only works when both client and server are build in 32 or 64 bit. But not when the client build in 64bit and the server in 32bit. Can someone take a look please?

sɐunıɔןɐqɐp
  • 3,332
  • 15
  • 36
  • 40
theateist
  • 13,879
  • 17
  • 69
  • 109

2 Answers2

3

IMHO, the easiest way to do this is to use COM+ (Component Services) which is part of Windows for like 20 years or so (previous versions used to be called MTS...). It provides the surrogate infrastructure for you with tools, UI, and everything you need. But that means you'll have to use COM, so it's good to know a bit of COM for this.

First create an x86 COM DLL. I've used ATL for that. Created an ATL project, added an ATL simple object to it, added the method to the IDL and implementation.

.idl (note the [out, retval] attributes so the temperature is considered a return value for higher level languages including .NET):

import "oaidl.idl";
import "ocidl.idl";

[
  object,
  uuid(f9988875-6bf1-4f3f-9ad4-64fa220a5c42),
  dual,
  nonextensible,
  pointer_default(unique)
]
interface IMyObject : IDispatch
{
  HRESULT GetTemperature(ULONG sensorId, [out, retval] FLOAT* temperature);
};
[
  uuid(2de2557f-9bc2-42ef-8c58-63ba77834d0f),
  version(1.0),
]
library x86LibraryLib
{
  importlib("stdole2.tlb");
  [
    uuid(b20dcea2-9b8f-426d-8d96-760276fbaca9)
  ]
  coclass MyObject
  {
    [default] interface IMyObject;
  };
};

import "shobjidl.idl";

Method implementation for testing purposes:

STDMETHODIMP GetTemperature(ULONG sensorId, FLOAT* temperature)
{
  *temperature = sizeof(void*); // should be 4 in x86 :-)
  return S_OK;
}

Now, you must register this component in the 32-bit registry (in fact, if you're running Visual Studio w/o admin rights, it will complain at compile time that the component cannot be registered, that's expected), so on a 64-bit OS, you must run something like this (note SysWow64) with admin rights:

c:\Windows\SysWOW64\regsvr32 x86Library.dll

Once you've done that, run "Component Services", browse "Computers/My Computer/COM+ Applications", right click and create a New Application. Choose a name and a "Server application". It means your component will be hosted in COM+ surrogate process.

enter image description here

Once you've done that, browse "Components", right click and create a New Component. Make sure you select "32-bit registry". You should see your object's ProgId. In my case when I created my ATL project I added "MyObject" as a Progid, but otherwise it could be named something like "x86Library.MyObject" or "x86LibraryLib.MyObject"... If it's not there, than you made some mistake earlier.

enter image description here

That's it. Now, this .NET program will always be able to run, compiled as AnyCpu or x86 or x64:

class Program
{
    static void Main(string[] args)
    {
        var type = Type.GetTypeFromProgID("MyObject"); // the same progid
        dynamic o = Activator.CreateInstance(type);
        Console.WriteLine(o.GetTemperature(1234)); // always displays 4
    }
}

You can use Component Services UI to configure your surrogate (activation, shutdown, etc.). It also has an API so you can create COM+ apps programmatically.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • In my case the implementation of `GetTemperature` will be loading my 32bit dll by calling `LoadLibrary(...)` and then calling the right function from that dll, right? – theateist Oct 02 '18 at 14:41
  • Also, you created in-process COM dll with ATL. Why didn't you create out-of-process COM exe with ATL? Wouldn't be that COM exe loades as surrogate automatically when I double click on it? – theateist Oct 02 '18 at 14:47
  • Yes, for the LoadLibrary, no problem, provided they're all 32-bit binaries. For the COM exe with ATL, well, I've almost never done that because it's much easier to write a COM dll and benefit from Component Service features (activation, security, shutdown, etc.). Plus when you write a DLL, you still can use it in-process in other hosts for other scenarios. – Simon Mourier Oct 02 '18 at 16:35
0

You are not going to be able to directly call 32-bit code from 64-bit code (or the other way around), it simply is not going to happen.

There are alternatives, such as creating a 32-bit COM host program that then forwards calls to the DLL. Coupled with that you use DCOM standard marshalling so your 64-bit process can connect to the 32-bit host.

But if recompiling the 32-bit DLL is at all an option that is almost certainly your best option.

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
  • I'm sorry, but how does this answers my question? I'm aware that you cannot load a 32bit dll in 64bit space. I'm aware that you I need to load that dll into 32bit process and use IPC to call a function from a 64bit process into 32bit process. That's exactly what they example project that is mentioned in my post suppose to do. But, I don't quite understand how it achived it if the autoher builds the dll as 64bit. – theateist Oct 02 '18 at 03:45
  • can you provide a simple example or step by step instructions how to do it please? – theateist Oct 02 '18 at 05:07