Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
491 views
in Technique[技术] by (71.8m points)

c# - Calling a function of a 32-bit DLL from a 64-bit Application

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...