Powershell использует Start-Process в PSSession для открытия Блокнота

Я создал pssession на удаленном компьютере и вошел в это владение. Из этого сеанса я использую start-process для запуска блокнота. Я могу подтвердить, что блокнот работает с помощью команды get-process, а также с помощью taskmgr на удаленном компьютере. Однако сторона процесса с графическим интерфейсом не отображается. Это последовательность, которую я использовал:

$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

Тот же результат, хотя ... Процесс запускается, но блокнот не отображается. Я пробовал это при удаленном подключении к ящику (но выданном с моего локального хоста), а также перед удаленным подключением к серверу.


person Colyn1337    schedule 11.09.2013    source источник


Ответы (4)


Это связано с тем, что ваш сеанс powershell на удаленном компьютере переходит не на какой-либо видимый рабочий стол, а на невидимый системный рабочий стол. Принимающая сторона вашего удаленного сеанса powershell — это служба Windows. Процесс запущен, но ни вы, ни кто-либо другой никогда его не увидит.

И если вы думаете об этом, поскольку несколько пользователей могут подключаться к одному и тому же компьютеру по RDP, на самом деле нет причин предполагать, что удаленный сеанс powershell в конечном итоге будет отображаться на любом из рабочих столов пользователей. На самом деле, почти во всех случаях вы все равно бы этого не хотели.

psexec с параметром -i может делать то, что вы хотите, но вы должны указать, в каком из сеансов (пользователей) вы хотите, чтобы он отображался.

person Lars Truijens    schedule 11.09.2013
comment
Я добавил столбец идентификатора сеанса в Taskmgr, и процессы блокнота, которые я запускал, находились в сеансе 0. Я надеялся, что, используя мои учетные данные, он автоматически прикрепит процесс к моему сеансу. - person Colyn1337; 12.09.2013
comment
Есть ли способ получить графический интерфейс с помощью Powershell, а не psexec? - person Trevor Hickey; 09.05.2015

Я знаю, что это старо, но я сам наткнулся на него в поисках решения, поэтому я хотел обновить его для будущих бедолаг.

Нативным обходным путем для этой проблемы является использование запланированного задания. Это будет использовать активный сеанс

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
    }

}
person Ben    schedule 31.05.2016

Когда вы используете New-PSSession, а затем RDP на том же компьютере, вы фактически используете два отдельных и разных сеанса входа пользователя. Таким образом, процесс Notepad.exe, который вы запустили в PSSession, не виден для вашего сеанса RDP (кроме как другого запущенного процесса через диспетчер задач или get-process).

После того, как вы подключились к серверу по RDP (после выполнения того, что вы написали в своем посте), запустите оттуда еще один экземпляр Блокнота. Затем перейдите в PowerShell и запустите это: get-process -name notepad |select name,processid

Обратите внимание, что есть два экземпляра, каждый в другом сеансе.

Теперь откройте диспетчер задач и посмотрите на сеансы пользователей. Ваш сеанс RDP, вероятно, будет указан как сеанс 1.

Теперь закройте Блокнот и снова запустите get-process. Вы увидите один экземпляр, но для сеанса 0. Это тот, который вы создали в своем удаленном сеансе PSSession.

person alroc    schedule 11.09.2013

Есть только 2 обходных пути, о которых я знаю, которые могут сделать это.

  1. Создайте расписание задач от имени пользователя, вошедшего в систему, без триггера и запускайте его вручную.
  2. Создайте службу, которая запускает процесс с дублированным токеном вошедшего в систему пользователя.

Что касается способа планирования задач, я скажу, что new-scheduledtask доступен только в Windows 8+. Для Windows 7 вам необходимо подключиться к службе расписания, чтобы создать задачу, подобную этой (этот пример также запускает задачу при входе в систему);

                $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)

Создание службы намного сложнее, поэтому я только обрисую в общих чертах вызовы, необходимые для ее реализации. Самый простой способ — использовать скрипт invoke-asservice в галерее powershell: 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 о получении этих данных; Как получить список вошедших/подключенных пользователей в .NET?

Здесь вы реализуете свою логику, чтобы определить, на какой сеанс нацеливаться, хотите ли вы отображать его на рабочем столе Active независимо от того, как он представлен, через RDP или на локальной консоли? А также, что вы будете делать, если никто не войдет в систему? (Я настроил автоматический вход в систему и вызываю команду блокировки рабочего стола при входе в систему, чтобы вошедший в систему пользователь был доступен.) Вам нужно найти идентификатор процесса, который выполняется на рабочем столе от имени этого пользователя. Вы можете выбрать проводник, но на вашем компьютере может быть Server Core, проводник которого не запущен по умолчанию. Также не рекомендуется ориентироваться на winlogon, потому что он работает как система, или на dwm, поскольку он работает как непривилегированный пользователь. Следующие команды необходимо запускать в службе, поскольку они требуют привилегий, которые есть только у системных служб. Используйте OpenProcess, чтобы получить дескриптор процесса, используйте OpenProcessToken, чтобы получить токен безопасности процесса, продублируйте токен, используя DuplicateTokenEx, затем вызовите ``CreateProcessAsUser`` и, наконец, закройте свои дескрипторы.

Вторая половина этого кода реализована в сценарии powershell invoke-asservice.

Вы также можете использовать инструмент sysinternals psexec, я не указал его как третий способ, потому что он просто автоматизирует процесс создания службы.

person silicontrip    schedule 11.11.2020