C#里调用非托管的Dll

今天花了一些精力来调查了一下C#里调用非托管的Dll,C#里调用非托管的Dll要使用P/Invoke平台调用技术, 这里先简单介绍一下P/Invoke平台调用技术。
    由于开发程序转到托管代码,所以开发过程中会经常研究底层的一些关键功能,通过 P/Invoke(平台调用)即 公共语言运行库 (CLR) 的 interop 功能,
来进行底层或者其他平台dll的调用。

C#语言声明外部方法,基本形式是:
[DLLImport(“DLL文件”,……)]
修饰符 extern 返回变量类型 方法名称 (参数列表)

DLL文件:包含定义外部方法的库文件。

修饰符: 访问修饰符,除了abstract以外在声明方法时可以使用的修饰符。

extern:extern 修饰符用于声明在外部实现的方法,常见用法是在使用 Interop 服务调入非托管代码时与 DllImport 属性一起使用

返回变量类型:在DLL文件中你需调用方法的返回变量类型。

方法名称:在DLL文件中你需调用方法的名称。

参数列表:在DLL文件中你需调用方法的列表。

C# 的规则之一是:
它的调用语法只能访问 CLR 数据类型,例如 System.UInt32 和 System.Boolean。
C# 显然不识别 Windows API 中使用的基于 C 的数据类型(例如 UINT 和 BOOL),这些类型只是 C 语言类型的类型定义而已。所以当 Windows API 函数如果按以下方式编写时
BOOL MessageBeep( UINT uType )
外部方法就必须使用 CLR 类型来定义,如前面的代码片段中所看到的。
需要使用与基础 API 函数类型不同但与之兼容的 CLR 类型是 P/Invoke 较难使用的一个方面。(数据封送处理)

可选的 DllImportAttribute 属性
除了指出宿主 DLL 外,DllImportAttribute 还包含了一些可选属性,
包括:EntryPointCharSetSetLastError CallingConvention

EntryPoint
在不希望外部托管方法具有与 DLL 导出相同的名称的情况下,可以设置该属性来指示导出的 DLL 函数的入口点名称。
当定义两个调用相同非托管函数的外部方法时,这特别有用。
另外,在 Windows 中还可以通过它们的序号值绑定到导出的 DLL 函数。
如果需要这样做,则诸如“#1”或“#129”的 EntryPoint 值指示 DLL 中非托管函数的序号值而不是函数名,
( 通常这个很少有相同名称)

ExactSpelling

指示 EntryPoint 是否必须与指示的入口点的拼写完全匹配,如:ExactSpelling=false;

CharSet

指示用在入口点中的字符集,如:CharSet=CharSet.Ansi;
如果没有显式地设置 CharSet 属性,则其默认值为 CharSet.Ansi。这个默认值是有缺点的,
因为对于在 Windows 2000、Windows XP 和 Windows NT 上进行的 interop 调用,它会消极地影响文本参数封送处理的性能。
如果CharSet 属性设置为 CharSet.Auto。这样可以使 CLR 根据宿主 OS 使用适当的字符集。
这个同时根据操作系统具体设置 例如:基于 Windows NT 的操作系统中,并且只支持 Unicode ,所以我们就要设置为 CharSet.Unicode。

SetLastError

指示方法是否保留 Win32"上一错误",如:SetLastError=true;
如果使用 GetLastError 来查找扩展的错误信息,
则应该在外部方法的 DllImportAttribute 中将 SetLastError 属性设置为 true
这将导致 CLR 在每次调用外部方法之后缓存由 API 函数设置的错误
在包装方法中,
可以通过调用类库的 System.Runtime.InteropServices.Marshal 类型中定义的 Marshal.GetLastWin32Error 方法来获取缓存的错误值

PreserveSig

指示方法的签名应当被保留还是被转换, 如:PreserveSig=true;

CallingConvention

指示入口点的调用约定, 如:CallingConvention=CallingConvention.Winapi;
通过此属性,可以给 CLR 指示应该将哪种函数调用约定用于堆栈中的参数。
CallingConvention.Winapi 的默认值是最好的选择,它在大多数情况下都可行
在 c,c++运行时 DLL 函数和少数函数中,可能需要将约定更改为 CallingConvention.Cdecl。

“数据封送处理”及“封送数字和逻辑标量”

这个我在学习当中,如果有兴趣的话,可以更加深入。

下面举了一个简单的 P/Invoke 示例

c# 调用 c++  .dll 】
首先需要添加using System.Runtime.InteropServices; //交互服务的命名空间
静态加载.dll
   1. 首先把被加载.dll拷贝到运行目标bin/debug目录下(如果路径要求不被限制,就可以选择动态加载)
   2.  声明调用方法名
   [DllImport("xxxx.dll", EntryPoint = "xxx方法名xxx", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
   public static extern int OperatePlus(int a, int b); 
   3.执行调用
       static void Main(string[] args)
       {
           Console.WriteLine(OperatePlus(100, 102));
           Console.Read();
       }
   4.静态调用成功
动态加载.dll
    1. 
        声明调用kernel32.dll中调用方法
        /// <summary>
        /// 装载动态库
        /// </summary>
        /// <param name="lpLibFileName">DLL 文件名</param>
        /// <returns>函数库模块的句柄 </returns>
        [DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
        public static extern int LoadLibrary(
            [MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);

/// <summary>
        /// 获取要引入的函数,将符号名或标识号转换为DLL内部地址。
        /// </summary>
        /// <param name="hModule">包含需调用函数的函数库模块的句柄</param>
        /// <param name="lpProcName">调用函数的名称</param>
        /// <returns>函数指针</returns>
        [DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
        public static extern IntPtr GetProcAddress(int hModule,
            [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

/// <summary>
        /// 释放动态链接库。
        /// </summary>
        /// <param name="hModule">需释放的函数库模块的句柄</param>
        /// <returns>是否已释放指定的 Dll</returns>
        [DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
        public static extern bool FreeLibrary(int hModule);
      
    2. 声明委托

   /// <summary>
        ///函数指针封装成委托
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] //控制作为非托管函数指针传入或传出非托管代码的委托签名的封送行为
        delegate int OperatePlus(int a, int b);
        
   3. 调用声明的方法
        static void Main(string[] args)
        {
            int hModule = LoadLibrary(@"path\xxx.dll");
            if (hModule == 0)
                return;
            IntPtr intPtr = GetProcAddress(hModule, "xxx方法名xxx");
            //xxx委托名xxx OperatePlusFunction = (xxx委托名xxx )Marshal.GetDelegateForFunctionPointer(intPtr, typeof(xxx委托名xxx ));//函数指针封装成委托
            OperatePlus OperatePlusFunction = (OperatePlus)Marshal.GetDelegateForFunctionPointer(intPtr, typeof(OperatePlus));//函数指针封装成委托
            Console.WriteLine(OperatePlusFunction(2, 2));
            Console.Read();
        }
       
  同样C#中调用Delphi.dll c.dll 等这些操作方式应该是一样。

C#引用非托管.dll的更多相关文章

  1. 将WinForm程序(含多个非托管Dll)合并成一个exe的方法

    原文:将WinForm程序(含多个非托管Dll)合并成一个exe的方法 开发程序的时候经常会引用一些第三方的DLL,然后编译生成的exe文件就不能脱离这些DLL独立运行了. ILMerge能把托管dl ...

  2. 托管DLL和非托管DLL的区别

    首先解释一下,托管DLL和非托管DLL的区别.狭义解释讲,托管DLL就在Dotnet环境生成的DLL文件.非托管DLL不是在Dotnet环 境生成的DLL文件. 托管DLL文件,可以在Dotnet环境 ...

  3. ASP.NET与非托管DLL的那些事儿【转+增】

    https://www.cnblogs.com/yeahgis/archive/2011/11/12/2246341.html ASP.NET与非托管DLL的那些事儿 环境VS2010 语言:ISO ...

  4. C#调用非托管dll

    以C#开发周立功CAN举例,在官网下载了周立功的demo 一.C++头文件样子 //接口卡类型定义#define VCI_PCI5121 1 //一些结构体定义 typedef struct tagR ...

  5. 托管程序调用非托管dll问题总结

    托管程序Visual Basic.net, 非托管DLL标准C++程序(使用VC++编译) 函数调用定义 第一种写法: <DllImportAttribute("XXX.dll&quo ...

  6. 托管非托管Dll动态调用

    原文:托管非托管Dll动态调用 最近经常看到有人问托管非托管Dll调用的问题.对于动态库的调用其实很简单.网上很多代码都实现了Dll的静态调用方法.我主要谈论下动态库的动态加载. 对于托管动态库,实现 ...

  7. .Net 程序在自定义位置查找托管/非托管 dll 的几种方法

    原文:.Net 程序在自定义位置查找托管/非托管 dll 的几种方法 一.自定义托管 dll 程序集的查找位置 目前(.Net4.7)能用的有2种: #define DEFAULT_IMPLEMENT ...

  8. 关于C#调用非托管DLL,报“内存已损坏的”坑,坑,坑

    因客户需求,与第三方对接,调用非托管DLL,之前正常对接的程序,却总是报“内存已损坏的异常”,程序进程直接死掉,折腾到这个点(2018-05-11 00:26),终于尘埃落定,直接上程序. 之前的程序 ...

  9. C#如何加载嵌入到资源的非托管dll

    如何加载非托管Dll 我们总会遇到需要加载非Win32的非托管dll,这里推荐一种方式就是将那些非win32的非托管dll嵌入资源的方式,在入口解压并且加载的方式,我先来看看如何实现吧,首先我们准备好 ...

随机推荐

  1. rownum(转载)

    对于 Oracle 的 rownum 问题,很多资料都说不支持>,>=,=,between...and,只能用以上符号(<.<=.!=),并非说用>,>=,=,be ...

  2. Unity5UGUI 官方教程学习笔记(二)Rect Transform

    Rect Transform Posx    Posy   Posz  :  ui相对于父级的位置 Anchors :锚点  定义了与父体之间的位置关系    一个锚点由四个锚组成  四个锚分别代表了 ...

  3. centos 安装mysql密码修改后还是不能连接的原因

    centos 上安装mysql密码修改后还是不能连接出现错误:ERROR 1142 (42000): SELECT command denied to user ''@'localhost' for ...

  4. PHP中include和require的区别

    include和require的区别,其实两者没有太大的区别,如果要包含的文件不存在,include提示notice,然后继续执行下面的语句,require提示致命错误并且退出. 根据测试,win32 ...

  5. 如何将mongodb bson文件转成json文件

    使用mongodb自带的命令 bsondump collection.bson > collection.json

  6. Linux 网络编程基础(2)-- 获取主机信息

    前一篇已经介绍了最基本的网络数据结构.这篇介绍一下获取主机信息的函数 举个例子,想要通过代码的方式从百度获取当前的时间,怎么做?我们不知道百度的IP地址啊,这代码怎么写?还好,Linux提供了一些AP ...

  7. canvas 基础知识

    canvas 基础 低版本的ie不支持html5,需要引入excanvas.js来让ie支持canvas. 检测支持canvas <canvas id="canvas" wi ...

  8. linux系统巡检脚本shell实例

    #!/bin/sh BACKUP_TIMESTAMP=`date +%Y%m%d` HOSTNAME=`hostname` num=89 ###################核查文件系统opt### ...

  9. openStack 王者归来之 trivial matters

    <一,openStack img 制作> tips:制作大部分cloud platforms img准备工作. <1,> http://www.pubyun.com/blog/ ...

  10. 交换机VLAN研究

    这两天在研究openWRT的网络接口问题,涉及到了交换机的一些概念,主要是跟VLAN相关的,在此总结一下. VLAN在802.11Q中定义,802.11Q帧格式如下图所示: 交换机示意图如下图所示: ...