• 上一章地址

  • API版本

    • 具有字符串参数的API通常有两种版本

      • GetWindowText

        • GetWindowTextA
        • GetWindowTextW
    • 缺省情况下CLR会自动寻找合适的匹配

      • CharSet=Ansi时寻找A版本
      • CharSet=Unicode寻找W版本
      • CharSet=Auto和平台相关
    • DLLImportAttribute(ExactSpelling=true)会关掉这种行为

  • Ref/Out参数 (C#关键字)

    • out=[Out]&

      • C#:out int a
      • IL:[Out] int &a
      • C++:int *pa;
    • ref=[In,Out]&

      • C#:ref int a
      • IL:[In,Out] int &a
      • C++:int *pa;
    • 个人感觉 使用Ref/Out 参数P/Invoke CRL会对根据对应关键字添加对应IL特性 System.Runtime.InteropServices下面的InAttribute、OutAttribute   

  • 引用类型和值类型

    • 非托管C/C++ 代码:struct A{int a;} 

      托管代码:struct A{int a;}  class A{int a;}

    • 非托管C/C++代码 A A* A**
      A为值类型 A ref A IntPtr  a
      A为引用类型 N/A A ref A
  • 自定义参数转换

    • 托管类型和非托管类型之间是一对多关系

      • String

        • char */wchar_t *  
        • char[N]/wchar_t[N] 固定长度的字符串
        • BSTR, Ansi BSTR
    • MarshalAsAttribute(UnmanagedType) 
    • UnmanagedType 枚举指定对应非托管类型
    • [MarshalAsAttribute(UnmanagedType.LPWSTR)] string A
    • 非托管函数:void Func(wchar_t *)
    • 托管函数:Public static extern void  Func([MarshalAs(UnmanagdType.LPWSTR)] string);
  • 内存管理

    • 在数据转换中需要进行内存分配

    • 举例:

      • C#调用Func(string)      Marshal.StringToHGlobalAnsi("balabala")
      • C#调用Func(ref string)
      • C#调用string Func()
    • CoTaskMemAlloc / CoTaskMemFree (分配/释放 内存) 内存交换才使用 

  • 实例分析CreateProcess

    • API原型

      BOOL WINAPI CreateProcess(
      _In_opt_    LPCTSTR               lpApplicationName,
      _Inout_opt_ LPTSTR                lpCommandLine,
      _In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes,
      _In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes,
      _In_        BOOL                  bInheritHandles,
      _In_        DWORD                 dwCreationFlags,
      _In_opt_    LPVOID                lpEnvironment,
      _In_opt_    LPCTSTR               lpCurrentDirectory,
      _In_        LPSTARTUPINFO         lpStartupInfo,
      _Out_       LPPROCESS_INFORMATION lpProcessInformation
      );

           (opt 可选的输入参数)

    • C#  

      Public static extern bool CreateProcess(
           [In] string applicationName,
           [In,Out] StringBuilder commandName,
           [In] ref SECURITY_ATTRIBUTES processAttributes,
           [In] ref SECURITY_ATTRIBUTES threadAttributes,
           [In] bool inheritHandles,
           [In] uint creationFlags,
           [In] IntPtr lpEnvironment,
           [In] string currentDirectory,
           [In] ref STARTUP_INFO startupInfo,
           [Out] out PROCESS_INFORMATION processInformation
      ); 

  • DLL生命周期

    • Dll在第一次调用的时候自动加载,不会自动被释放,加载不到或者找不到函数就会抛出异常

    • Dll不会自动被卸载,建议做法生命一个外部调用类 DLL会伴随外部调用类一起被清理卸载

    • 如果需要手段管理DLL生命周期,只能手动调用LoadLibrary/GetProcAddress

  • 逆向P/Invoke

    非托管代码到托管代码这种叫逆向P/Invoke

    • 部分API接收函数指针作为参数

    • EnumWindows(WNDENUMPROC IpEnumFunc,LPARAM lPararm) 

    • .NET 程序可以通过P/Invoke调用 EnumWindows API

    • CLR能够将Delegate 转换为函数指针,EnumWindows 来回调这个函数指针

    • [UnmanagedFunctionPointer(CallingConvention.Winapi)]
      delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);
      EnumWindow(new EnumWindowProc(MyEnumWindow), IntPtr.Zero);
  • DEMO

     class Program
    {
    /// Return Type: BOOL->int
    ///hwnd: HWND->HWND__*
    ///lParam: LPARAM->LONG_PTR->int
    [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)]
    public delegate bool WNDENUMPROC(IntPtr hwnd, IntPtr lParam); class NativeMethods
    {
    [DllImport("User32.dll",CallingConvention=CallingConvention.Winapi)]
    public static extern int GetWindowText(IntPtr hwnd, StringBuilder text, int count); /// Return Type: BOOL->int
    ///lpEnumFunc: WNDENUMPROC
    ///lParam: LPARAM->LONG_PTR->int
    [DllImportAttribute("user32.dll", EntryPoint = "EnumWindows")]
    [return: MarshalAsAttribute(UnmanagedType.Bool)]
    public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, int lParam);
    } private static bool WNDENUMPROC_Callback(IntPtr hwnd, IntPtr lParam)
    {
    StringBuilder text = new StringBuilder();
    NativeMethods.GetWindowText(hwnd, text, );
    Console.WriteLine("窗口名:" + text + ",id=" + hwnd.ToInt32() + ",lParam=" + lParam);
    return true;
    }
    static void Main(string[] args)
    {
    WNDENUMPROC myWNDENUMPROC = new WNDENUMPROC(WNDENUMPROC_Callback);
    NativeMethods.EnumWindows(myWNDENUMPROC,);
    Console.Read();
    } }
  • 常见问题

    • 抛出DllNotFoundException

      找不到DLL一般他会到当前目录去找 或者系统目录去找 DLL如果实在无法差错可以使用全路径+dll名不推荐最好是放相对路径

    • 抛出AccessViolationException

      访问违规,一般是内存不可以读不可以写或者一起读一起写造成的,通常原因参数转换错误,类型不对应,长度问题。

    • 抛出EntryPointNotFoundException

      API方法入口找不到一般都是方法名写错或者调用协定不对

    • MDA报错StackImbalanceMDA

      VS提示报错,参数错位调用协定不对

    • 返回数据错误

      参数是否正确,MarshalAs转换是否成功,开启非托管调试查看。

  • 进阶DEMO

    • c++

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <Windows.h>
      /*自定义*/
      typedef struct Mytype
      {
      int i;
      char *s;
      double d;
      struct Mytype *p;
      }Mytype; int TestInt(int a,int b)
      {
      return a + b;
      }
      void TestIntPtr(int *i)
      {
      *i = *i * -;
      }
      char * TestCharPtr(char *a,char *b)
      {
      return strcat(a, b);
      }
      void TestStructPtr(Mytype *p)
      {
      p->i++;
      p->d++;
      }
      void TestPtrPtr(int **a,int length)
      {
      *a = (int*)malloc(sizeof(int)*length);
      memset(*a, , sizeof(int)* length);
      }
      void TestArray(int *a, int length)
      {
      for (int i = ; i < length; i++)
      {
      a[i]++;
      }
      } void TestCallback(void(*pf)())
      {
      pf();
      }
      int TestCharWidth(char *s)
      {
      return strlen(s);
      } 文件名 Source.cpp
      LIBRARY "PInvoke"
      EXPORTS
      TestInt
      TestIntPtr
      TestCharPtr
      TestStructPtr
      TestPtrPtr
      TestArray
      TestCallback
      TestCharWidth
      文件名 Source.def
    • C#
       class Program
      { [StructLayoutAttribute(LayoutKind.Sequential)]
      public struct Mytype
      { /// int
      public int i; /// char*
      [MarshalAsAttribute(UnmanagedType.LPStr)]
      public string s; /// double
      public double d; /// Mytype*
      public System.IntPtr p;
      } /// Return Type: void
      public delegate void Callback(); static void CallbackFunction()
      {
      Console.WriteLine("callback invoked");
      }
      /// Return Type: int
      ///a: int
      ///b: int
      [DllImportAttribute("Pinvoke.dll", EntryPoint = "TestInt", CallingConvention = CallingConvention.Cdecl)]
      public static extern int TestInt(int a, int b); /// Return Type: void
      ///i: int*
      [DllImportAttribute("Pinvoke.dll", EntryPoint = "TestIntPtr", CallingConvention = CallingConvention.Cdecl)]
      public static extern void TestIntPtr(ref int i); /// Return Type: char*
      ///a: char*
      ///b: char*
      [DllImportAttribute("Pinvoke.dll", EntryPoint = "TestCharPtr", CallingConvention = CallingConvention.Cdecl)]
      public static extern System.IntPtr TestCharPtr(System.IntPtr a, System.IntPtr b); /// Return Type: void
      ///p: Mytype*
      [DllImportAttribute("Pinvoke.dll", EntryPoint = "TestStructPtr", CallingConvention = CallingConvention.Cdecl)]
      public static extern void TestStructPtr(ref Mytype p); /// Return Type: void
      ///a: int**
      ///length: int
      [DllImportAttribute("Pinvoke.dll", EntryPoint = "TestPtrPtr", CallingConvention = CallingConvention.Cdecl)]
      public static extern void TestPtrPtr(ref System.IntPtr a, int length); /// Return Type: void
      ///a: int*
      ///length: int
      [DllImportAttribute("Pinvoke.dll", EntryPoint = "TestArray", CallingConvention = CallingConvention.Cdecl)]
      public static extern void TestArray(int[] a, int length); /// Return Type: void
      ///pf: Anonymous_5afb5371_1680_4be9_99a9_ab5bd7ded029
      [DllImportAttribute("Pinvoke.dll", EntryPoint = "TestCallback", CallingConvention = CallingConvention.Cdecl )]
      public static extern void TestCallback(Callback pf); /// Return Type: int
      ///s: char*
      [DllImportAttribute("Pinvoke.dll", EntryPoint = "TestCharWidth", CallingConvention = CallingConvention.Cdecl)]
      public static extern int TestCharWidth( IntPtr s); static void Main(string[] args)
      { Console.WriteLine("TestInt:" + TestInt(, )); // TestIntPtr
      int i = ;
      TestIntPtr(ref i);
      Console.WriteLine("TestIntPtr:" + i); // TestCharPtr
      IntPtr helloPtr = Marshal.StringToHGlobalAnsi("啊阿斯达斯的阿萨德+");
      IntPtr worldPtr = Marshal.StringToHGlobalAnsi("+阿萨德阿萨德");
      IntPtr helloWorldPtr = TestCharPtr(helloPtr, worldPtr);
      string helloWorld = Marshal.PtrToStringAnsi(helloWorldPtr);
      Console.WriteLine("TestCharPtr:" + helloWorld); //helloPtr = Marshal.StringToHGlobalUni("啊阿斯蒂芬a");
      //worldPtr = Marshal.StringToHGlobalUni("阿萨德阿萨德");
      //helloWorldPtr = TestCharPtr(helloPtr, worldPtr);
      //helloWorld = Marshal.PtrToStringUni(helloWorldPtr);
      //Console.WriteLine("TestCharPtr01:" + helloWorld);
      // Marshal.FreeCoTaskMem()
      Marshal.FreeHGlobal(helloPtr);
      Marshal.FreeHGlobal(worldPtr); // Marshal.FreeHGlobal(helloWorldPtr); // 因为helloWorldPtr和helloPtr指向的是同一地址,所以再次释放会报错 // TestCharWidth
      string a = "a的";
      IntPtr aPtr = Marshal.StringToHGlobalAnsi(a); // Ansi
      int len = TestCharWidth(aPtr);
      Console.WriteLine("TestCharWidth:" + len);
      a = Marshal.PtrToStringAnsi(aPtr);
      Marshal.FreeHGlobal(aPtr); aPtr = Marshal.StringToHGlobalUni(a); // Unicode
      len = TestCharWidth(aPtr); // 值是1,strlen没有正确处理unicode,所以不要使用strlen测量unicode字符串的长度
      Console.WriteLine("TestCharWidth:" + len);
      a = Marshal.PtrToStringUni(aPtr);
      Marshal.FreeHGlobal(aPtr); // TestStructPtr
      Mytype myType = new Mytype { i = , d = 1.1, s = a, p = IntPtr.Zero };
      TestStructPtr(ref myType);
      Console.WriteLine("Mytype->i:" + myType.i);
      Console.WriteLine("Mytype->d:" + myType.d); // TestArray
      int[] array = new int[] { , , };
      TestArray(array, array.Length); for (int z = ; z < array.Length; z++)
      {
      Console.WriteLine("array[{0}]" + array[z], z);
      } // TestCallback
      TestCallback(CallbackFunction); Console.Read();
      } }

          

      

