我有一个以“Any CPU”为目标平台构建的应用程序,并且有两个针对x86和x64的相同库的第三方DLL。 我想在客户端机器上根据平台在运行时包含其中一个库。最佳方法是什么?
我有一个以“Any CPU”为目标平台构建的应用程序,并且有两个针对x86和x64的相同库的第三方DLL。 我想在客户端机器上根据平台在运行时包含其中一个库。最佳方法是什么?
如果我们谈论未经管理的DLL,那么声明p/invokes应该像这样:
[DllImport("DllName.dll")]
static extern foo();
请注意,我们并未指定DLL的路径,只是指定了其名称,我认为32位和64位版本的名称应该是相同的。
在调用任何P/Invoke函数之前,先将库加载到进程中。可以通过P/Invoke调用LoadLibrary
API函数来实现。此时,您将确定您的进程是32位还是64位,并相应地构建DLL的完整路径。这个完整的路径就是您传递给LoadLibrary
的参数。
现在,当您调用库的p/invokes函数时,它们将被加载的模块解析。
对于托管程序集,您可以使用Assembly.LoadFile
指定程序集的路径。这可能有点棘手,但这篇优秀的文章向您展示了如何实现:Automatically Choose 32 or 64 Bit Mixed Mode DLLs。与混合模式和本机DLL依赖项相关的细节可能与您无关。关键是使用AppDomain.CurrentDomain.AssemblyResolve
事件处理程序。
DllImport
? - Scott Chamberlain我在这个话题上有一些经验,所以我想根据我在Pencil.Gaming中使用的方法来回答。首先,你需要从32位dll和64位dll(或者其他平台使用的dylib等)中"DllImport
"两个函数。
static class Foo32 {
[DllImport("32bitdll.dll")]
internal static extern void Foo();
}
static class Foo64 {
[DllImport("64bitdll.dll")]
internal static extern void Foo();
}
那么您需要一个包含委托的中间类,并根据 IntPtr
的大小从32位或64位Interop导入它们(我不使用 Environment.Is64BitProcess
,因为那是.NET 4函数):
internal delegate void FooDelegate();
static class FooDelegates {
internal static FooDelegate Foo;
static FooDelegates() {
Type interop = (IntPtr.Size == 8) ? typeof(Foo64) : typeof(Foo32);
FieldInfo[] fields = typeof(FooDelegates).GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
foreach (FieldInfo fi in fields) {
MethodInfo mi = glfwInterop.GetMethod(fi.Name, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
Delegate function = Delegate.CreateDelegate(fi.FieldType, mi);
fi.SetValue(null, function);
}
}
}
然后,我通常使用一个“真实”的类,其中包含您导入的函数(尽管这在技术上并非必需):
public static class FooApi {
public static void Foo() {
FooDelegates.Foo();
}
}
DllImport
来导入所有函数完成整个操作。 - David Heffernan我的问题的完整解决方案是使用David Heffernan提供的第二个链接。 我所做的是 1. 在项目中引用一个虚拟dll。 2. 指定两个预构建事件。
xcopy /y "$(SolutionDir)\Assemblies\Lib\x86\(Assembly name)*" "$(TargetDir)"
xcopy /y "$(SolutionDir)\Assemblies\Lib\x64\(Assemble name)*" "$(TargetDir)"
3. 在应用程序启动时,在程序集解析事件中更改相应的程序集,具体取决于平台。
var currentDomain = AppDomain.CurrentDomain;
var location = Assembly.GetExecutingAssembly().Location;
var assemblyDir = Path.GetDirectoryName(location);
if (assemblyDir != null && (File.Exists(Path.Combine(assemblyDir, "(Assembly name).proxy.dll"))
|| !File.Exists(Path.Combine(assemblyDir, "(Assembly name).x86.dll"))
|| !File.Exists(Path.Combine(assemblyDir, "(Assembly name).x64.dll"))))
{
throw new InvalidOperationException("Found (Assembly name).proxy.dll which cannot exist. "
+ "Must instead have (Assembly name).x86.dll and (Assembly name).x64.dll. Check your build settings.");
}
currentDomain.AssemblyResolve += (sender, arg) =>
{
if (arg.Name.StartsWith("(Assembly name),", StringComparison.OrdinalIgnoreCase))
{
string fileName = Path.Combine(assemblyDir,
string.Format("(Assembly).{0}.dll", (IntPtr.Size == 4) ? "x86" : "x64"));
return Assembly.LoadFile(fileName);
}
return null;
};