lib/types.ps1
using namespace System.Reflection using namespace System.Reflection.Emit using namespace System.Collections.Specialized using namespace System.Runtime.InteropServices function New-Enum { [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [ValidateNotNullOrEmpty()] [String]$Name, [Parameter(Mandatory, Position=1)] [ValidateScript({![String]::IsNullOrEmpty($_)})] [ScriptBlock]$Definition, [Parameter()] [Type]$Type = [Int32], [Parameter()] [Switch]$Flags ) end { if (!($pmb = Get-DynBuilder).GetType($Name)) { $pack = $Type -as [Type] $type = $pmb.DefineEnum($Name, 'Public', $pack) if ($Flags) { $type.SetCustomAttribute(( [CustomAttributeBuilder]::new([FlagsAttribute].GetConstructor(@()), @()) )) } $i = 0 $Definition.ToString().Trim().Split("`r`n", [StringSplitOptions]::RemoveEmptyEntries).ForEach{ $arr = @(($_ -split '(?:\s+)?=(?:\s+)?').Trim()) [void]$type.DefineLiteral($arr[0], ($i = [Int32]($arr[1] ?? $i)) -as $pack) $i+=1 } [void]$type.CreateType() } } } function New-Structure { [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [ValidateNotNullOrEmpty()] [String]$Name, [Parameter(Mandatory, Position=1)] [ValidateScript({![String]::IsNullOrEmpty($_)})] [ScriptBlock]$Definition, [Parameter()] [ValidateSet( 'Unspecified', 'Size1', 'Size2', 'Size4', 'Size8', 'Size16', 'Size32', 'Size64', 'Size128' )] [ValidateNotNullOrEmpty()] [PackingSize]$PackingSize = 'Unspecified', [Parameter()] [ValidateSet('Ansi', 'Auto', 'Unicode')] [ValidateNotNullOrEmpty()] [CharSet]$CharSet = 'Ansi', [Parameter()] [Switch]$Explicit ) begin { [TypeAttributes]$attr = 'BeforeFieldInit, Class, Public, Sealed' $attr = $attr -bor ($Explicit.IsPresent ? 'Explicit' : 'Sequential') -bor "$($CharSet)Class" } process {} end { if (!($pmb = Get-DynBuilder).GetType($Name)) { $type = $pmb.DefineType($Name, $attr, [ValueType], $PackingSize) $ctor = [MarshalAsAttribute].GetConstructor( [BindingFlags]'Instance, Public', $null, [Type[]]([UnmanagedType]), $null ) $sc = [MarshalAsAttribute].GetField('SizeConst') $Definition.Ast.FindAll({$args[0].CommandElements}, $true).ToArray().ForEach{ $ftype, $fdesc = $_.CommandElements.Value $ftype = $pmb.GetType($ftype) ?? [Type]$ftype $fdesc = @(($fdesc -split '\s+?').Where{$_}) # field, param ... switch ($fdesc.Length) { 1 {[void]$type.DefineField($fdesc[0], $ftype, 'Public')} 2 { [void]($Explicit.IsPresent ? $type.DefineField($fdesc[0], $ftype, 'Public' ).SetOffset([Int32]$fdesc[1]) : $type.DefineField( $fdesc[0], $ftype, 'Public, HasFieldMarshal' ).SetCustomAttribute( [CustomAttributeBuilder]::new($ctor, [Object]([UnmanagedType]$fdesc[1])) )) } 3 { [void]$type.DefineField($fdesc[0], $ftype, 'Public, HasFieldMarshal' ).SetCustomAttribute( [CustomAttributeBuilder]::new($ctor, [UnmanagedType]$fdesc[1], $sc, ([Int32]$fdesc[2])) ) } } } $il = $type.DefineMethod('GetSize', 'Public, Static', [Int32], [Type[]]@()).GetILGenerator() $il.Emit([OpCodes]::ldtoken, $type) $il.Emit([OpCodes]::call, [Type].GetMethod('GetTypeFromHandle')) $il.Emit([OpCodes]::call, [Marshal].GetMethod('SizeOf', [Type[]]([Type]))) $il.Emit([OpCodes]::ret) $il = $type.DefineMethod('OfsOf', 'Public, Static', [Int32], [Type[]]@([String])).GetILGenerator() $local = $il.DeclareLocal([String]) $il.Emit([OpCodes]::ldtoken, $type) $il.Emit([OpCodes]::call, [Type].GetMethod('GetTypeFromHandle')) $il.Emit([OpCodes]::ldarg_0) $il.Emit([OpCodes]::call, [Marshal].GetMethod('OffsetOf', [Type[]]([Type], [String]))) $il.Emit([OpCodes]::stloc_0) $il.Emit([OpCodes]::ldloca_s, $local) $il.Emit([OpCodes]::call, [IntPtr].GetMethod('ToInt32', [Type[]]@())) $il.Emit([OpCodes]::ret) $il = $type.DefineMethod( 'op_Implicit', 'PrivateScope, Public, Static, HideBySig, SpecialName', $type, [Type]([IntPtr]) ).GetILGenerator() $il.Emit([OpCodes]::ldarg_0) $il.Emit([OpCodes]::ldtoken, $type) $il.Emit([OpCodes]::call, [Type].GetMethod('GetTypeFromHandle')) $il.Emit([OpCodes]::call, [Marshal].GetMethod('PtrToStructure', [Type[]]([IntPtr], [Type]))) $il.Emit([OpCodes]::unbox_any, $type) $il.Emit([OpCodes]::ret) [void]$type.CreateType() } } } function ConvertTo-BitMap { [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [ValidateNotNullOrEmpty()] [Object]$Value, [Parameter(Mandatory, Position=1)] [ValidateScript({![String]::IsNullOrEmpty($_)})] [ScriptBlock]$BitMap ) end { $vtor = [BitVector32]::new($Value) [PSCustomObject](ConvertFrom-StringData ( ($BitMap.Ast.FindAll({$args[0].CommandElements}, $true).ToArray().ForEach{ $fname, $fbits = $_.CommandElements[0, 2] $mov = !$mov ? [BitVector32]::CreateSection($fbits.Value) : [BitVector32]::CreateSection($fbits.Value, $mov) '{0} = {1}' -f $fname.Value, $vtor[$mov] }) | Out-String) ) } } function ConvertTo-PointerOrStructure { [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [ValidateNotNullOrEmpty()] [Byte[]]$Buffer, [Parameter(Position=1)] [ValidateNotNull()] [Type]$Type ) end { try { $gch = [GCHandle]::Alloc($Buffer, [GCHandleType]::Pinned) if ($Type) { $gch.AddrOfPinnedObject() -as $Type } else { $gch.AddrOfPinnedObject() } } catch { Write-Verbose $_ } finally { if ($gch) { $gch.Free() } } } } |