公共语言运行库(CLR)开发系列课程(2):Pinvoke 进阶 学习笔记的更多相关文章

  1. 公共语言运行库(CLR)开发系列课程(1):Pinvoke 简介 学习笔记

    前言 让拖管代码对象和非托管对象协同工作的过程称为互用性(Interoperability),通常简称为 Interop. P/Invoke在托管代码与非托管代码交互式时产生一个事务(Transiti ...

  2. 公共语言运行库(CLR)开发系列课程(3):COM Interop基础 学习笔记

    上章地址 什么是COM Component Object Model 组建对象模型 基于接口(Interface) 接口=协议 IID 标识接口 V-table 虚表 方式调用 单继承 对象(Obje ...

  3. CLR 公共语言运行库

    1..支持多语言..只是语言是面向CLR的..均可以在此基础上运行. 2..程序集加载..程序打包之后的Dll文件由CLR(公共语言运行库)来编译并加载到可以执行状态..由CLR(公共语言运行库)加载 ...

  4. 公共语言运行库(CLR)和中间语言(IL)(一)

    公共语言运行库(.net运行库)即CLR 1.C#先编译为IL,IL为ms的中间语言,IL是平台无关性的. 2.CLR再将IL编译为平台专用语言. 3.CLR在编译IL时为即时编译(JIT) VB.V ...

  5. Silverlight for Windows Phone开发系列课程

    Silverlight for Windows Phone开发系列课程(1):Windows Phone平台概况         课程简介:本节开始介绍系列课程的概况,包括课程内容,先决条件,学习目的 ...

  6. ASP.NET MVC框架开发系列课程 (webcast视频下载)

    课程讲师: 赵劼 MSDN特邀讲师 赵劼(网名“老赵”.英文名“Jeffrey Zhao”,技术博客为http://jeffreyzhao.cnblogs.com),微软最有价值专家(ASP.NET ...

  7. CLR(Common Language Runtime) 公共语言运行库

    .NET Core 使用 CoreCLR .NET Framework 使用CLR. 1. 将代码编译为IL (Intermediate Language) 2. CLR 把IL 编译为平台专用的本地 ...

  8. iOS开发系列课程预告

    近期在Mac和iOS上做开发,认为应该写一点东西分享给感兴趣的童鞋们.在此之前.以前有非常多同行们都在埋怨苹果Objective-C的复杂和难以上手,为此也有非常多人对今年(2014年)刚推出的Swi ...

  9. 【VS开发】Windows平台下Makefile学习笔记

    作者:朱金灿 来源:http://blog.csdn.net/clever101 决心学习Makefile,一方面是为了解决编译开源代码时需要跨编译平台的问题(发现一些开源代码已经在使用VS2010开 ...

随机推荐

  1. 中间件——dubbo

    DUBBO初探-搭建DUBBO开发环境 2016年10月13日 12:27:49 NeroJings 阅读数:1697 标签: dubbo 更多 个人分类: dubbo   我所理解的DUBBO 相对 ...

  2. 各小组Alpha版项目发布作品点评

    第一组:新蜂小组 题目:俄罗斯方块 评论:主体功能已经完成,可以流畅的进行游戏,游戏素材都是由贴图美化过的,期待计分系统等的完善. 第二组:天天向上 题目:连连看 评论:核心功能完成,可以流畅的进行游 ...

  3. SQLSERVER 设置自动备份数据库

    1. SQLSERVER 简单的设置 计划任务 进行 备份数据库的操作. 首先需要打开 一些设置 执行 命令如下: sp_configure ; GO RECONFIGURE; GO sp_confi ...

  4. SP2713 GSS4

    题目链接 这是一道假题,表面上看起来,好像使用了什么奇妙的操作,其实就是一个无脑暴力 我们会发现,即使是\(1e18\),在开方\(6\)次之后也已经变成了\(1\),而\(1\)再怎么开方还是\(1 ...

  5. Day25-JSONP跨域请求

    1.  json 是一种数据格式, jsonp 是一种数据调用的方式.你可以简单的理解为 带callback的json就是jsonp. 2. Python里面有个模块requests, request ...

  6. 【题解】 bzoj1207: [HNOI2004]打鼹鼠 (动态规划)

    bzoj1207,懒得复制,戳我戳我 Solution: 挺傻逼的一个\(dp\),直接推就好了 这题在bzoj上的数据有点问题,题目保证每个时间点不会出现在同一位置两个地鼠,然而他有= =(还浪费我 ...

  7. hadoop2相对hadoop1有非常重大的改进

    hadoop2相对hadoop1有非常重大的改进. 下面看一下在HDFS和MapReduce方面的改进: HDFS Federation(HDFS联邦)federation-background[1] ...

  8. AtCoder Grand Contest 008

    AtCoder Grand Contest 008 A - Simple Calculator 翻译 有一个计算器,上面有一个显示按钮和两个其他的按钮.初始时,计算器上显示的数字是\(x\),现在想把 ...

  9. mfc视类中错误:IntelliSense: declaration is incompatible with。。。解决方案

    基本情况是我自己写了一个类: class CDib {....} 然后在mfc自动生成的“工程名Dlg.cpp”中使用类CDib,我的工程名是MfcPictureProcessing,所以类是clas ...

  10. HTTP ERROR 400 Bad Request

    一springmvc项目中我新增记录完全ok,编辑就是不行,后台方法进不去.老是报错HTTP ERROR 400 Bad Request. 经过查询,说是400表示请求中的语法错误. 我把新增记录的请 ...