zoukankan      html  css  js  c++  java
  • C#调用Win32 api时的内存操作

    一般情况下,C#与Win 32 Api的互操作都表现的很一致:值类型传递结构体,一维、二维指针传递IntPtr。在Win32 分配内存时,可以通过IntPtr以类似移动指针的方式读取内存。通过IntPtr移动时,需要考虑指针的计算。规则总体上来说显得一致,但Win32 Api庞杂,总有一些令人困惑的函数。比如GetIpForwardTable。该函数的功能是返回Ip(v4)的路由表。在win32 的结构体定义如下:

    DWORD GetIpForwardTable(
      _Out_   PMIB_IPFORWARDTABLE pIpForwardTable,
      _Inout_ PULONG              pdwSize,
      _In_    BOOL                bOrder
    );
    
    typedef struct _MIB_IPFORWARDTABLE {
      DWORD            dwNumEntries;
      MIB_IPFORWARDROW table[ANY_SIZE];
    } MIB_IPFORWARDTABLE, *PMIB_IPFORWARDTABLE;

    MIB_IPFORWARDROW table[ANY_SIZE]这个定义就很容易使人误解。初一看是一个数组,但搜索之下,却并没有轻易发现ANY_SIZE到底是多大。如果互操作需要定义数组,则一定要指定其大小。再看一下该参数的说明:指向路由实体的指针。很显然,并不是一个数组,而是一个指针。则定义为IntPtr。(确实没有搜索大ANY_SIZE定义)

    函数就更有迷惑了,先看定义:

    DWORD GetIpForwardTable(
      _Out_   PMIB_IPFORWARDTABLE pIpForwardTable,
      _Inout_ PULONG              pdwSize,
      _In_    BOOL                bOrder
    );

    Win32 Api的一般套路是空参数返回全部从参数的大小;传递内存空间返回全部内容的数组。按照常理,返回的预期缓存大小是数组的大小,很容易让人以为是MIB_IPFORWARDROW table[ANY_SIZE]; 所需要的大小(基于字节)。我这么做了,并且网上还有示例也这么做了。我按照自己的要么内存错误,要么读取不到数据。不知道网络上是怎么编译通过的。

    网络错误示例:

    var buffSize = TableLength();
    var size = 1 + buffSize / (ulong)Marshal.SizeOf<MibIpForwardRow>();
    var table = new MibIpForwardTable()
    {
        dwNumEntries = (uint)size,
         table = Marshal.AllocHGlobal((int)buffSize)
    };
    
    /** 思路大概是这样,还对结构体数量加了1 (C语言经常这么干)**/

    依然失败。我尝试了多种方法,没有成功。初步怀疑自己理解错了,看了MSDN上,关于该函数的C++示例,果然是自己理解错了。(MSDN关于此函数的链接

     if (GetIpForwardTable(pIpForwardTable, &dwSize, 0) ==
            ERROR_INSUFFICIENT_BUFFER) {
            FREE(pIpForwardTable);
            pIpForwardTable = (MIB_IPFORWARDTABLE *) MALLOC(dwSize);
            if (pIpForwardTable == NULL) {
                printf("Error allocating memory
    ");
                return 1;
            }
        }
    
        /* Note that the IPv4 addresses returned in 
         * GetIpForwardTable entries are in network byte order 
         */
        if ((dwRetVal = GetIpForwardTable(pIpForwardTable, &dwSize, 0)) == NO_ERROR) {
            printf("	Number of entries: %d
    ",
                   (int) pIpForwardTable->dwNumEntries);
            for (i = 0; i < (int) pIpForwardTable->dwNumEntries; i++) {
                /* Convert IPv4 addresses to strings */
                IpAddr.S_un.S_addr =
                    (u_long) pIpForwardTable->table[i].dwForwardDest;

    看黑色加粗的两个部分。当第一次call此函数时,返回的预期空间大小(基于字节)是指针指向的内存空间大小。是 dwNumEntries +  table[ANY_SIZE]的总字节大小。需要自行操作内存,以获取table的首地址。也就是说当通过IntPtr获取到所有的路由表后,需要转换、操作赋值,即:

                var tablePtr = Marshal.AllocHGlobal((int)buffSize);
                GetIpForwardTable(tablePtr, ref buffSize, false);
    
                var table = Marshal.PtrToStructure<MibIpForwardTable>(tablePtr);
                var list = new List<MibIpForwardRow>();
                var begin = table.table = tablePtr + sizeof(uint);
                for (var i = 0; i < table.dwNumEntries; i++)
                {
                    list.Add(Marshal.PtrToStructure<MibIpForwardRow>(begin));
                    begin += Marshal.SizeOf<MibIpForwardRow>();
                }
    
    
                Marshal.FreeHGlobal(tablePtr);
                return list;

    黑体部分:

    现将填充后的IntPtr转换为MibIpForwardTable结构体;这时候会得到dwNumEntries。但第二个参数IntPtr需要手动再赋值。

    var table = Marshal.PtrToStructure<MibIpForwardTable>(tablePtr);

    在赋值语句为:
    table.table = tablePtr + sizeof(uint);
    需要手动向前移动4个字节。
    这样内存布局就正确了。

    完整代码如下:

     1 //一定要指定编码,否则默认是ASCII格式
     2 [DllImport("iphlpapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
     3 public static extern int GetIpForwardTable(IntPtr pIpForwardTable, ref ulong pdwSize, bool bOrder);
     4 
     5 [DllImport("Ntdll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
     6 public static extern IntPtr RtlIpv4AddressToString(ref IN_ADDR Addr, IntPtr s);
     7 
     8 [DllImport("Ws2_32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
     9 public static extern string inet_ntoa(IN_ADDR inAddr);
    10 
    11         public IEnumerable<Win32.MibIpForwardRow> ForwardTable()
    12         {
    13             ulong TableLength()
    14             {
    15                 ulong _len = 0;                
    16                 GetIpForwardTable(IntPtr.Zero, ref _len, false);
    17                 return _len;
    18             }
    19 
    20             var buffSize = TableLength();
    21             var tablePtr = Marshal.AllocHGlobal((int)buffSize);
    22             GetIpForwardTable(tablePtr, ref buffSize, false);
    23 
    24             var table = Marshal.PtrToStructure<MibIpForwardTable>(tablePtr);
    25             var list = new List<MibIpForwardRow>((int)table.dwNumEntries);
    26             var begin = table.table = tablePtr + sizeof(uint);
    27             for (var i = 0; i < table.dwNumEntries; i++)
    28             {
    29                 list.Add(Marshal.PtrToStructure<MibIpForwardRow>(begin));
    30                 begin += Marshal.SizeOf<MibIpForwardRow>();
    31             }
    32 
    33             Marshal.FreeHGlobal(tablePtr);
    34             return list;
    35         }
    36 
    37 public static string ToInAddrString(this uint value)
    38         {
    39             var addr = new IN_ADDR() { S_addr = value };
    40             var ptr = Marshal.AllocHGlobal(64);
    41             RtlIpv4AddressToString(ref addr, ptr);
    42             var ip = Marshal.PtrToStringUni(ptr);
    43             Marshal.FreeHGlobal(ptr);
    44             return ip;
    45         }
    46 
    47 static void Main(string[] args)
    48         {
    49             var route = new Router();
    50             foreach (var r in route.ForwardTable())
    51                 Console.WriteLine(r.dwForwardDest.ToInAddrString());
    52         }
    View Code

    输出:

    0.0.0.0
    127.0.0.0
    127.0.0.1
    127.255.255.255
    192.168.199.0
    192.168.199.245
    192.168.199.255
    224.0.0.0
    224.0.0.0
    255.255.255.255
    255.255.255.255
    请按任意键继续. . .
  • 相关阅读:
    Leetcode 191.位1的个数 By Python
    反向传播的推导
    Leetcode 268.缺失数字 By Python
    Leetcode 326.3的幂 By Python
    Leetcode 28.实现strStr() By Python
    Leetcode 7.反转整数 By Python
    Leetcode 125.验证回文串 By Python
    Leetcode 1.两数之和 By Python
    Hdoj 1008.Elevator 题解
    TZOJ 车辆拥挤相互往里走
  • 原文地址:https://www.cnblogs.com/jjseen/p/7076831.html
Copyright © 2011-2022 走看看