6

我已经将一些 DHCP Win32 Api 翻译成 C#,所以我可以使用来自 PowerShell 的:

$definition = @"
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Net;

namespace Dhcpsapi
{

    public enum DHCP_SEARCH_INFO_TYPE : int
    {
        DhcpClientIpAddress = 0,
        DhcpClientHardwareAddress = 1,
        DhcpClientName = 2
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct DHCP_BINARY_DATA
    {
        public uint DataLength;
        public IntPtr Data;
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct DHCP_IP_ADDRESS
    {
        public UInt32 IPAddress;
    }

    [StructLayout(LayoutKind.Explicit, Size=8)]
    public struct SearchInfo
    {
        [FieldOffset(0)]
        public DHCP_IP_ADDRESS ClientIpAddress;
        [FieldOffset(0)]
        public DHCP_BINARY_DATA ClientHardwareAddress;
        [FieldOffset(0)]
        public IntPtr ClientName; //LPWSTR
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DHCP_SEARCH_INFO
    {
        public DHCP_SEARCH_INFO_TYPE SearchType;
        public SearchInfo SearchInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DATE_TIME
    {
        public UInt32 dwLowDateTime;
        public UInt32 dwHighDateTime;

        public DateTime ToDateTime()
        {
            if (dwHighDateTime == 0 && dwLowDateTime == 0)
            {
                return DateTime.MinValue;
            }
            if (dwHighDateTime == int.MaxValue && dwLowDateTime == UInt32.MaxValue)
            {
                return DateTime.MaxValue;
            }
            return DateTime.FromFileTime((((long)dwHighDateTime) << 32) | dwLowDateTime);
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct DHCP_HOST_INFO
    {
        public uint IpAddress;
        public string NetBiosName;
        public string HostName;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct DHCP_CLIENT_INFO
    {
        public uint ClientIpAddress;
        public uint SubnetMask;
        public DHCP_BINARY_DATA ClientHardwareAddress;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string ClientName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string ClientComment;
        public DATE_TIME ClientLeaseExpires;
        public DHCP_HOST_INFO OwnerHost;
    }

    public static class PS
    {

        [DllImport("Dhcpsapi.dll")]
        public static extern uint DhcpGetClientInfo(
            [MarshalAs(UnmanagedType.LPWStr)]
            string ServerIpAddress,
            [MarshalAs(UnmanagedType.Struct)]
            ref DHCP_SEARCH_INFO SearchInfo,
            out IntPtr ClientInfo);

        [DllImport("dhcpsapi.dll", SetLastError=true)]
        public static extern void DhcpRpcFreeMemory(IntPtr BufferPointer);
    }
}
"@

Add-Type -TypeDefinition $definition -PassThru

现在我尝试从 PowerShell 中使用它:

$dsi = New-Object dhcpsapi.DHCP_SEARCH_INFO
$dsi.SearchType = [Dhcpsapi.DHCP_SEARCH_INFO_TYPE]::DhcpClientIpAddress
$ipa = [System.Net.IPAddress]::Parse("10.4.3.101")
$ip = [UInt32][System.Net.IPAddress]::NetworkToHostOrder([int][System.BitConverter]::ToUInt32($ipa.GetAddressBytes(), 0))

# $ip is now 168035173
$dsi.SearchInfo.ClientIpAddress.IPAddress = $ip

#however $dsi.SearchInfo.ClientIpAddress.IPAddress is 0
$dsi.SearchInfo.ClientIpAddress.IPAddress

在运行 $dsi.SearchInfo.ClientIpAddress.IPAddress 之后,我期望的是 0 而不是 168035173 。为什么会发生这种情况(它按 C# 的预期工作)以及如何使其工作?

4

2 回答 2

4

SearchInfo 是一个结构,所以当你这样做时:

#however $dsi.SearchInfo.ClientIpAddress.IPAddress is 0
$dsi.SearchInfo.ClientIpAddress.IPAddress

那么您正在创建 SearchInfo 的副本,并且该副本上的值正在发生变化。对于每个结构,您需要改变结构,然后将其分配回:

$clientIpAddress = $dsi.SearchInfo.ClientIpAddress
$clientIpAddress.IPAddress = $ip

$searchInfo = $dsi.SearchInfo
$searchInfo.ClientIpAddress = $clientIpAddress

$dsi.SearchInfo = $searchInfo
于 2012-10-31T15:47:45.647 回答
2

问题是您使用的是值类型的结构,而不是引用类型。通过 PowerShell'.'运算符访问这些成员时,它会返回该结构成员的副本,并且值分配发生在该副本上,并且永远不会将其放入您尝试修改的对象中。

这是一个较小的示例,可以证明这一点:

> add-type @"
namespace TestStructs {
    public struct Inner {
        public int i;
    };
    public class Outer {
        public Inner i;
    }
}
"@
$s = new-object TestStructs.Outer

我创建了一个Outer具有Innerstruct 作为 member的类i。如果我尝试分配值,我会得到您所看到的行为,它保持为 0:

> $s.i.i
0

> $s.i.i = 6

> $s.i.i
0

解决这个问题的方法是分配整个结构。因此,对于这个简单的案例,我可以创建一个新结构,设置值,然后将其分配给我要修改的对象:

> $new_inner = new-object TestStructs.Inner
> $new_inner.i
0

> $new_inner.i = 6
> $new_inner.i
6

> $s.i = $new_inner
> $s.i.i
6

我可以使用一些速记来做到这一点:

> $s.i = new-object TestStructs.Inner -prop @{ i = 7 }
> $s.i.i
7

但是,如果结构中有很多值,这可能不切实际。因此,您还可以将其保存为临时值,然后重新分配:

> $s.i = &{ $temp = $s.i; $temp.i = 10; $temp }

H:\
> $s.i.i
10
于 2012-10-31T17:36:00.703 回答