Изменить целевой код С# DllImport в зависимости от x64/x86

У меня есть внешняя DLL c++ для импорта с использованием DLLImport. Если мое приложение компилируется в x64, мне нужно импортировать x64-версию этого dll, если это x86-сборка, мне нужен x86-dll.

Каков наилучший способ достичь этого?

В идеале я хотел бы получить некоторую директиву препроцессора, но я понимаю, что это не работает в С#?

Больше информации: DLL импортируется проектом, для которого установлено AnyCPU. Родительский проект - это тот, который определяет, будет ли приложение компилироваться как x64 или x86. Мы компилируем обе версии для разных клиентов - и я хочу поделиться дочерним проектом в обеих версиях.

Ответы

Ответ 1

Это прежде всего проблема развертывания, просто попросите установщика скопировать нужную DLL на основе версии Windows на целевой машине.

Но никто никогда не любит это делать. Динамически вызывать правильную функцию DLL чрезвычайно тяжело, вам нужно писать типы делегатов для каждой экспортируемой функции и использовать LoadLibrary + GetProcAddress + Marshal.GetDelegateForFunctionPointer для создания объекта делегирования.

Но никто никогда не любит это делать. Менее болезненным является объявление функции дважды, присвоение ей разных имен и использование свойства EntryPoint в атрибуте [DllImport] для указания реального имени. Затем проверьте во время выполнения, которое вы хотите вызвать.

Но никто никогда не любит это делать. Самый эффективный трюк - это заставить Windows загружать нужную DLL для вас. Первое, что вам нужно сделать, это скопировать DLL в каталог, где Windows не будет искать его. Лучший способ - создать подкаталог "x86" и "x64" в каталоге сборки и скопировать соответствующую DLL в каждую. Сделайте это, написав событие post-build, которое создает каталоги и копирует библиотеки DLL.

Затем скажите Windows об этом, выведя SetDllDirectory(). Указанный вами путь будет добавлен в каталоги, которые Windows ищет в DLL. Вот так:

using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;

class Program {
    static void Main(string[] args) {
        var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
        path = Path.Combine(path, IntPtr.Size == 8 ? "x64" : "x86");
        bool ok = SetDllDirectory(path);
        if (!ok) throw new System.ComponentModel.Win32Exception();
        //etc..
    }
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool SetDllDirectory(string path);
}

Учтите, действительно ли полезен для вас код, выполняющийся в 64-битном режиме. Довольно редко требуется огромное виртуальное адресное пространство виртуальной памяти, которое вы получаете от него, единственное реальное преимущество. Вам все равно необходимо поддерживать 32-разрядную версию, которая должна работать правильно в случае с 2 гигабайтами.

Ответ 2

Добавьте как x86, так и x86_64 DLL-импорт с разными именами, тогда вы можете условно вызывать их в зависимости от архитектуры во время выполнения, проверяя значение Environment.Is64BitProcess (или IntPtr.size, если вы используете <.Net 4). Это будет работать независимо от того, построен ли проект как x86, x86_64 или AnyCPU

В качестве альтернативы, настройте две разные конфигурации компоновки - одну, которая только делает x86, и только x86_64, дайте каждому условный символ компиляции и используйте #ifdef для вашего пользовательского символа.