Используя 32bit или 64bit dll файлы атрибут DllImport в C#
вот ситуация, я использую dll на основе C в моем dot.net применение. Есть 2 DLL, один из которых 32bit называется MyDll32.dll и другой-это 64-битная версия под названием MyDll64.файл DLL.
существует статическая переменная, содержащая имя файла DLL: string DLL_FILE_NAME.
и он используется следующим образом:
[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
private static extern int is_Func1(int var1, int var2);
просто до сих пор.
Как вы можете себе представить, программное обеспечение компилируется с включенным "любым процессором".
у меня тоже есть следующий код, чтобы определить, должна ли система использовать 64-битный файл или 32-битный файл.
#if WIN64
public const string DLL_FILE_NAME = "MyDll64.dll";
#else
public const string DLL_FILE_NAME = "MyDll32.dll";
#endif
теперь вы должны увидеть проблему.. DLL_FILE_NAME определяется во время компиляции, а не во время выполнения, поэтому правильная dll не загружается в соответствии с контекстом выполнения.
каков был бы правильный способ решения этой проблемы? Мне не нужны два исполнительных файла (один для 32bit, а другой для 64bit)? Как я могу установить DLL_FILE_NAME до он используется в заявлении DllImport?
7 ответов:
Я нашел самый простой способ сделать это, чтобы импортировать два метода с разными именами, и называть правильным. DLL не будет загружена до тех пор, пока вызов не будет сделан, так что все в порядке:
[DllImport("MyDll32.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)] private static extern int Func1_32(int var1, int var2); [DllImport("MyDll64.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)] private static extern int Func1_64(int var1, int var2); public static int Func1(int var1, int var2) { return IntPtr.Size == 8 /* 64bit */ ? Func1_64(var1, var2) : Func1_32(var1, var2); }
конечно, если у вас есть много импорта, это может стать довольно громоздким, чтобы поддерживать вручную.
вот еще одна альтернатива, которая требует, чтобы две библиотеки DLL имели то же имя и помещаются в разные папки. Например:
win32/MyDll.dll
win64/MyDll.dll
хитрость заключается в том, чтобы вручную загрузить DLL с
LoadLibrary
прежде чем CLR сделает это. Тогда он увидит, что aMyDll.dll
уже загружен и использовать его.это можно легко сделать в статическом конструкторе родителя класс.
static class MyDll { static MyDll() { var myPath = new Uri(typeof(MyDll).Assembly.CodeBase).LocalPath; var myFolder = Path.GetDirectoryName(myPath); var is64 = IntPtr.Size == 8; var subfolder = is64 ? "\win64\" : "\win32\"; LoadLibrary(myFolder + subfolder + "MyDll.dll"); } [DllImport("kernel32.dll")] private static extern IntPtr LoadLibrary(string dllToLoad); [DllImport("MyDll.dll")] public static extern int MyFunction(int var1, int var2); }
редактировать 2017/02/01 используйте
Assembly.CodeBase
так что это работает, даже если Теневое Копирование это.
в этом случае я должен сделать так (сделать 2 папки, x64 и x86 + поместить соответствующую 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(); } [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool SetDllDirectory(string path); }
существует статическая переменная, содержащая имя файла DLL
это не статическая переменная. Это константа, во время компиляции. Вы не можете изменить константу времени компиляции во время выполнения.
каков был бы правильный способ решения этой проблемы?
честно говоря, я бы рекомендовал просто нацелиться на x86 и забыть 64-разрядную версию вместе, и позволить вашему приложению работать на WOW64, если ваше приложение не имеет настоятельной необходимости запускать х64.
Если есть необходимость в x64, вы можете:
измените библиотеки DLL, чтобы иметь то же имя, например
MyDll.dll
, и во время установки / развертывания поставьте правильный на место. (Если ОС x64, разверните 64-разрядную версию DLL, в противном случае версию x86).есть две отдельные сборки в целом, один для x86 и один для x64.
то, что вы описываете, известно как "бок о бок сборка" (две версии одной и той же сборки, одна 32 и другая 64 бит)... Я думаю, что вы найдете эти полезные:
- использование параллельных сборок для загрузки x64 или x32 версии a DLL
- http://blogs.msdn.com/b/gauravseth/archive/2006/03/07/545104.aspx
- http://www.thescarms.com/dotnet/Assembly.aspx
здесь вы можете найти пошаговое руководство для вашего сценария (.NET DLL wrapping C++/CLI DLL ссылается на собственную DLL).
рекомендация:
просто построить его как x86 и сделать с ним... или есть 2 сборки (одна x86 и одна x64)... как и выше техника довольно сложная...
альтернативный подход может быть
public static class Sample { public Sample() { string StartupDirEndingWithSlash = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\"; string ResolvedDomainTimeFileName = StartupDirEndingWithSlash + "ABCLib_Resolved.dll"; if (!File.Exists(ResolvedDomainTimeFileName)) { if (Environment.Is64BitProcess) { if (File.Exists(StartupDirEndingWithSlash + "ABCLib_64.dll")) File.Copy(StartupDirEndingWithSlash + "ABCLib_64.dll", ResolvedDomainTimeFileName); } else { if (File.Exists(StartupDirEndingWithSlash + "ABCLib_32.dll")) File.Copy(StartupDirEndingWithSlash + "ABCLib_32.dll", ResolvedDomainTimeFileName); } } } [DllImport("ABCLib__Resolved.dll")] private static extern bool SomeFunctionName(ref int FT); }
Я использовал один из подходов, обозначенных vcsjones:
" измените библиотеки DLL, чтобы иметь то же имя, например MyDll.dll, и во время установки / развертывания, поместите правильный на место."
этот подход требует поддержания двух платформ сборки, хотя см. Эту ссылку для получения более подробной информации:https://stackoverflow.com/a/6446638/38368