托管PowerShell运行时是否可以将PSObject其转换回原始类型?
例如:
我有一个cmdlet调用WriteObject并推送管道中的ClassXzy集合.当我PowerShell.Invoke从主机端调用时,我检索PSObject带有BaseObject属性的s 集合.铸造BaseObject于ClassXyz失败.
有没有办法将每个属性值映射到其对应的原始对象?
我假设PowerShell以某种方式执行此操作,因为您可以将PSObjects 传递给cmdlet并将它们转换为参数类型.但是怎么样?
我花时间用反射器撕裂了PS组件,但还没有真正确定这种魔法是如何发生的.
有任何想法吗?
编辑:我忘记了一个非常重要的细节.在PSObject我正在测试对,因此是一个远程对象BaseObject类型命名Deserialized.ClassXyz.这就是我看到这种奇怪行为的原因.
我正在用C#编写PowerShell提供程序.提供程序通过类似驱动器的接口公开应用程序域对象.例如:
my:\Users\joe@blow.com
my:\Customers\Marty
Run Code Online (Sandbox Code Playgroud)
这些数据最终来自数据库.
我一直无法找到任何有关何时应该访问数据库以及何时应该缓存数据的指导.我发现PowerShell多次调用ItemExists和GetChildNames之类的方法; 经常反复为同一个命令.例如,仅仅因为他们点击Tab进行自动完成而去数据库5到6次是不切实际的.
但与此同时,作为命令提示符下的用户,如果我键入Get-ChildItem(dir)并查看列表,那么在PowerShell外部执行一些操作以便我知道数据已刷新,另一个目录列表应该会看到任何更改数据库.
我觉得,如果我知道用正确的术语来描述我的问题(用PowerShell的说法),我可以谷歌答案或找到一个现有的重复问题,但我卡住了.
我的 C# 文件中有以下函数:
private void RunScriptFile(string scriptPath, string computerName, PSCredential credential)
{
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
string scriptCommand = "Invoke-Command -ComputerName " + computerName + " -FilePath " + scriptPath + " -ArgumentList " + credential;
pipeline.Commands.AddScript(scriptCommand);
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
}
Run Code Online (Sandbox Code Playgroud)
我使用上面 C# 代码中传递的凭据调用以下 PowerShell 脚本;脚本.ps1
Param([PSCredential]$Credentials)
<code part using credentials>
Run Code Online (Sandbox Code Playgroud)
之后pipeline.Invoke(),C# 代码只是关闭,没有任何操作,也不会抛出任何错误。
我在拨打电话时是否做错了什么?如果从 PowerShell 调用相同的调用,如下所示,则可以正常工作:
Invoke-Command -ComputerName <computerName> -FilePath <scipt.ps1> -ArgumentList …Run Code Online (Sandbox Code Playgroud) 我有一个C#应用程序,并希望允许人们在我的应用程序中编写powershell代码.
有没有人知道有一个适合该法案的intellisense的powershell编辑器?
我基本上是在使用运行空间构建自己的并行 foreach 管道函数。
我的问题是:我这样调用我的函数:
somePipeline | MyNewForeachFunction { scriptBlockHere } | pipelineGoesOn...
Run Code Online (Sandbox Code Playgroud)
如何将$_参数正确传递到 ScriptBlock 中?当 ScriptBlock 包含作为第一行时它起作用
param($_)
Run Code Online (Sandbox Code Playgroud)
但是,正如你可能已经注意到,在PowerShell中内置的foreach对象和地点,对象也不会需要传递给他们的每脚本块这样的参数声明。
感谢您提前回答 fjf2002
编辑:
目标是:我想让函数的用户感到舒适MyNewForeachFunction——他们不需要param($_)在他们的脚本块中写一行。
在里面MyNewForeachFunction,ScriptBlock 当前被调用通过
$PSInstance = [powershell]::Create().AddScript($ScriptBlock).AddParameter('_', $_)
$PSInstance.BeginInvoke()
Run Code Online (Sandbox Code Playgroud)
编辑2:
关键是,例如内置函数的ForEach-Object实现如何实现$_不需要在其 ScriptBlock 参数中声明为参数,我也可以使用该功能吗?
(如果答案是,它ForEach-Object是一个内置函数并使用了一些我无法使用的魔法,那么在我看来,这将使整个 PowerShell 语言失去资格)
编辑3:
感谢 mklement0,我终于可以构建我的通用 foreach 循环。这是代码:
function ForEachParallel {
[CmdletBinding()]
Param(
[Parameter(Mandatory)] [ScriptBlock] $ScriptBlock,
[Parameter(Mandatory=$false)] [int] $PoolSize = 20,
[Parameter(ValueFromPipeline)] $PipelineObject
)
Begin {
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $poolSize) …Run Code Online (Sandbox Code Playgroud) 我正在尝试使用“ GET-PSDrive”命令通过PowerShell 6.0获取服务器的驱动器信息。直接在PowerShell中运行命令,我在输出表中看到“ Used”和“ Free”的值,但是使用Microsoft.Powershell.Sdk在代码中运行相同的命令,但“ Used”和“ Free”字段却没有可用。
我在PSObject.Properties数组下看到了两个项目,但是尝试访问该值时会收到异常:
“此线程中没有可用于运行脚本的运行空间。您可以在System.Management.Automation.Runspaces.Runspace类型的DefaultRunspace属性中提供一个。您尝试调用的脚本块是:##”
以下是我正在使用的POC代码:
using (var psCon = PowerShell.Create())
{
psCon.AddCommand("GET-PSDrive");
var psReturn = psCon.Invoke();
foreach (var psObj in psReturn)
{
var driveUsedValue = psObj.Properties["Used"].Value;
}
}
Run Code Online (Sandbox Code Playgroud)
我期望获得该属性的值,但是每次评估该值时,我都会收到一条错误消息,指出没有可用的运行空间。检查该属性,我确实看到它是一个ScriptProperty,那么您如何获得该生成的值?
我正在使用 GUI 创建一个 powershell 脚本,它将用户配置文件从选定的源磁盘复制到目标磁盘。我使用 VS Community 2019 在 XAML 中创建了 GUI。该脚本的工作原理如下:您选择源磁盘、目标磁盘、用户配置文件和要复制的文件夹。当您按下“开始”按钮时,它会调用一个名为 Backup_data 的函数,在其中创建一个运行空间。在这个运行空间中,只有一个小的复制项,其中包含您选择的参数。
该脚本工作正常,所有想要的项目都被正确复制。问题是 GUI 在复制过程中冻结(没有“无响应”消息或其他任何信息,它只是完全冻结;无法单击任何地方,无法移动窗口)。我已经看到使用运行空间可以解决这个问题,但对我来说却不然。我错过了什么吗?
这是函数Backup_Data:
Function BackupData {
##CREATE RUNSPACE
$PowerShell = [powershell]::Create()
[void]$PowerShell.AddScript( {
Param ($global:ReturnedDiskSource, $global:SelectedUser, $global:SelectedFolders, $global:ReturnedDiskDestination)
##SCRIPT BLOCK
foreach ($item in $global:SelectedFolders) {
Copy-Item -Path "$global:ReturnedDiskSource\Users\$global:SelectedUser\$item" -Destination "$global:ReturnedDiskDestination\Users\$global:SelectedUser\$item" -Force -Recurse
}
}).AddArgument($global:ReturnedDiskSource).AddArgument($global:SelectedUser).AddArgument($global:SelectedFolders).AddArgument($global:ReturnedDiskDestination)
#Invoke the command
$PowerShell.Invoke()
$PowerShell.Dispose()
}
Run Code Online (Sandbox Code Playgroud) 我尝试结合 stackoverflow 的两个答案(第一和第二)
InitialSessionState iss = InitialSessionState.CreateDefault();
// Override ExecutionPolicy
PropertyInfo execPolProp = iss.GetType().GetProperty(@"ExecutionPolicy");
if (execPolProp != null && execPolProp.CanWrite)
{
execPolProp.SetValue(iss, ExecutionPolicy.Bypass, null);
}
Runspace runspace = RunspaceFactory.CreateRunspace(iss);
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
//Here's how you add a new script with arguments
Command myCommand = new Command(scriptfile);
CommandParameter testParam = new CommandParameter("key","value");
myCommand.Parameters.Add(testParam);
pipeline.Commands.Add(myCommand);
// Execute PowerShell script
results = pipeline.Invoke();
Run Code Online (Sandbox Code Playgroud)
在我的 powershell 脚本中,我有以下参数:
Param(
[String]$key
)
Run Code Online (Sandbox Code Playgroud)
但是,当我执行此操作时,会出现以下异常:
System.Management.Automation.CmdletInvocationException: Cannot validate argument on parameter 'Session'. …Run Code Online (Sandbox Code Playgroud) 我可以从我的桌面完美运行这个脚本:
private void Sleep_Click(object sender, EventArgs e)
{
PowerShell ps = PowerShell.Create();
ps.AddScript(@"D:\Desktop\alllightsoff.ps1");
ps.Invoke();
}
Run Code Online (Sandbox Code Playgroud)
但是当我更改程序文件的路径时,它什么也没做......有什么想法吗?
private void Sleep_Click(object sender, EventArgs e)
{
PowerShell ps = PowerShell.Create();
ps.AddScript(@"C:\Program Files (x86)\Home Control\alllightsoff.ps1");
ps.Invoke();
}
Run Code Online (Sandbox Code Playgroud)
可能与权限有关,该脚本不需要管理员权限即可运行,当我从程序文件夹手动运行 ps1 脚本时就可以正常工作。
我什至尝试了 %AppData% 文件夹并得到相同的结果,ps1 文件无法运行。
在使用使用 RunSpace 的脚本时,我发现它占用越来越多的系统内存。据我了解,这是由于打开的 RunSpace 完成后不会关闭。它们保留在内存中,积累兆字节。
如何正确关闭RunSpace?然而,我不知道需要多长时间——1秒或1小时。完成后自行关闭。
作为示例,我将给出任意脚本。
第一个脚本是我如何在 RunSpace 完成时关闭它(它显然不起作用)。
$Array = 1..10000
$PowerShell = [PowerShell]::Create()
$RunSpace = [Runspacefactory]::CreateRunspace()
$RunSpace.Open()
$RunSpace.SessionStateProxy.SetVariable('Array', $Array)
$RunSpace.SessionStateProxy.SetVariable('PowerShell', $PowerShell)
$PowerShell.Runspace = $RunSpace
[void]$PowerShell.AddScript({
# Fill the system memory so that it can be seen in the Task Manager.
$Array += $Array
$Array
# Closing the Process, which should close the RunSpace, but this does not happen.
$Powershell.Runspace.Dispose()
$PowerShell.Dispose()
})
$Async = $PowerShell.BeginInvoke()
# Other jobs in the main thread...
Run Code Online (Sandbox Code Playgroud)
从系统内存来看,第二个脚本似乎更正确。然而,它当然不适用于生活,因为它Start-Sleep 10会冻结主进程。
$Array = …Run Code Online (Sandbox Code Playgroud) powershell ×10
powershell-sdk ×10
c# ×5
runspace ×2
.net ×1
.net-core ×1
foreach ×1
pipe ×1
wpf ×1
xaml ×1