在C#DllImport中使用32位或64位dll
在这种情况下,我在我的dot.net应用程序中使用了一个基于C的dll。有两个dll,一个是32位的MyDll32.dll,另一个是64位的MyDll64.dll 有一个静态变量保存DLL文件名:string DLL\u file\u name 其使用方式如下:在C#DllImport中使用32位或64位dll,c#,.net,pinvoke,32bit-64bit,dllimport,C#,.net,Pinvoke,32bit 64bit,Dllimport,在这种情况下,我在我的dot.net应用程序中使用了一个基于C的dll。有两个dll,一个是32位的MyDll32.dll,另一个是64位的MyDll64.dll 有一个静态变量保存DLL文件名:string DLL\u file\u name 其使用方式如下: [DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")] private static extern int is_
[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
private static extern int is_Func1(int var1, int var2);
到目前为止很简单
正如您所想象的,软件是在“任何CPU”打开的情况下编译的
我还有以下代码来确定系统应该使用64位文件还是32位文件
#if WIN64
public const string DLL_FILE_NAME = "MyDll64.dll";
#else
public const string DLL_FILE_NAME = "MyDll32.dll";
#endif
现在你应该看到问题了。。DLL_文件_名称是在编译时定义的,而不是在执行时定义的,因此不会根据执行上下文加载正确的DLL
处理这个问题的正确方法是什么?我不想要两个执行文件(一个用于32位,另一个用于64位)?在DllImport语句中使用DLL\U文件之前,如何设置其名称
有一个静态变量保存DLL文件名
它不是一个静态变量。在编译时,它是一个常量。不能在运行时更改编译时常量
处理这个问题的正确方法是什么
老实说,我建议只针对x86,同时忘记64位版本,让您的应用程序在WOW64上运行,除非您的应用程序迫切需要以x64运行
如果需要x64,您可以:
- 将dll更改为具有相同的名称,例如
,并在安装/部署时将正确的dll放置到位。(如果操作系统是x64,则部署64位版本的DLL,否则部署x86版本)MyDll.dll
- 总共有两个独立的版本,一个用于x86,一个用于x64
只需将其构建为x86并使用它即可。。。或者有2个版本(一个x86和一个x64)。。。由于上述技术相当复杂…我发现最简单的方法是导入两个名称不同的方法,然后调用正确的方法。在调用之前,不会加载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);
}
当然,如果您有许多导入,手动维护会变得相当麻烦。我使用了vcsjones所指的方法之一: 将dll更改为具有相同的名称,如MyDll.dll,并在安装/部署时将正确的dll放置到位
此方法需要维护两个构建平台,但有关更多详细信息,请参见此链接:这里是另一个备选方案,要求两个DLL具有相同的名称,并放置在不同的文件夹中。例如:
win32/MyDll.dll
win64/MyDll.dll
LoadLibrary
手动加载DLL。然后它会看到一个MyDll.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
,这样即使启用它也可以工作。在这种情况下,我应该这样做(创建两个文件夹,x64和x86+在两个文件夹中放置同名的对应dll):
另一种方法可能是
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);
}
我使用的技巧是:
V8.NETProxyInterface
;例如:.csproj
文件复制到同一文件夹中,并重命名它们。在我的例子中,项目文件被重命名为V8.Net-ProxyInterface-x64
和V8.Net-ProxyInterface-x86
,然后我将项目添加到我的解决方案中。在Visual Studio中打开每个程序集的项目设置,并确保程序集名称中包含x64或x86。此时您有3个项目:第一个“占位符”项目和2个特定于体系结构的项目。对于2个新项目:
a) 打开x64接口项目设置,转到构建
选项卡,在顶部为平台
选择所有平台
,然后在条件编译符号
中输入x64
b) 打开x86接口项目设置,转到Build
选项卡,在顶部为Platform
选择All Platforms
,然后在条件编译符号中输入x86
Build->Configuration Manager…
并确保选择了x64
作为x64项目的平台,并且选择了x86
作为x86项目的平台,用于Debug
和Release
配置Build->output path
)Resolver
方法中,我只是根据当前进程指示的当前平台加载文件(注意:此代码是精简版本,未经测试):
最后,在解决方案资源管理器中转到宿主项目,展开引用
,选择在步骤1中创建的第一个虚拟项目,右键单击它以打开属性,即
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);
}
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);
static V8Engine()
{
AppDomain.CurrentDomain.AssemblyResolve += 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));
#if X64
[DllImport("MyDll64.dll", CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
#else
[DllImport("MyDll32.dll", CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
#endif
private static extern int is_Func1(int var1, int var2);