【问题标题】:Powershell Using Start-Process in PSSession to Open NotepadPowershell 在 PSSession 中使用 Start-Process 打开记事本
【发布时间】:2013-09-11 18:12:07
【问题描述】:

我在远程计算机上创建了一个 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'd 到盒子时,记事本没有打开。如果我从服务器打开记事本,则会开始一个新的记事本进程。我也尝试过使用这样的动词参数:

[$server]: PS C:\>Start-Process notepad -Wait -WindowStyle Maximized -Verb Open

结果相同... 进程开始,但没有记事本显示。我在远程进入盒子(但从我的本地主机发出)以及远程进入服务器之前尝试过这个。

【问题讨论】:

    标签: powershell powershell-3.0 start-process


    【解决方案1】:

    那是因为您在远程机器上的 powershell 会话不会转到任何可见的桌面,而是转到不可见的系统桌面。 Powershell 远程会话的接收端是 Windows 服务。该过程已启动,但您或其他任何人都无法看到它。

    如果您考虑一下,由于多个用户可以 RDP 到同一台计算机,因此没有理由假设远程 powershell 会话最终会显示在任何用户桌面上。实际上,在几乎所有情况下,您都不会想要它。

    psexec 带有 -i 参数可以做你想做的事,但你必须指定你希望它出现在哪个会话(用户)中。

    【讨论】:

    • 我在 Taskmgr 的会话 id 列中添加了我正在启动的记事本进程在会话 0 中。我希望通过使用我的凭据,它会自动将进程附加到我的会话中。跨度>
    • 有没有办法让 GUI 使用 Powershell 而不是 psexec?
    【解决方案2】:

    我知道这已经过时了,但我自己在寻找解决方案时遇到了它,所以我想为未来的可怜人更新它。

    解决此问题的本机解决方法是使用计划任务。这将使用活动会话

    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
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      当您在同一台计算机上使用New-PSSession 然后 RDP 时,您实际上是在使用两个独立且不同的用户登录会话。因此,您在 PSSession 中启动的 Notepad.exe 进程对您的 RDP 会话不可见(通过任务管理器或get-process 作为另一个正在运行的进程除外)。

      将 RDP 导入服务器后(在完成您在帖子中所写的操作之后),从那里启动另一个记事本实例。然后放到 PowerShell 并运行:get-process -name notepad |select name,processid

      请注意,有两个实例,每个实例位于不同的会话中。

      现在打开任务管理器并查看用户会话。您的 RDP 会话可能会被列为会话 1。

      现在退出记事本并再次运行get-process。您将看到一个实例,但对于会话 0。那是您在远程 PSSession 中创建的实例。

      【讨论】:

        【解决方案4】:

        据我所知,只有 2 种解决方法可以做到这一点。

        1. 以登录用户的身份创建任务计划,无需触发器并手动触发。
        2. 创建一个使用登录用户的重复令牌启动进程的服务。

        对于任务计划方式,我会说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

        使用WTSOpenServerWTSEnumerateSessions 获取机器上的会话列表。您还需要在每个会话上使用WTSQuerySessionInformation 来获取用户名等附加信息。请记住使用WTSFreeMemoryWTSCloseServer 释放您的资源,您最终会得到一些看起来像这样的数据(这是来自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?

        这是您实现逻辑以确定目标会话的地方,您是否希望在活动桌面上显示它,而不管它是通过 RDP 还是在本地控制台上呈现的?还有如果没有人登录你会怎么做? (我设置了自动登录并在登录时调用锁定桌面命令,以便登录用户可用。) 您需要找到以该用户身份在桌面上运行的进程的进程 ID。您可以选择资源管理器,但您的机器可能是 Server Core,默认情况下该资源管理器未运行。定位 winlogon 也不是一个好主意,因为它作为系统运行,或者 dwm 因为它作为非特权用户运行。 以下命令需要在服务中运行,因为它们需要只有系统服务才有的权限。使用OpenProcess获取进程句柄,使用OpenProcessToken获取进程的安全令牌,使用DuplicateTokenEx复制令牌然后调用``CreateProcessAsUser```最后关闭你的句柄。

        这段代码的后半部分在invoke-asservice powershell脚本中实现。

        您也可以使用 sysinternals 工具 psexec,我没有将其列为第三种方式,因为它只是自动执行创建服务的过程。

        【讨论】:

          猜你喜欢
          • 2019-08-16
          • 1970-01-01
          • 2019-09-29
          • 1970-01-01
          • 2018-06-15
          • 2015-08-12
          • 2021-04-15
          • 2020-08-17
          • 1970-01-01
          相关资源
          最近更新 更多