SYSTEM 用户和登录用户之间的 Powershell 交互

Powershell interaction between SYSTEM user and logged on users

我想要一个 powershell 脚本 运行 作为 SYSTEM 用户在另一个用户会话中显示一个 Windows 表单并与它的控件进行交互。

我正在尝试使用 Solarwinds N-Able 自动执行 installation/repair Symantec Endpoint Protection。该平台使用安装在客户端上的代理软件来监控和执行客户端上的任务。

代理使用 NT AUTHORITY\SYSTEM 用户在机器上执行任务。到目前为止,SEP 的安装工作正常,但是 deinstall/install 阶段之间的重新启动仍然无法作为计算机上的普通用户进行控制。我希望当前活跃的用户能够控制这个重启周期。类似于 Windows 更新重启提示。

我的想法是在登录用户的桌面上显示一个窗口窗体,上面有执行或延迟重启的控件。我现在的问题是如何在另一个用户的会话中显示在 powershell 中定义的窗口窗体,以及如何在 SYSTEM 用户 运行 的脚本中恢复控件的操作。

我已经尝试过使用 msg 命令向系统上的所有用户发送消息。但这只是一种单向通信,并不真正意味着在这种情况下使用是猜测。

我找到了解决问题的方法。我使用了 boxdog 在评论中建议的 WTSSendMessage 函数。我将其与获取已登录用户的 sessionID 的脚本相结合。我将此脚本最小化以仅获取 "Active" 用户的会话 ID。然后使用它向用户发送消息。我在 Solarwinds 中对其进行了测试,到目前为止,它完美无缺。

我的编码技能很基础,但这是最终结果。

function Send-MessageBox
{
[CmdletBinding()]
[OutputType([string])]
Param
(        
    [Parameter(Mandatory=$true, Position=0)]
    [string]$title,
    [Parameter(Mandatory=$true, Position=1)]
    [string]$message,
    [Parameter(Mandatory=$true, Position=2)]
    [int]$duration,
    [Parameter(Mandatory=$true, Position=3)]
    [int]$style
)

Begin
{
    $typeDefinition = @"
        using System;
        using System.Runtime.InteropServices;

        public class WTSMessage {
            [DllImport("wtsapi32.dll", SetLastError = true)]
            public static extern bool WTSSendMessage(
                IntPtr hServer,
                [MarshalAs(UnmanagedType.I4)] int SessionId,
                String pTitle,
                [MarshalAs(UnmanagedType.U4)] int TitleLength,
                String pMessage,
                [MarshalAs(UnmanagedType.U4)] int MessageLength,
                [MarshalAs(UnmanagedType.U4)] int Style,
                [MarshalAs(UnmanagedType.U4)] int Timeout,
                [MarshalAs(UnmanagedType.U4)] out int pResponse,
                bool bWait
            );

            static int response = 0;

            public static int SendMessage(int SessionID, String Title, String Message, int Timeout, int MessageBoxType) {
                WTSSendMessage(IntPtr.Zero, SessionID, Title, Title.Length, Message, Message.Length, MessageBoxType, Timeout, out response, true);

                return response;
            }
        }
"@
}

Process
{
    if (-not ([System.Management.Automation.PSTypeName]'WTSMessage').Type)
    {
        Add-Type -TypeDefinition $typeDefinition
    }

    $RawOuput = (quser) -replace '\s{2,}', ',' | ConvertFrom-Csv
    $sessionID = $null

    Foreach ($session in $RawOuput) {  
        if(($session.sessionname -notlike "console") -AND ($session.sessionname -notlike "rdp-tcp*")) {
            if($session.ID -eq "Active"){    
                $sessionID = $session.SESSIONNAME
            }                        
        }else{
            if($session.STATE -eq "Active"){      
                $sessionID = $session.ID
            }        
        }   
    }
    $response = [WTSMessage]::SendMessage($sessionID, $title, $message, $duration, $style )
}
End
{
    Return $response
}
}

Send-MessageBox -title "Title" -message "Message" -duration 60 -style 0x00001034L