2

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?

klyd
  • 3,939
  • 3
  • 24
  • 34
  • 1
    from inside the system account you can use scheduled tasks under the aforementioned users credentials. Be sure to use the "Run only when user is logged on" setting. – Knuckle-Dragger Aug 05 '14 at 20:05
  • Do you really want to do this? I'm fairly certain your script will need access to the credentials for every user, which is a nightmare. – alx9r Dec 01 '14 at 01:23

2 Answers2

0

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.

BSAFH
  • 725
  • 3
  • 6
  • 19
0

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

Fabian S.
  • 23
  • 5