Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C#DllImport中使用32位或64位dll_C#_.net_Pinvoke_32bit 64bit_Dllimport - Fatal编程技术网

在C#DllImport中使用32位或64位dll

在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_

在这种情况下,我在我的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_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更改为具有相同的名称,例如
    MyDll.dll
    ,并在安装/部署时将正确的dll放置到位。(如果操作系统是x64,则部署64位版本的DLL,否则部署x86版本)

  • 总共有两个独立的版本,一个用于x86,一个用于x64


您所描述的被称为“并行程序集”(同一程序集的两个版本,一个32位,另一个64位)。。。我想你会发现这些很有帮助:

您可以找到完全适合您的场景的演练(.NET DLL包装C++/CLI DLL引用本机DLL)

建议:


只需将其构建为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
诀窍是在CLR执行之前,使用
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);
}
我使用的技巧是:

  • 创建一个新的C#“proxy interface”项目,其中包含所有用于在不同体系结构之间切换的定义。在我的例子中,项目名为
    V8.NETProxyInterface
    ;例如:
  • 这是您将参考的项目。不要引用下两个:

  • 再创建两个项目以生成库的x64和x86版本。这很简单:只需复制n粘贴即可将
    .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
    配置

  • 确保将2个新接口项目(用于x64和x86)输出到宿主项目的相同位置(请参见项目设置
    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);