zoukankan      html  css  js  c++  java
  • 公共语言运行库(CLR)开发系列课程(2):Pinvoke 进阶 学习笔记

    • 上一章地址

    • 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(255);
                  NativeMethods.GetWindowText(hwnd, text, 255);
                  Console.WriteLine("窗口名:" + text + ",id=" + hwnd.ToInt32() + ",lParam=" + lParam);
                  return true;
              }
              static void Main(string[] args)
              {
                  WNDENUMPROC myWNDENUMPROC = new WNDENUMPROC(WNDENUMPROC_Callback); 
                  NativeMethods.EnumWindows(myWNDENUMPROC,22);
                  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 * -1;
        }
        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, 0, sizeof(int)* length);
        }
        void TestArray(int *a, int length)
        {
            for (int i = 0; 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(11, 2));
        
                    // TestIntPtr
                    int i = 22;
                    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 = 0, 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[] { 1, 2, 3 };
                    TestArray(array, array.Length);
        
                    for (int z = 0; z < array.Length; z++)
                    {
                        Console.WriteLine("array[{0}]" + array[z], z);
                    }
        
                    // TestCallback
                    TestCallback(CallbackFunction);
        
                    Console.Read();
                }
        
        
            }

            

          

  • 相关阅读:
    C# .NET 支付宝IOT小程序AES密钥解密
    aws平台中为ec2实例添加双网卡
    Python使用lxml模块和Requests模块抓取HTML页面的教程
    CentOS下安装PHP Oracle数据库扩展
    如何在Ubuntu 16.04上安装配置Redis
    zabbix参考文档
    docker学习网站
    设置Linux打开文件句柄/proc/sys/fs/file-max和ulimit -n的区别
    MySql按字段分组取最大值记录 [此博文包含图片]
    dokcer使用--link 让容器相连
  • 原文地址:https://www.cnblogs.com/kubimiantiao/p/6098212.html
Copyright © 2011-2022 走看看