具有固定大小数组的元数据结构,用于非托管api调用

时间:2019-04-13 15:22:53

标签: c# pinvoke marshalling unmanaged unsafe

我正在用C#为DHCP服务器API创建托管包装器,并且定义了固定大小缓冲区的非托管结构有问题。我想在DHCP服务器上添加过滤器,API(dhcpsapi.h)定义了以下内容:

#define MAX_PATTERN_LENGTH      255
#define MAC_ADDRESS_LENGTH      6
#define HWTYPE_ETHERNET_10MB    1

typedef enum _DHCP_FILTER_LIST_TYPE {
    Deny,
    Allow
} DHCP_FILTER_LIST_TYPE, *LPDHCP_FILTER_LIST_TYPE;

typedef struct _DHCP_ADDR_PATTERN {
    BOOL MatchHWType;
    BYTE HWType;
    BOOL IsWildcard;
    BYTE Length;
    BYTE Pattern[MAX_PATTERN_LENGTH];
} DHCP_ADDR_PATTERN, *LPDHCP_ADDR_PATTERN;

typedef struct _DHCP_FILTER_ADD_INFOV4 {
    DHCP_ADDR_PATTERN     AddrPatt;
    LPWSTR                Comment;
    DHCP_FILTER_LIST_TYPE ListType;
} DHCP_FILTER_ADD_INFO, *LPDHCP_FILTER_ADD_INFO;

因此,我定义了以下结构/枚举/函数:

    [StructLayout(LayoutKind.Sequential)]
    unsafe internal struct DHCP_FILTER_ADD_INFO
    {
        public DHCP_ADDR_PATTERN AddrPatt;
        public char* Comment;
        public DhcpFilterListType ListType;
    }

    [StructLayout(LayoutKind.Sequential)]
    unsafe internal struct DHCP_ADDR_PATTERN
    {
        public bool MatchHWType;
        public HWTYPE HWType;
        public bool IsWildcard;
        public byte Length;   
        public fixed byte Pattern[255];
    }

    internal enum HWTYPE : byte
    {
        HWTYPE_ETHERNET_10MB = 1
        ...
    }

    // NativeMethods.DhcpAddFilterV4 signature
    [DllImport("dhcpsapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern DHCP_ERROR_CODES DhcpAddFilterV4(string serverIpAddress, IntPtr addFilterInfoPtr, bool forceFlag);

    // Wrapper function
    unsafe public static void AddFilter(string serverAddressString, byte[] macAddress, DhcpFilterListType listType)
    {
        if (macAddress == null)
            throw new ArgumentNullException(nameof(macAddress));
        if (macAddress.Length < 6)
            throw new ArgumentException("Invalid mac address", nameof(macAddress));
        if (!Enum.IsDefined(typeof(DhcpFilterListType), listType))
            throw new ArgumentOutOfRangeException(nameof(listType));

        var addressPattern = new DHCP_ADDR_PATTERN
        {
            HWType = HWTYPE.HWTYPE_ETHERNET_10MB,
            IsWildcard = false,
            MatchHWType = true,
            Length = 6
        };
        // Copy the mac address into the fixed size array
        fixed (byte* macPtr = macAddress)
        {
            Buffer.MemoryCopy(macPtr, addressPattern.Pattern, 255, 6);
        }
        var filterAddInfo = new DHCP_FILTER_ADD_INFO
        {
            ListType = listType,
            AddrPatt = addressPattern
        };
        IntPtr filterAddInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(filterAddInfo));
        try
        {
            Marshal.StructureToPtr(filterAddInfo, filterAddInfoPtr, true);
            DHCP_ERROR_CODES result = NativeMethods.DhcpAddFilterV4(serverAddressString, filterAddInfoPtr, true);
            if (result != DHCP_ERROR_CODES.ERROR_SUCCESS)
                throw DhcpServerException.GetExceptionForErrorCode(result);
        }
        finally
        {
            Marshal.FreeHGlobal(filterAddInfoPtr);
        }
    }

因此,当我调用该函数并尝试为mac地址“ A​​BCDEF123456”添加过滤器时,DHCP MMC会显示一个mac地址为“ AB0000000000”的条目。因此,我略微更改了DHCP_ADDR_PATTERN结构,以在“模式”字段中使用MarshalAs-Attribute:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
public byte[] Pattern;

这还需要我更改包装函数:

byte[] pattern = new byte[255];
Buffer.BlockCopy(macAddress, 0, pattern, 0, 6);
addressPattern.Pattern = pattern;

然后它就像一种魅力。 使用第二种方法时有什么魔力,我介绍的第一种方法中的数据会怎样?

0 个答案:

没有答案