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 接口。

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

  • 相关阅读:
    WebQQ协议分析(9)——聊天(2)
    我的程序员之路(3)——学生时代(3)
    我的程序员之路(2)——学生时代(2)
    我的程序员之路(1)——学生时代(1)
    WebQQ协议分析——目录
    我的程序员之路(4)——工作半年
    WebQQ协议分析(7)——获取群信息(2)
    WebQQ协议分析(8)——聊天(1)
    VS2008编译器下ACE的配置
    WebQQ协议分析(10)——聊天(3)
  • 原文地址:https://www.cnblogs.com/xugang/p/1011597.html
Copyright © 2011-2022 走看看