简体   繁体   中英

Is there a way to start a PowerShell Runspace in a different User's Context?

I'm trying to build a script that will display a WPF window to a user indicating progress. The script itself will run from the system account, but will need to show progress to the end user. The script will be launched in the System account either through PSExec or SCCM (Note in both cases the initial script can't be run with 'user interaction' enabled. Yeah. I know. Its a requirement though).

Is there a way to create that window from the System context so that a user can interact with it? Alternatively, can a Runspace be opened in another User's context? Or are neither of these a viable route?

Something like:

Start-Process powershell.exe -credential {cred. maybe a stored cred?} -nonewwindow -working directory {wherever you want it to start from. cred value must have access} -argumentlist "-file yourfile.ps1"

"yourfile.ps1" would have the code block for interacting with the user. -nonewwindow is of course up to you. I'm not sure how you'd communicate across userspace. Im still more sysadmin than programmer so I'd do something hacky. Like re-draw the window every 10% or something. Depends what feedback you want from the user.

The only solution I found was this function: Send-TSMessageBox. http://pinvoke.net/default.aspx/wtsapi32.WTSSendMessage

This could be run as SYSTEM but shows up on the user's desktop. One downside: On a virtual machine, the message box shows up in session 0 (which is the Hyper-V "integrated" connection window). If you are connected via RDP (mstc) session you will not see the message box. But on a Citrix desktop it works. The message box pops up within the user session and not on the Citrix host.

Here's the full function:

Function Send-TSMessageBox {
    <#
    .SYNOPSIS   
        Send a message or prompt to the interactive user with the ability to get the results.

    .DESCRIPTION
        Allows the administrator to send a message / prompt to an interactive user. 

    .EXAMPLE   
        "Send a message immediately w/o waiting for a responce."
        Send-TSMessageBox -Title "Email Problem" -Message "We are currently having delays and are working on the issue."

        "Send a message waiting 60 seconds for a reponse of [Yes / No]."
        $Result = Send-TSMessageBox -Title "System Updated" -Message "System requires a reboot. Would you like to the reboot system now?" `
        -ButtonSet 4 -Timeout 60 -WaitResponse $true 

    .ButtonSets
        0 = OK
        1 = Ok/Cancel
        2 = Abort/Retry/Ignore
        3 = Yes/No/Cancel
        4 = Yes/No
        5 = Retry/Cancel
        6 = Cancel/Try Again/Continue    

    .Results
        "" = 0
        "Ok" = 1
        "Cancel" = 2  
        "Abort" = 3
        "Retry" = 4    
        "Ignore" = 5
        "Yes" = 6
        "No" = 7
        "Try Again" = 10
        "Continue" = 11
        "Timed out" = 32000
        "Not set to wait" = 32001 

    .NOTES   
        Author: Raymond H Clark
        Twitter: @Rowdybullgaming

    .RESOURCES
        http://technet.microsoft.com/en-us/query/aa383488 
        http://technet.microsoft.com/en-us/query/aa383842
        http://pinvoke.net/default.aspx/wtsapi32.WTSSendMessage
    #> 
    Param([string]$Title = "Title", [string]$Message = "Message", [int]$ButtonSet = 0, [int]$Timeout = 0, [bool]$WaitResponse = $false)

        $Signature = @"
        [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);

            [DllImport("kernel32.dll")]
            public static extern uint WTSGetActiveConsoleSessionId();
"@

            [int]$TitleLength = $Title.Length;
            [int]$MessageLength = $Message.Length;
            [int]$Response = 0;

            $MessageBox = Add-Type -memberDefinition $Signature -name "WTSAPISendMessage" -namespace "WTSAPI" -passThru   
            $SessionId = $MessageBox::WTSGetActiveConsoleSessionId()

            $MessageBox::WTSSendMessage(0, $SessionId, $Title, $TitleLength, $Message, $MessageLength, $ButtonSet, $Timeout, [ref] $Response, $WaitResponse)

            $Response
}


Unfortunately the design of the messsage box is very limited. Actually it looks ugly :-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM