zoukankan      html  css  js  c++  java
  • 如何使用C#调用非托管DLL函数


    由于工作需要,学习了GDI+编程的一些知识。其中看到了一个比较好的Demo,深入的了解后,却发现自己对如何用C#调用非托管DLL函数也有了更好的理解,于是整理了一下,跟大家一起分享。


    引用: 用C#来捕获屏幕的源程序代码(Capture.cs)

    C#捕获当前屏幕的例子

    上面的例子中,应用C#调用非托管DLL的函数如下:

    //声明一个API函数 
     [ System.Runtime.InteropServices.DllImportAttribute ( "gdi32.dll" ) ] 
     
    private static extern bool BitBlt ( 
      IntPtr hdcDest , 
    // 目标 DC的句柄 
      int nXDest , 
      
    int nYDest , 
      
    int nWidth , 
      
    int nHeight , 
      IntPtr hdcSrc , 
    // 源DC的句柄 
      int nXSrc , 
      
    int nYSrc , 
      System.Int32 dwRop 
    // 光栅的处理数值 
      ) ;


    在上面这段代码中,我将以分别介绍 DllImportAttribute属性、extern关键字 、IntPtr类型 这三个方面,向大家介绍如何应用C#调用非托管DLL函数。


    C# 如何使用 DllImport Attribute(属性) 标识 DLL 和函数

    System.Runtime.InteropServices.DllImportAttribute

    从托管代码中访问非托管 DLL 函数之前,需要知道该函数的名称以及该 DLL 的名称,然后为 DLL 的非托管函数 编写 托管定义。

    它将用到 static 和 extern 修饰符,此类型的公共静态成员对于多线程操作是安全的。

    DllImport 属性提供非托管 DLL 函数的调用信息。


    示例1  简单DllImportAttribute 应用

    using System.Runtime.InteropServices;
    [DllImport(
    "user32.dll")]     
    public static extern int MessageBox(int hWnd, String text, String caption, uint type);

    示例2  如何将 DllImportAttribute 应用于方法。

    using System.Runtime.InteropServices;
    [DllImport(  
    "KERNEL32.DLL"
                 EntryPoint
    ="MoveFileW"
                 SetLastError
    =true
                 CharSet
    =CharSet.Unicode, 
                 ExactSpelling
    =true
                 CallingConvention
    =CallingConvention.StdCall
              )
    ]
    public static extern bool MoveFile(String src, String dst);

    参数说明:

    EntryPoint         指定要调用的 DLL 入口点。

    SetLastError       判断在执行该方法时是否出错(使用 Marshal.GetLastWin32Error API 函数来确定)。
                       C#中默认值为 false。

    CharSet            控制名称及函数中字符串参数的编码方式。默认值为 CharSet.Ansi。

    ExactSpelling      是否修改入口点以对应不同的字符编码方式。

    CallingConvention  指定用于传递方法参数的调用约定。默认值为 WinAPI。
                       该值对应于基于32位Intel平台的 __stdcall。

    BestFitMapping     是否启用最佳映射功能,默认为 true。
                       最佳映射功能提供在没有匹配项时,自动提供匹配的字符。
                       无法映射的字符通常转换为默认的“?”。

    PreserveSig        托管方法签名是否转换成返回 HRESULT,默认值为 true(不应转换签名)。
                       并且返回值有一个附加的 [out, retval] 参数的非托管签名。 
                        
    ThrowOnUnmappableChar     控制对转换为 ANSI '?' 字符的不可映射的 Unicode 字符引发异常。


    C# 关键字 extern 的使用

    public static extern int MyMethod(int x);

    外部修饰符 extern 用于指示外部实现方法,常与 DllImport 属性一起使用(DllImport 属性提供非托管 DLL 函数的调用信息)。

    若将 abstract 和 extern 修饰符一起使用来修改同一成员是错误的。extern 将方法在 C# 代码的外部实现,而 abstract 意味着在此类中未提供此方法的实现。

    因为外部方法声明不提供具体实现,所以没有方法体;
    此方法声明只是以一个分号结束,并且在签名后没有大括号{ }。

    示例3  接收用户输入的字符串并显示在消息框中

    程序从 User32.dll 库导入的 MessageBox 方法。

    using System;
    using System.Runtime.InteropServices;
    class MyClass 
    {
       [DllImport(
    "User32.dll")]
       
    public static extern int MessageBox(int h, string m, string c, int type);

       
    public static int Main() 
       {
          
    string myString; 
          Console.Write(
    "Enter your message: ");
          myString 
    = Console.ReadLine();
          
    return MessageBox(0, myString, "My Message Box"0);
       }
    }

    运行结果: 输入"Hello"文本后,屏幕上将弹出一个包含该文本的消息框。
    Enter your message: Hello

    示例4  调用DLL进行计算 

    该示例使用两个文件 CM.cs 和 Cmdll.c 来说明 extern。
    C 文件是从 C# 程序中调用的外部 DLL。

    使用 Visual C++ 命令行将 Cmdll.c 编译为 DLL:
    cl /LD /MD Cmdll.c

    文件:Cmdll.c

    // cmdll.c
    // compile with: /LD /MD
    int __declspec(dllexport) MyMethod(int i)
    {
       
    return i*10;
    }


    使用命令行编译 CM.cs:
    csc CM.cs
    这将创建可执行文件 CM.exe。


    文件:CM.cs

    // cm.cs
    using System;
    using System.Runtime.InteropServices;
    public class MyClass 
    {
       [DllImport(
    "Cmdll.dll")]
       
    public static extern int MyMethod(int x);

       
    public static void Main() 
       {
          Console.WriteLine(
    "MyMethod() returns {0}.", MyMethod(5));
       }
    }

    运行此程序,MyMethod 将值 5 传递到 DLL 文件,该文件将此值乘以 10 返回。
    运行结果:MyMethod() returns 50.


    IntPtr 类型的说明

    对于平台调用,应让参数为 IntPtr 类型,而不是 String 类型。使用 System.Runtime.InteropServices.Marshal 类所提供的方法,可将类型手动转换为字符串并手动将其释放。

    IntPtr 类型被设计成整数,其大小适用于特定平台。
                      在 32 位硬件和操作系统中将是 32 位;
                      在 64 位硬件和操作系统中将是 64 位。

    IntPtr 类型由支持指针的语言使用,并作为在支持与不支持指针的语言间引用数据的一种通用方式。它也可用于保持句柄。例如,IntPtr 的实例广泛地用在 System.IO.FileStream 类中来保持文件句柄。

    IntPtr 类型符合 CLS,而 UIntPtr 类型却不符合。只有 IntPtr 类型可用在公共语言运行库中。此类型实现 ISerializable 接口。

    其中引用了微软官方的部分例子,特此声名。希望多多指教,共同进步!

  • 相关阅读:
    Leetcode 1489找到最小生成树李关键边和伪关键边
    Leetcode 113 路径总和 II
    hdu 1223 还是畅通工程
    hdu 1087 Super Jumping! Jumping! Jumping!
    hdu 1008 Elevator
    hdu 1037 Keep on Truckin'
    湖工oj 1241 畅通工程
    湖工oj 1162 大武汉局域网
    hdu 2057 A + B Again
    poj 2236 Wireless Network
  • 原文地址:https://www.cnblogs.com/xugang/p/1011597.html
Copyright © 2011-2022 走看看