在 C# DllImport 中使用 32 位或 64 位 dll
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10852634/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using a 32bit or 64bit dll in C# DllImport
提问by Gilad
Here is the situation, I'm using a C based dll in my dot.net application. There are 2 dlls, one is 32bit called MyDll32.dll and the other is a 64bit version called MyDll64.dll.
这是情况,我在我的 dot.net 应用程序中使用基于 C 的 dll。有2个dll,一个是32位版本的MyDll32.dll,另一个是64位版本的MyDll64.dll。
There is a static variable holding the DLL file name: string DLL_FILE_NAME.
有一个保存 DLL 文件名的静态变量:字符串 DLL_FILE_NAME。
and it is used in the following way:
它的使用方式如下:
[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
private static extern int is_Func1(int var1, int var2);
Simple so far.
简单到此为止。
As you can imagine, the software is compiled with "Any CPU" turned on.
可以想象,该软件是在打开“Any CPU”的情况下编译的。
I also have the following code to determine if the system should use the 64bit file or the 32bit file.
我还有以下代码来确定系统是应该使用 64 位文件还是 32 位文件。
#if WIN64
public const string DLL_FILE_NAME = "MyDll64.dll";
#else
public const string DLL_FILE_NAME = "MyDll32.dll";
#endif
By now you should see the problem.. DLL_FILE_NAME is defined in compilation time and not in execution time so the right dll isn't loaded according to the execution context.
现在您应该看到问题了。DLL_FILE_NAME 是在编译时而不是在执行时定义的,因此不会根据执行上下文加载正确的 dll。
What would be the correct way to deal with this issue? I do not want two execution files (one for 32bit and the other for 64bit)? How can I set DLL_FILE_NAME beforeit is used in the DllImport statement?
处理这个问题的正确方法是什么?我不想要两个执行文件(一个用于 32 位,另一个用于 64 位)?如何在 DllImport 语句中使用DLL_FILE_NAME之前设置它?
采纳答案by Julien Lebosquain
I've found the simplest way to do this is to import the two methods with different names, and calling the right one. The DLL won't be loaded until the call is made so it's fine:
我发现最简单的方法是导入具有不同名称的两种方法,并调用正确的方法。在调用之前不会加载 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);
}
Of course, if you have many imports, this can be become quite cumbersome to maintain manually.
当然,如果您有很多导入,手动维护会变得非常麻烦。
回答by vcsjones
There is a static variable holding the DLL file name
有一个静态变量保存 DLL 文件名
It is not a static variable. It's a constant, at compile time. You can't change a compile time constant at runtime.
它不是静态变量。它是一个常量,在编译时。您不能在运行时更改编译时常量。
What would be the correct way to deal with this issue?
处理这个问题的正确方法是什么?
Honestly I would recommend just targeting x86 and forgetting the 64-bit version all together, and letting your application run on WOW64, unless your application has a compelling need to run as x64.
老实说,我建议只针对 x86 而忘记 64 位版本,并让您的应用程序在 WOW64 上运行,除非您的应用程序迫切需要以 x64 运行。
If there is a need for x64, you could:
如果需要 x64,您可以:
Change the DLLs to have the same name, such as
MyDll.dll, and at install / deploy time, put the right one in place. (If the OS is x64, deploy the 64-bit version of the DLL, otherwise the x86 version).Have two separate builds altogether, one for x86 and one for x64.
将 DLL 更改为具有相同名称,例如
MyDll.dll,并在安装/部署时放置正确的 DLL 。(如果操作系统是 x64,则部署 64 位版本的 DLL,否则部署 x86 版本)。总共有两个单独的版本,一个用于 x86,一个用于 x64。
回答by Yahia
What you describe is known as "side-by-side assembly" (two versions of the same assembly, one 32 and the other 64 bit)... I think you will find these helpful:
您所描述的称为“并行程序集”(同一程序集的两个版本,一个是 32 位,另一个是 64 位)……我想您会发现这些很有帮助:
- Using Side-by-Side assemblies to load the x64 or x32 version of a DLL
- http://blogs.msdn.com/b/gauravseth/archive/2006/03/07/545104.aspx
- http://www.thescarms.com/dotnet/Assembly.aspx
- 使用并行程序集加载 x64 或 x32 版本的 DLL
- http://blogs.msdn.com/b/gauravseth/archive/2006/03/07/545104.aspx
- http://www.thescarms.com/dotnet/Assembly.aspx
Hereyou can find a walkthrough for exactly your scenario (.NET DLL wrapping C++/CLI DLL referencing a native DLL).
在这里,您可以找到针对您的场景的演练(.NET DLL 包装 C++/CLI DLL 引用本机 DLL)。
RECOMMENDATION:
推荐:
Just build it as x86 and be done with it... or have 2 builds (one x86 and one x64)... as the above techniques are rather complicated...
只需将其构建为 x86 并完成它...或者有 2 个构建(一个 x86 和一个 x64)...因为上述技术相当复杂...
回答by Danny Varod
I have used one of the approaches meantioned by vcsjones:
我使用了 vcsjones 所指的方法之一:
"Change the DLLs to have the same name, such as MyDll.dll, and at install / deploy time, put the right one in place."
“将 DLL 更改为具有相同名称,例如 MyDll.dll,并在安装/部署时将正确的放在适当的位置。”
This approach requires maintaining two build platforms though see this link for more details: https://stackoverflow.com/a/6446638/38368
这种方法需要维护两个构建平台,但请参阅此链接了解更多详细信息:https: //stackoverflow.com/a/6446638/38368
回答by Benoit Blanchon
Here is another alternative that requires that the two DLLs have the same nameand are placed in different folders. For instance:
这是另一种替代方法,它要求两个 DLL 具有相同的名称并放置在不同的文件夹中。例如:
win32/MyDll.dllwin64/MyDll.dll
win32/MyDll.dllwin64/MyDll.dll
The trick is to manually load the DLL with LoadLibrarybefore the CLR does it. It will then see that a MyDll.dllis already loaded and use it.
诀窍是LoadLibrary在 CLR 执行之前手动加载 DLL 。然后它会看到 aMyDll.dll已经加载并使用它。
This can be done easily in the static constructor of the parent class.
这可以在父类的静态构造函数中轻松完成。
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);
}
EDIT 2017/02/01: Use Assembly.CodeBaseso that it works even if Shadow Copyingis enabled.
编辑 2017/02/01:使用Assembly.CodeBase它,即使启用了阴影复制也能正常工作。
回答by Kisded Szabi - CodeRevolution
In this case, i should do like this (make 2 folders, x64 and x86 + put the corresponding dll, WITH THE SAME NAME, in both folders):
在这种情况下,我应该这样做(创建 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);
}
回答by user2381106
an alternative approach may be
另一种方法可能是
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);
}
回答by James Wilkins
The trick I use for V8.Netis this:
我用于V8.Net的技巧是:
- Create a new C# "proxy interface" project with all the defines to switch between the different architectures. In my case the project was named
V8.Net-ProxyInterface; example:
- 创建一个新的 C#“代理接口”项目,其中包含在不同架构之间切换的所有定义。就我而言,该项目名为
V8.Net-ProxyInterface; 例子:
public unsafe static class V8NetProxy
{
#if x86
[DllImport("V8_Net_Proxy_x86")]
#elif x64
[DllImport("V8_Net_Proxy_x64")]
#else
[DllImport("V8_Net_Proxy")] // (dummy - NOT USED!)
#endif
public static extern NativeV8EngineProxy* CreateV8EngineProxy(bool enableDebugging, void* debugMessageDispatcher, int debugPort);
THIS is the project you will reference. DO NOT reference the next two:
这是您将参考的项目。不要引用接下来的两个:
Create two more projects to generate x64 and x86 versions of the library. This is VERY EASY: Just copy-n-paste to duplicate the
.csprojfile in the same folder and renamed them. In my case the project file was renamed toV8.Net-ProxyInterface-x64andV8.Net-ProxyInterface-x86, then I added the projects to my solution. Open the project settings for each of them in Visual Studio and make sure theAssembly Namehas either x64 or x86 in the name. At this point you have 3 projects: the first "placeholder" project, and the 2 architecture-specific ones. For the 2 new projects:a) Open the x64 interface project settings, go to the
Buildtab, selectAll PlatformsforPlatformat the top, then enterx64inConditional compilation symbols.b) Open the x86 interface project settings, go to the
Buildtab, selectAll PlatformsforPlatformat the top, then enterx86inConditional compilation symbols.Open
Build->Configuration Manager...and make sure thatx64is selected as the platform for x64 projects, andx86is selected for the x86 projects, for BOTHDebugANDReleaseconfigurations.Make sure the 2 new interface projects (for x64 and x86) output to the same location of your host project (see project setting
Build->Output path).The final magic: In a static constructor for my engine I quickly attach to the assembly resolver:
再创建两个项目以生成库的 x64 和 x86 版本。这非常简单:只需复制粘贴即可将
.csproj文件复制到同一文件夹中并重命名。在我的情况下,项目文件被重命名为V8.Net-ProxyInterface-x64andV8.Net-ProxyInterface-x86,然后我将项目添加到我的解决方案中。在 Visual Studio 中打开每个项目的项目设置,并确保名称中Assembly Name包含 x64 或 x86。此时您有 3 个项目:第一个“占位符”项目和 2 个特定于体系结构的项目。对于 2 个新项目:a)打开64接口项目设置,转到
Build选项卡,选择All Platforms了Platform在最上面,然后输入x64在Conditional compilation symbols。B)打开了x86接口项目设置,转到
Build选项卡,选择All Platforms了Platform在最上面,然后输入x86在Conditional compilation symbols。打开
Build->Configuration Manager...并确保x64为 x64 项目选择了平台,并x86为 x86 项目选择了 BOTHDebugANDRelease配置。确保 2 个新接口项目(用于 x64 和 x86)输出到宿主项目的相同位置(请参阅项目设置
Build->Output path)。最后的魔法:在我引擎的静态构造函数中,我快速附加到程序集解析器:
static V8Engine()
{
AppDomain.CurrentDomain.AssemblyResolve += Resolver;
}
In the Resolvermethod, I just load the file based on the current platform indicated by the current process (note: this code is a stripped-down version and not tested):
在Resolver方法中,我只是根据当前进程所指示的当前平台加载文件(注意:此代码为精简版,未测试):
var currentExecPath = Assembly.GetExecutingAssembly().Location;
var platform = Environment.Is64BitProcess ? "x64" : "x86";
var filename = "V8.Net.Proxy.Interface." + platform + ".dll"
return Assembly.LoadFrom(Path.Combine(currentExecPath , fileName));
Finally, go to your host project in the solution explorer, expand References, select the first dummy project you created in step 1, right-click it to open the properties, and set Copy Localto false. This allows you to develop with ONE name for each P/Invoke function, while using the resolver to figure out which one to actually load.
最后,在解决方案资源管理器中转到您的宿主项目,展开References,选择您在步骤 1 中创建的第一个虚拟项目,右键单击它打开属性,然后设置Copy Local为false。这允许您为每个 P/Invoke 函数开发一个名称,同时使用解析器确定实际加载哪个名称。
Note that the assembly loader only runs when needed. It is only triggered (in my case) automatically by the CLR system upon the first access to the engine class. How that translates to you depends on how your host project is designed.
请注意,程序集加载器仅在需要时运行。它仅在第一次访问引擎类时由 CLR 系统自动触发(在我的情况下)。这如何转化为您取决于您的宿主项目的设计方式。

