我已将部分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#开始工作)以及如何使其正常工作?
答案 0 :(得分: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
答案 1 :(得分:2)
问题在于您使用的是结构,它们是值类型,而不是引用类型。通过PowerShell '.'
运算符访问这些成员时,它会返回该结构成员的副本,并在该副本上进行值赋值,而不会将其转换为您尝试修改的对象。
这是一个较小的例子来说明这一点:
> add-type @"
namespace TestStructs {
public struct Inner {
public int i;
};
public class Outer {
public Inner i;
}
}
"@
$s = new-object TestStructs.Outer
我创建了一个类Outer
,其Inner
结构为成员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