zoukankan      html  css  js  c++  java
  • 2019-8-30-C#-如何在项目引用x86-x64的非托管代码

    title author date CreateTime categories
    C# 如何在项目引用x86 x64的非托管代码
    lindexi
    2019-08-30 08:53:52 +0800
    2018-03-03 20:43:34 +0800
    C#

    因为现在的项目使用的是 AnyCpu 在 x86 的设备使用的是x86,在x64使用的是x64,但是对于非托管代码,必须要在x64使用x64的dll,在x86使用x86的dll。在C++没有和C#一样的 AnyCpu 所以需要在项目运行在x86的时候加载x86的dll。 本文告诉大家如何在代码引用不同的dll。

    使用宏

    最简单的方法是编译两个版本,编译多个版本可以点击配置管理器,然后创建x86和x64,然后版本添加宏,这样就可以判断宏来使用不同的dll

    点击活动解决方案平台,然后点击新建

    选择项目属性,点击生成,就可以添加不同的宏

    于是在后台代码可以这样写

    #if x86
            public const string DLL_FILE_NAME = "SvkiqauhKvdhrureh32.dll";
    #else
            public const string DLL_FILE_NAME = "SvkiqauhKvdhrureh64.dll";
    #endif
    
            [DllImport(DLL_FILE_NAME, EntryPoint = "HfwzsnHzhpbbzbn", CallingConvention = CallingConvention.Cdecl)]
            private static extern int HfwzsnHzhpbbzbn(int var1, int var2);
    

    可以看到这个方法如果有很多个 dll 那么需要写很多路径

    多个函数

    实际上如果已经有两个dll ,那么可以使用两个不同函数

            [DllImport("SvkiqauhKvdhrureh32.dll", EntryPoint = "HfwzsnHzhpbbzbn",
                CallingConvention = CallingConvention.Cdecl)]
            private static extern int HfwzsnHzhpbbzbn32(int txcuiwKjvwu, int hhmzfadnHexkmr);
    
            [DllImport("SvkiqauhKvdhrureh64.dll", EntryPoint = "HfwzsnHzhpbbzbn",
                CallingConvention = CallingConvention.Cdecl)]
            private static extern int HfwzsnHzhpbbzbn64(int txcuiwKjvwu, int hhmzfadnHexkmr);

    然后再写一个函数

           public int HfwzsnHzhpbbzbn(int txcuiwKjvwu, int hhmzfadnHexkmr)
            {
                if (Environment.Is64BitProcess)
                {
                    return HfwzsnHzhpbbzbn64(txcuiwKjvwu, hhmzfadnHexkmr);
                }
    
                return HfwzsnHzhpbbzbn32(txcuiwKjvwu, hhmzfadnHexkmr);
            }

    这样就不需要在使用的时候判断当前使用的是哪个,但是如果dll多了,一个dll都需要写三次,看起来代码还是很烂

    设置查找的文件

    实际上好多人都觉得,应用程序首先是从运行的目录开始查找dll,如果找不到,就去GAC查找,如果还是找不到,就去System查找。实际上这句话是不对的,在没有设置默认查找的文件的时候就是这样,但是软件是可以设置查找文件。

    设置的方法使用使用这个dll,请看下面

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool SetDllDirectory(string path);

    需要先把 x86 的 dll 放在程序的 x86文件夹,当然对于x64的大家也知道放哪里。

    然后在程序运行的时候使用下面的代码

           var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
              path = Path.Combine(path, Environment.Is64BitProcess ? "x64" : "x86");
              SetDllDirectory(path);

    这样就可以直接写一个函数,最后的方法是我推荐的。

    但是存在一些特殊的文件,他不能放在x86文件夹,所以就需要使用下面的代码特别加载

       [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibrary(string dllToLoad);
    
        LoadLibrary("../SdarTfqzok.dll");

    如果使用了 LoadLibrary 相对是比较复杂的做法,因为需要手动创建委托的方式。但是用 LoadLibrary 的好处是可以进行释放。

    先创建一个类用来存在辅助的方法,请看代码

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern IntPtr LoadLibrary(string dllToLoad);
    
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
    
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool FreeLibrary(IntPtr hModule);

    首先通过 LoadLibrary 加载 dll 请看下面代码

      var ptr = LoadLibrary("../SdarTfqzok.dll");
    

    这样就可以拿到 dll 的指针,通过 GetProcAddress 可以拿到方法的指针

    // 这里 C++ 的方法是 int HfwzsnHzhpbbzbn(int txcuiwKjvwu, int hhmzfadnHexkmr)
    // 方法名就是 HfwzsnHzhpbbzbn 通过方法名找到地址
    IntPtr pAddressOfFunctionToCall = GetProcAddress(pDll, "HfwzsnHzhpbbzbn32");

    只是拿到了以为方法的指针是比较难调用的,所以就需要将方法指针转换

    需要创建一个委托,签名和 dll 里的方法一样

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate int HfwzsnHzhpbbzbn(int txcuiwKjvwu, int hhmzfadnHexkmr);
    

    注意这个委托需要标记UnmanagedFunctionPointer才可以调用

    通过 Marshal.GetDelegateForFunctionPointer 可以转函数指针为对应的类

     HfwzsnHzhpbbzbn hfwzsnHzhpbbzbn = (HfwzsnHzhpbbzbn)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(HfwzsnHzhpbbzbn));

    这样就可以直接调用 C++ 的方法了,使用下面的代码调用

     var n = hfwzsnHzhpbbzbn(1, 2);

    遇过遇到了 LoadLibrary 返回的 ptr 是 0 那么需要调用下面代码

        var ptr = LoadLibrary("../SdarTfqzok.dll");
    
        if(ptr == IntPtr.Zero )
        {
           var foo = Marshal.GetLastWin32Error().ToString();
        }

    通过 foo 的值在 System Error Codes (0-499) 就可以找到原因

    需要注意,使用 GetLastWin32Error 必须设置 DllImport("xx.dll", SetLastError = true, CharSet = CharSet.Unicode) 才可以,当然最后的CharSet = CharSet.Unicode不是一定需要

    查看了项目的代码才看到,实际上还有一个方法,就是在运行的时候,如果当前运行的是x86的,就从x86文件夹复制dll出来,这个方法是速度最慢的。

  • 相关阅读:
    null in ABAP and nullpointer in Java
    SAP ABAP SM50事务码和Hybris Commerce的线程管理器
    Hybris service layer和SAP CRM WebClient UI架构的横向比较
    SAP ABAP和Linux系统里如何检查网络传输的数据量
    SAP CRM WebClient UI和Hybris的controller是如何被调用的
    SAP CRM和Cloud for Customer订单中的业务伙伴的自动决定机制
    SAP CRM WebClient UI和Hybris CommerceUI tag的渲染逻辑
    SAP BSP和JSP页面里UI元素的ID生成逻辑
    微信jsapi支付
    微信jsapi退款操作
  • 原文地址:https://www.cnblogs.com/lindexi/p/12085762.html
Copyright © 2011-2022 走看看