如何在不同PowerShell Runspace中复用本地函数?
在PowerShell Runspace中复用函数(WinForms场景)
问题背景
在WinForms与Runspace结合的开发中,当前只能将sqlExecuteRead函数完整复制到每个脚本块(如$QueryServer)才能在Runspace中执行,希望仅定义一次函数,就能在多个新建Runspace中直接引用,避免重复代码。
解决方案
以下两种方法可实现Runspace间的函数复用:
方法1:通过PowerShell实例预加载函数定义
创建[powershell]实例时,先将函数定义添加到执行管道,再添加目标脚本块。Runspace执行时会先加载函数,后续脚本块可直接调用。
修改PerformRunspaceAction中的代码:
$Powershell = [powershell]::Create().AddScript($function:sqlExecuteRead).AddScript($scriptRun)
方法2:通过SessionStateProxy注入函数
利用Runspace的SessionStateProxy将函数作为变量注入到Runspace会话状态,脚本块可直接调用该函数。
修改PerformRunspaceAction函数,在设置hash变量后添加注入代码:
$newRunspace.SessionStateProxy.SetVariable("sqlExecuteRead", $function:sqlExecuteRead)
修改后的完整代码
$hash = [hashtable]::Synchronized(@{}) $Script:Jobs = [system.collections.arraylist]::Synchronized((New-Object System.Collections.ArrayList)) # 主GUI窗体对象 $hash.Form = New-Object System.Windows.Forms.Form # 存储查询返回结果 $script:hash.ServerResults = $null # 创建按钮 $hash.QueryServerButton = New-Object System.Windows.Forms.Button # 按钮属性设置 $hash.QueryServerButton.Location = New-Object System.Drawing.Point(40,300) $hash.QueryServerButton.Size = New-Object System.Drawing.Size (105,40) $hash.QueryServerButton.Text = "获取服务器" $hash.QueryServerButton.Add_Click({ PerformRunspaceAction($QueryServer) }) $hash.ServerTab.Controls.Add($hash.QueryServerButton) $QueryServer = { $connectionString = "Server=xxx\xxx;Database=xxxx;Trusted_Connection=True;" $sql = @" SELECT * FROM DB_DB; "@ do { Start-Sleep -Seconds 30 $script:hash.ServerResults = sqlExecuteRead $connectionString $sql $pars } while ($true) } Function PerformRunspaceAction { param($scriptRun) $newRunspace = [runspacefactory]::CreateRunspace() $newRunspace.ApartmentState = "STA" $newRunspace.ThreadOptions = "ReuseThread" $newRunspace.Open() $newRunspace.SessionStateProxy.SetVariable("hash", $hash) # 注入sqlExecuteRead函数到Runspace会话状态 $newRunspace.SessionStateProxy.SetVariable("sqlExecuteRead", $function:sqlExecuteRead) $Powershell = [powershell]::Create().AddScript($scriptRun) $Powershell.Runspace = $newRunspace [void]$Script:Jobs.Add(( [PSCustomObject]@{ PowerShell = $PowerShell Runspace = $PowerShell.BeginInvoke() } )) } function sqlExecuteRead($connectionString, $sqlCommand, $pars) { $connection = new-object system.data.SqlClient.SQLConnection($connectionString) $connection.Open() $command = new-object system.data.sqlclient.sqlcommand($sqlCommand, $connection) if ($pars -and $pars.Keys) { foreach($key in $pars.keys) { # 避免varchar参数注入 $par = $command.Parameters.Add("@$key", [system.data.SqlDbType]::VarChar, 512); $par.Value = $pars[$key]; } } $adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command $dataset = New-Object System.Data.DataSet $adapter.Fill($dataset) | Out-Null $connection.Close() return $dataset.tables[0].rows }
补充说明
$function:sqlExecuteRead用于获取函数的定义字符串,通过SessionStateProxy.SetVariable将其注入目标Runspace,使脚本块可直接调用。- 若需复用多个函数,可批量注入,或封装为PowerShell模块,在Runspace中通过
Import-Module加载,这种方式更适合复杂场景。
内容的提问来源于stack exchange,提问作者Jurm
相关产品推荐
相关产品推荐

