Skip to content

Instantly share code, notes, and snippets.

@anonhostpi
Last active January 24, 2024 13:42
Show Gist options
  • Save anonhostpi/6b687757ba0582adfd3793956e5b9d02 to your computer and use it in GitHub Desktop.
Save anonhostpi/6b687757ba0582adfd3793956e5b9d02 to your computer and use it in GitHub Desktop.
Demonstration of PSObjects, Dispatchers, ScriptBlocks, and SessionStates
Add-Type -AssemblyName "WindowsBase"
$synchash = [hashtable]::Synchronized(@{})
$t = "should not get this"
$sb = & {
@(
{
$t
},
{ <# dummy #> },
{ <# dummy #> }
)
}
$sb[1] = $sb[0].Ast.GetScriptBlock()
$sb[2] = [scriptblock]::Create( $sb[0].ToString() )
$obj = @(
(New-Object PSObject),
(New-Object PSObject),
(New-Object PSObject)
)
$obj[0] | Add-Member -MemberType ScriptMethod -Name "Method" -Value { $t }
$obj[0] | Add-Member -MemberType ScriptProperty -Name "Property" -Value { $t }
$obj[0] | Add-Member -MemberType ScriptProperty -Name "Selfed" -Value { $this.Property }
$obj[1] | Add-Member -MemberType ScriptMethod -Name "Method" -Value $sb[0].Ast.GetScriptBlock()
$obj[1] | Add-Member -MemberType ScriptProperty -Name "Property" -Value $sb[0].Ast.GetScriptBlock()
$obj[1] | Add-Member -MemberType ScriptProperty -Name "Selfed" -Value { $this.Property }.Ast.GetScriptBlock()
$obj[2] | Add-Member -MemberType ScriptMethod -Name "Method" -Value ( [scriptblock]::Create( $sb[0].ToString() ) )
$obj[2] | Add-Member -MemberType ScriptProperty -Name "Property" -Value ( [scriptblock]::Create( $sb[0].ToString() ) )
$obj[2] | Add-Member -MemberType ScriptProperty -Name "Selfed" -Value ( [scriptblock]::Create( { $this.Property }.ToString() ) )
$synchash.sb = $sb
$synchash.obj = $obj
$dispatchers = [hashtable]::Synchronized(@{})
$end = 0;
while( $end -lt 1 ){
# get the number of threads user wants to create (default is 1)
# use console (not WPF) C# for this, because you can't set default values for Read-Host
$end = Read-Host "How many threads do you want to create? (default: 1)"
if ( [string]::IsNullOrEmpty( $end ) ){
$end = 1
} elseif ( $end -lt 1 ){
Write-Host "Please enter a number greater than 0"
}
}
foreach( $i in 1..$end ){
$script = @(
"`$dispatchers.Dispatcher$i = [System.Windows.Threading.Dispatcher]::CurrentDispatcher",
"`$t = `"thread $i`"",
{
[void][System.Windows.Threading.Dispatcher]::Run()
}.ToString()
) -join "`n"
$script = [scriptblock]::Create( $script )
$runspace = [runspacefactory]::CreateRunspace( $Host )
$runspace.ApartmentState = "STA"
$runspace.Name = "thread $i"
$runspace.ThreadOptions = "ReuseThread"
$runspace.Open() | Out-Null
$runspace.SessionStateProxy.PSVariable.Set( "synchash", $synchash ) | Out-Null
$runspace.SessionStateProxy.PSVariable.Set( "dispatchers", $dispatchers ) | Out-Null
$powershell = [powershell]::Create()
$powershell.Runspace = $runspace
$powershell.AddScript( $script ) | Out-Null
$powershell.BeginInvoke() | Out-Null
# ensures dispatchers are set in the correct order
# we do this instead of $powershell.Invoke() or $powershell.EndInvoke(), because of Dispatcher.Run()
while( -not $dispatchers."Dispatcher$i" ){
Start-Sleep -Milliseconds 100
}
}
$t = "main"
$dispatchers.GetEnumerator() | ForEach-Object {
$name = $_.Key
$dispatcher = $_.Value
Write-Host
Write-Host "Dispatcher: $name"
$dispatcher.InvokeAsync({
Write-Host
Write-Host "InvokeAsync - bound"
Write-Host "`$sb[0].Invoke() - bound: " $synchash.sb[0].Invoke()
Write-Host "`$sb[1].Invoke() - unbound(AST): " $synchash.sb[1].Invoke()
Write-Host "`$sb[2].Invoke() - unbound(Create()):" $synchash.sb[2].Invoke()
}) | Out-Null
$dispatcher.InvokeAsync({
Write-Host
Write-Host "InvokeAsync - unbound"
Write-Host "`$sb[0].Invoke() - bound: " $synchash.sb[0].Invoke()
Write-Host "`$sb[1].Invoke() - unbound(AST): " $synchash.sb[1].Invoke()
Write-Host "`$sb[2].Invoke() - unbound(Create()):" $synchash.sb[2].Invoke()
}.Ast.GetScriptBlock()) | Out-Null
$dispatcher.InvokeAsync({
Write-Host
Write-Host "InvokeAsync - bound"
Write-Host "`$obj[0].Method() - bound: " $synchash.obj[0].Method()
Write-Host "`$obj[1].Method() - unbound(AST): " $synchash.obj[1].Method()
Write-Host "`$obj[2].Method() - unbound(Create()):" $synchash.obj[2].Method()
Write-Host "`$obj[0].Property - bound: " $synchash.obj[0].Property
Write-Host "`$obj[1].Property - unbound(AST): " $synchash.obj[1].Property
Write-Host "`$obj[2].Property - unbound(Create()):" $synchash.obj[2].Property
Write-Host "`$obj[0].Selfed - bound: " $synchash.obj[0].Selfed
Write-Host "`$obj[1].Selfed - unbound(AST): " $synchash.obj[1].Selfed
Write-Host "`$obj[2].Selfed - unbound(Create()):" $synchash.obj[2].Selfed
}) | Out-Null
$output = $dispatcher.InvokeAsync({
Write-Host
Write-Host "InvokeAsync - unbound"
Write-Host "`$obj[0].Method() - bound: " $synchash.obj[0].Method()
Write-Host "`$obj[1].Method() - unbound(AST): " $synchash.obj[1].Method()
Write-Host "`$obj[2].Method() - unbound(Create()):" $synchash.obj[2].Method()
Write-Host "`$obj[0].Property - bound: " $synchash.obj[0].Property
Write-Host "`$obj[1].Property - unbound(AST): " $synchash.obj[1].Property
Write-Host "`$obj[2].Property - unbound(Create()):" $synchash.obj[2].Property
Write-Host "`$obj[0].Selfed - bound: " $synchash.obj[0].Selfed
Write-Host "`$obj[1].Selfed - unbound(AST): " $synchash.obj[1].Selfed
Write-Host "`$obj[2].Selfed - unbound(Create()):" $synchash.obj[2].Selfed
}.Ast.GetScriptBlock())
While( -not $output.Task.IsCompleted ){
# calling GetAwaiter().GetResult() immediately will cause a deadlock
Start-Sleep -Milliseconds 100
}
$output.Task.GetAwaiter().GetResult()
$dispatcher.InvokeShutdown()
}
Write-Host
Write-Host "done"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment