Есть только 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