我在远程计算机上创建了一个pssession并输入了所有权。在该会话中,我使用start-process启动记事本。我可以确认记事本是使用get-process命令运行的,还是使用远程计算机中的taskmgr运行。但是,该过程的GUI端未显示。这是我一直在使用的序列:
$server = New-PSSession -ComputerName myserver -Credential mycreds
Enter-PSSession $server
[$server]: PS C:\>Start-Process notepad -Wait -WindowStyle Maximized
该过程正在运行,但是当RDP到框时,记事本无法打开。如果我从服务器打开记事本,则会开始一个新的记事本过程。我也试过使用这样的动词参数:
[$server]: PS C:\>Start-Process notepad -Wait -WindowStyle Maximized -Verb Open
同样的结果......过程开始,但没有记事本显示。我已经尝试了这个,然后远程进入盒子(但是从我的本地主机发出)以及远程进入服务器之前。
答案 0 :(得分:11)
这是因为远程计算机上的powershell会话不会进入任何可见桌面,而是进入不可见的系统桌面。 powershell远程会话的接收端是Windows服务。这个过程已经开始,但你和其他任何人都无法看到它。
如果您考虑一下,由于多个用户可以将RDP连接到同一台计算机,因此没有理由认为远程PowerShell会话最终会显示在任何用户桌面上。实际上,几乎在所有情况下你都不会想要它。
带有-i参数的psexec能够执行您想要的操作,但您必须指定要将其显示在哪个会话(用户)中。
答案 1 :(得分:6)
我知道这已经过时了,但我遇到了它自己寻找解决方案,所以我想为未来的可怜灵魂更新它。
此问题的本机解决方法是使用计划任务。那将使用活动会话
function Start-Process-Active
{
param
(
[System.Management.Automation.Runspaces.PSSession]$Session,
[string]$Executable,
[string]$Argument,
[string]$WorkingDirectory,
[string]$UserID
)
if (($Session -eq $null) -or ($Session.Availability -ne [System.Management.Automation.Runspaces.RunspaceAvailability]::Available))
{
$Session.Availability
throw [System.Exception] "Session is not availabile"
}
Invoke-Command -Session $Session -ArgumentList $Executable,$Argument,$WorkingDirectory,$UserID -ScriptBlock {
param($Executable, $Argument, $WorkingDirectory, $UserID)
$action = New-ScheduledTaskAction -Execute $Executable -Argument $Argument -WorkingDirectory $WorkingDirectory
$principal = New-ScheduledTaskPrincipal -userid $UserID
$task = New-ScheduledTask -Action $action -Principal $principal
$taskname = "_StartProcessActiveTask"
try
{
$registeredTask = Get-ScheduledTask $taskname -ErrorAction SilentlyContinue
}
catch
{
$registeredTask = $null
}
if ($registeredTask)
{
Unregister-ScheduledTask -InputObject $registeredTask -Confirm:$false
}
$registeredTask = Register-ScheduledTask $taskname -InputObject $task
Start-ScheduledTask -InputObject $registeredTask
Unregister-ScheduledTask -InputObject $registeredTask -Confirm:$false
}
}
答案 2 :(得分:3)
当您使用New-PSSession
然后将RDP用于同一台计算机时,您实际上正在使用两个独立且不同的用户登录会话。因此,您在PSSession中启动的Notepad.exe进程对您的RDP会话不可见(除了作为另一个正在运行的进程通过任务管理器或get-process
)。
一旦你进入服务器的RDP(在完成你在帖子中写的内容之后),就从那里开始另一个Notepad实例。然后转到PowerShell&运行此:get-process -name notepad |select name,processid
请注意,有两个实例,每个实例都在不同的会话中。
现在打开任务管理器并查看用户会话。您的RDP会话可能会列为会话1。
现在退出记事本并再次运行get-process
。您将看到一个实例,但是对于会话0.这是您在远程PSSession中创建的实例。
答案 3 :(得分:0)
我知道只有两种变通办法可以实现这一目标。
对于任务计划方式,我将说new-scheduledtask
仅在Windows 8+中可用。对于Windows 7,您需要连接到Schedule Service以创建这样的任务(此示例还在登录时启动该任务);
$sched = new-object -ComObject("Schedule.Service")
$sched.connect()
$schedpath = $sched.getFolder("\")
$domain = "myDomain"
$user="myuser"
$domuser= "${domain}\${user}"
$task = $sched.newTask(0) # 0 - reserved for future use
$task.RegistrationInfo.Description = "Start My Application"
$task.Settings.DisallowStartIfOnBatteries=$false
$task.Settings.ExecutionTimeLimit="PT0S" # there's no limit
$task.settings.priority=0 # highest
$task.Settings.IdleSettings.StopOnIdleEnd=$false
$task.settings.StopIfGoingOnBatteries=$false
$trigger=$task.Triggers.create(9) # 9 - at logon
$trigger.userid="$domuser" # at logon
$action=$task.actions.create(0) # 0 - execute a command
$action.path="C:\windows\system32\cmd.exe"
$action.arguments='/c "c:\program files\vendor\product\executable.exe"'
$action.WorkingDirectory="c:\program files\vendor\product\"
$task.principal.Id="Author"
$task.principal.UserId="$domuser"
$task.principal.LogonType=3 # 3 - run only when logged on
$task.principal.runlevel=1 # with elevated privs
# 6 - TASK_CREATE_OR_UPDATE
$schedpath.RegisterTaskDefinition("MyApplication",$viztask,6,$null,$null,$null)
创建服务要复杂得多,因此,我仅概述实现该服务所需的调用。简单的方法是在powershell画廊上使用invoke-asservice脚本:https://www.powershellgallery.com/packages/InvokeAsSystem/1.0.0.0/Content/Invoke-AsService.ps1
使用WTSOpenServer
和WTSEnumerateSessions
获取计算机上的会话列表。您还需要在每个会话上使用WTSQuerySessionInformation
来获取其他信息,例如用户名。记住要使用WTSFreeMemory
和WTSCloseServer
释放资源,最终会得到一些看起来像这样的数据(来自qwinsta命令);
SESSIONNAME USERNAME ID STATE
services 0 Disc
>rdp-tcp#2 mheath 1 Active
console 2 Conn
rdp-tcp 65536 Listen
有关如何获取此数据的SO帖子; How do you retrieve a list of logged-in/connected users in .NET?
在这里实现逻辑来确定要定位的会话,是要在Active桌面上显示它,而不管它是如何通过RDP或在本地控制台上显示的?另外,如果没有人登录怎么办? (我已经设置了自动登录,并在登录时调用了锁定桌面命令,以便可以使用已登录的用户。)
您需要找到以该用户身份在桌面上运行的进程的进程ID。您可以使用资源管理器,但您的计算机可能是服务器核心,默认情况下该资源管理器未运行。瞄准winlogon也不是一个好主意,因为它以系统身份运行,或者以dwm作为非特权用户身份运行。
以下命令需要在服务中运行,因为它们需要只有系统服务才具有的特权。使用OpenProcess
获取流程句柄,使用OpenProcessToken
获取流程的安全令牌,使用DuplicateTokenEx
复制该令牌,然后调用“ CreateProcessAsUser”`,最后关闭您的句柄。
此代码的后半部分在invoke-asservice powershell脚本中实现。
您还可以使用sysinternals工具psexec,我没有将它列为第三种方法,因为它只是使创建服务的过程自动化。