据我所知,只有两种解决方法可以实现这一点。
- 以登录用户身份创建任务计划,无需触发器并手动触发。
- 创建一个使用登录用户的重复令牌启动进程的服务。
对于任务计划方式,我会说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 帖子;如何在 .NET 中检索已登录/已连接用户的列表?
这是您实现逻辑以确定目标会话的地方,您是否希望在活动桌面上显示它,而不管它是如何呈现的,通过 RDP 还是在本地控制台上?还有如果没有人登录你会怎么做?(我设置了自动登录并在登录时调用锁定桌面命令,以便登录用户可用。)您需要找到以该用户身份在桌面上运行的进程的进程 ID。您可以选择资源管理器,但您的机器可能是 Server Core,默认情况下该资源管理器未运行。定位 winlogon 也不是一个好主意,因为它作为系统运行,或者 dwm 因为它作为非特权用户运行。以下命令需要在服务中运行,因为它们需要只有系统服务才有的权限。用于OpenProcess
获取进程句柄,OpenProcessToken
要获取进程的安全令牌,请使用复制令牌,DuplicateTokenEx
然后调用“CreateProcessAsUser”,最后关闭您的句柄。
此代码的后半部分在 invoke-asservice powershell 脚本中实现。
您也可以使用 sysinternals 工具 psexec,我没有将其列为第三种方式,因为它只是自动执行创建服务的过程。