Set value in C # struct using PowerShell

I translated some of Win32 Api DHCP into C # to use 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 

Now I'm trying to use this from 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 

After running this, $ dsi.SearchInfo.ClientIpAddress.IPAddress will be 0 instead of 168035173, as I expect. Why is this happening (it works as expected from C #) and how does it work?

+6
source share
2 answers

SearchInfo is a structure, so when you do this:

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

then you create a copy of SearchInfo, and the value changes to that copy. For each structure, you need to change the structure and then assign it:

 $clientIpAddress = $dsi.SearchInfo.ClientIpAddress $clientIpAddress.IPAddress = $ip $searchInfo = $dsi.SearchInfo $searchInfo.ClientIpAddress = $clientIpAddress $dsi.SearchInfo = $searchInfo 
+4
source

The problem is that you are using structures that are value types, as opposed to reference types. When accessing these members through the PowerShell '.' it returns a copy of this element of the structure to you, and the value assignment takes place on this copy and never gets into the object that you are trying to change.

Here is a small example demonstrating this:

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

I created an Outer class that has an Inner structure as a member of i . If I try to assign a value, I get the behavior that you see where it stays at 0:

 > $sii 0 > $sii = 6 > $sii 0 

The way around this is to assign the whole structure. So, for this simple case, I can create a new structure, set the value, and then assign it to the object I want to change:

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

I can use a few shortcuts to do this:

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

This can be impractical if you have many meanings in your structure. This way you can also save it to a temporary value and reassign:

 > $si = &{ $temp = $si; $temp.i = 10; $temp } H:\ > $sii 10 
+2
source

All Articles