zoukankan      html  css  js  c++  java
  • [C#/C++]C#调用非托管DLL的APIs

    [C#/C++]C#调用非托管DLL的APIs

     
        上网baidu一下或google一下这个东东就有很多人在问这个问题,最近我也用到了这个,所以就留下来以备往后需要是可以查找。我想通过这个来作为C#调用windows APIs的出发点,在以后的随笔当中介绍一下我现阶段用到的一些APIs或非托管类库。在调用非托管DLL的APIs前,我们应该好好掌握一下DllImportAttribute,MSDN给出的定义为:可将该属性应用于方法。DllImportAttribute 属性提供对从非托管 DLL 导出的函数进行调用所必需的信息。作为最低要求,必须提供包含入口点的 DLL 的名称。

    1 [DllImport("KERNEL32.DLL", EntryPoint="MoveFileW",     SetLastError=true,
    2 
    3 CharSet=CharSet.Unicode, ExactSpelling=true,
    4 
    5 CallingConvention=CallingConvention.StdCall)]
    6 
    7 public static extern bool MoveFile(String src, String dst);
    8 
    9 

        从上面的例子中我们可以看出,从Kernel32.dll中引入这个API,其中EntryPoint一看就知道是入口点,也就是DLL中的函数名称。其实只要用过VC++的人都知道,Windows APIs中都提供两个版本,一个是W,一个是A也就是Ansi和Unicode之分,现在一般都采用W,Unicode编程,但是.Net和win32交互的时候,默认是使用CharSet.Ansi来传送。在 DllImportAttribute.ExactSpelling 字段为 true 时(它是 Visual Basic .NET 中的默认值),平台调用将只搜索您指定的名称。例如,如果指定 MessageBox,则平台调用将搜索 MessageBox,如果它找不到完全相同的拼写则失败。当 ExactSpelling 字段为 false(它是 C++ 托管扩展和 C# 中的默认值),平台调用将首先搜索未处理的别名 (MessageBox),如果没有找到未处理的别名,则将搜索已处理的名称 (MessageBoxA)。

        由于很多参数和C#中有所区别,下面让我们看看参数是怎么个情况:

        extern “C” __declspec(dllexport)  int WINAPI sumAB(int a,int b)这个和C#中一样是值类型,a及b的变化不会对C#中的参数造成影响。通过DllImport表示为:
    1 [DllImport(“CppDll.dll")]
    2 //返回个int 类型
    3 public static extern int sumAB (int a1,int b1); 

        下面的是引用类型:也就是说a和b的变化直接影响到a1和b1的值。
    public static extern int sum (ref int a1,ref int b1);
    //DLL中申明
    extern “C” __declspec(dllexport)  int WINAPI sum(int *a,int *b) 

        同理我们可以得到一下几种传值方法:第一种参数的改变不影响C#中参数的变化,需要传入char*类型。
    [DllImport(“CppDll.dll")]
    //传入值
    public static extern int together (string  astr,string bstr);
    //DLL中申明
    extern “C” __declspec(dllexport)  int WINAPI together(char * stra,char * strb) 

        当我们需要从参数中传出char*类型是,那我们就要用到StringBuilder了。
    1 // 传出值
    2 public static extern int getParameter (StringBuilder sb1, StringBuilder sb2);
    3 //DLL中申明
    4 extern “C” __declspec(dllexport)  int WINAPI getParameter(char * stra,char * strb) 
       
        接着我们先来看看结构的封送,这里我们要看看StructLayoutAttribute这个标签,通过它可以定义自己的格式化类型,在托管代码中,格式化类型是一个用StructLayoutAttribute说明的结构或类成员,通过它能够保证其内部成员预期的布局信息,他的成员说明如下:


         LayoutKind.Automatic 为了提高效率允许运行态对类型成员重新排序。注意:永远不要使用这个选项来调用非托管的动态链接库函数。

         LayoutKind.Explicit 对每个域按照FieldOffset属性对类型成员排序

         LayoutKind.Sequential 对出现在托管类型定义地方的非托管内存中的类型成员进行排序。


        我们可以通过GetSystemInfo来获取系统的信息,代码如下:
     1 using System.Runtime.InteropServices;
     2 [StructLayout(LayoutKind.Sequential)]
     3     public struct SYSTEM_INFO {
     4     public uint dwOemId;
     5     public uint dwPageSize;
     6     public uint lpMinimumApplicationAddress;
     7     public uint lpMaximumApplicationAddress;
     8     public uint dwActiveProcessorMask;
     9     public uint dwNumberOfProcessors;
    10     public uint dwProcessorType;
    11     public uint dwAllocationGranularity;
    12     public uint dwProcessorLevel;
    13     public uint dwProcessorRevision;
    14 }
    15 //调用
    16 [DllImport("kernel32")]
    17 static extern void GetSystemInfo(ref SYSTEM_INFO pSI);
    18 SYSTEM_INFO pSI = new SYSTEM_INFO();
    19 GetSystemInfo(ref pSI);

        当我们遇到函数指针参数是我们就需要考虑是否要用到回调函数,例如枚举所有窗口。
    BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam)
        我们可以通过创建一个代理,它带有两个参数hwnd和lparam,第一个参数是一个窗口句柄,第二个参数由应用程序定义,两个参数均为整形。当这个回调函数返回一个非零值时,标示执行成功,零则暗示失败,这个例子总是返回True值,以便持续枚举。最后创建以代理对象(delegate),并把它作为一个参数传递给EnumWindows 函数,平台会自动地 把代理对象转化成函数能够识别的回调格式。

     1 using System;
     2 using System.Runtime.InteropServices;
     3 public delegate bool CallBack(int hwnd, int lParam);
     4 public class EnumReportApp {
     5 [DllImport("user32")]
     6 public static extern int EnumWindows(CallBack x, int y);
     7 public static void Main()
     8 {
     9 CallBack myCallBack = new CallBack(EnumReportApp.Report);
    10 EnumWindows(myCallBack, 0);
    11 }
    12 public static bool Report(int hwnd, int lParam) {
    13 Console.Write("窗口句柄为");
    14 Console.WriteLine(hwnd);
    15 return true;
    16 }
    17 }

        OK,如果熟悉了以上方方面面,基本上也能够调用APIs了别忘了P/Invoke能够帮上很大的忙,我们可以去wiki网站查询我们所要的API:http://pinvoke.net。还需要说明的是很多例子等都来自MSDN和网上检索得到的!!!
  • 相关阅读:
    基于Dubbo框架构建分布式服务(一)
    大型网站架构系列:消息队列
    Redis Cluster 分区实现原理
    Redis五种数据结构简介
    Java中创建对象的5种方式
    Netty 系列之 Netty 高性能之道
    Java 抽象类与接口
    谈iOS抓包:Mac下好用的HTTP/HTTPS抓包工具Charles
    Web系统大规模并发——电商秒杀与抢购
    [转]MS SQL Server 数据库连接字符串详解
  • 原文地址:https://www.cnblogs.com/diulela/p/2703585.html
Copyright © 2011-2022 走看看