Вызов функции из 64-битного приложения из 32-битной DLL - PullRequest
0 голосов
/ 02 октября 2018

У меня есть 32-битная библиотека DLL (без исходного кода), к которой мне нужно получить доступ из 64-битного приложения C #.Я прочитал эту статью и посмотрел соответствующий код из здесь .Я также прочитал этот пост.Я не уверен, что задаю правильный вопрос, поэтому, пожалуйста, помогите мне.

Есть 3 проекта: dotnetclient, x86Library и x86x64.x86x64 имеет x86LibraryProxy.cpp, который загружает x86library.dll и вызывает функцию GetTemperature:

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 вызывает эту функцию GetTemperature и печатает результат:

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...");

Это все работает, если я собираю все проекты как x86 или x64.Результат для температуры я получаю 20.Но вся идея заключалась в том, чтобы использовать 32-битную x86x64Lib.dll.Это означает, что dotnetclient должен быть построен как x64, а x86Library и x86x64 как x86, верно?Если я сделаю это, я получу -1 в результате.

Должен ли я построить x86Library и x86x64 как x86 и dotnetclient как x64?Если да, то в чем может быть проблема, которую я получаю -1?

CLARIFICATION Кажется, что приведенный пример работает только тогда, когда клиент и сервер встроены в 32 или 64 бит,Но не тогда, когда клиент строит в 64-битной, а сервер в 32-битной.Может кто-нибудь взглянуть, пожалуйста?

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

ИМХО, самый простой способ сделать это - использовать COM + (Службы компонентов) , который является частью Windows примерно 20 лет назад (предыдущие версии назывались MTS ...).Он предоставляет вам суррогатную инфраструктуру с инструментами, пользовательским интерфейсом и всем необходимым.Но это означает, что вам придется использовать COM, поэтому для этого полезно немного узнать о COM.

Сначала создайте COM-библиотеку x86.Я использовал ATL для этого.Создал проект ATL, добавил в него простой объект ATL, добавил метод в IDL и его реализацию.

.idl (обратите внимание на атрибуты [out, retval], поэтому температура считается возвращаемым значением для более высокого уровня).языки, включая .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";

Реализация метода для целей тестирования:

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

Теперь вы должны зарегистрировать этот компонент в 32-битном реестре (на самом деле, если вы 'При запуске Visual Studio без прав администратора, во время компиляции он будет жаловаться, что компонент не может быть зарегистрирован, что ожидается), поэтому в 64-битной ОС вы должны запустить что-то вроде этого (обратите внимание на SysWow64) с правами администратора:

c:\Windows\SysWOW64\regsvr32 x86Library.dll

После этого запустите «Службы компонентов», выберите «Компьютеры / Мой компьютер / Приложения COM +», щелкните правой кнопкой мыши и создайте Новое приложение.Выберите имя и «Серверное приложение».Это означает, что ваш компонент будет размещен в суррогатном процессе COM +.

enter image description here

После того, как вы это сделаете, выберите «Компоненты», щелкните правой кнопкой мыши и создайтеНовый Компонент.Убедитесь, что вы выбрали «32-битный реестр».Вы должны увидеть ProgId вашего объекта.В моем случае, когда я создал свой проект ATL, я добавил «MyObject» как Progid, но в противном случае его можно было бы назвать как «x86Library.MyObject» или «x86LibraryLib.MyObject» ... Если его там нет, то вы допустили ошибкуранее.

enter image description here

Вот и все.Теперь эта программа .NET всегда сможет работать, скомпилированная как AnyCpu или x86 или 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
    }
}

. Вы можете использовать интерфейс компонентов служб для настройки суррогата (активация, завершение работы и т. Д.).Он также имеет API, поэтому вы можете программно создавать COM + приложения .

0 голосов
/ 02 октября 2018

Вы не сможете напрямую вызывать 32-битный код из 64-битного кода (или наоборот), это просто не произойдет.

Существуют альтернативы, такие каксоздание 32-битной хост-программы COM, которая затем перенаправляет вызовы в DLL.В сочетании с этим вы используете стандартную сортировку DCOM, чтобы ваш 64-битный процесс мог подключаться к 32-битному хосту.

Но если перекомпиляция 32-битной DLL - это вообще вариант, который почти наверняка является вашим лучшим вариантом.

...