ElevateMe provides a way to run processes with the highest available privileges
 of the current user without UAC confirmation.
This enables automation scripts to run unattended.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Installation:
Run "Install_ElevateMe".
- Prompts the user for UAC confirmation only once.
- File "%LOCALAPPDATA%\ElevateMe.vbs" is created (where %LOCALAPPDATA% is
   usually C:\Users\<your user name>\AppData\Local).
- Scheduled Task "ElevateMe" is created.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Usage:
%LOCALAPPDATA%\ElevateMe.vbs  envVarName  [wndStyle]  [sync]

  envVarName  Name of an environment variable that contains the command to be
               executed with highest available privileges.
              NOTE: Do NOT pass the command itself. Assign the command to an
               environment variable in the current process first, and only pass
               the name of the variable without expanding it to its value.

  wndStyle    (optional) Integer value that specifies the window appearance of
               the elevated process.
              When in daubt choose 1, 3, or 7. Default value is 1.
              Possible values are:
               0 Hide the window and activate another window.
               1 Activate and display the window.
               2 Activate and minimize.
               3 Activate and maximize.
               4 Restore. The active window remains active.
               5 Activate and Restore.
               6 Minimize and activate the next top-level window in the Z order.
               7 Minimize. The active window remains active.
               8 Display the window in its current state. The active window
                  remains active.
               9 Restore and Activate. Specify this flag when restoring a
                  minimized window.
              10 Sets the show-state based on the state of the program that
                  started the application.

  sync        (optional) Integer value that specifies whether or not the
               elevated process is synchronized with the execution of the
               calling process. Value 0 is for "false", any other value is for
               "true". By default the elevated process is synchronized ("true").
               That is, the calling process waits for the elevated process to be
               finished before it resumes the execution.

"ElevateMe.vbs" restores the current directory of the calling process for the
 elevated process. Arguments may be part of the command to be executed,
 separated by spaces.

Return value:
- If an error occurs in the VBScript code the script will return the VBScript
  error number (like 13 for wrong type of the second or third argument, or
  450 for not passing any argument).
- Else if the Run method fails the script will return a HRESULT code
  (like -2147024894 for File Not Found).
- Else if 0 is passed for sync the script will return 0.
- Else the script returns the exit code of the elevated process to the caller.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

FAQ:

How does this even work?
 A scheduled task can be configured to run with the highest available privileges
 of a user. Once the task is created its execution doesn't invoke the UAC prompt
 anymore. To reuse this scheduled task we need a broker script which acquires
 elevation, and implements the interface between both the least-privileged and
 the elevated process.

Why is the interface a VBScript?
 Yeah, I know it's old school. However, WScript.exe runs without a window. Thus,
 you won't ever see any flushing console window caused by the interface.

Why is the command line passed via an environment variable?
 At first glance this seems rather clumsy. Though if you think about how
 difficult it may get to preserve proper quoting across different shells you
 might get an idea of why the definition of an environment variable was much
 easier. Once it's defined, you don't have to worry about parsing constraints
 anymore.

Why does the VBScript create temporary files?
 Synchronization and inter-process communication are always a little tricky. The
 use of files is comparatively easy. A write lock can be used to avoid race
 conditions, and data can be transferred between different processes in a file.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Examples
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example 1a:
Batch code to open an elevated CMD prompt.

:::::
set "cmdLn=cmd /k" &%LOCALAPPDATA%\ElevateMe.vbs cmdLn 1 0
:::::

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example 1b:
PowerShell code to open an elevated PowerShell prompt.

#####
$Env:cmdLn="powershell"; start "$Env:LOCALAPPDATA\ElevateMe.vbs" "cmdLn",1,0 -Wait
#####

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example 2a:
The Batch code below has an additional first line which checks whether or not
 the process is elevated. If it runs with least privileges, it will assign its
 own command line to variable cmdLn and pass it to ElevateMe.vbs in order to run
 the Batch file again with highest available privileges.
Drag/drop one or more files onto the Batch file to demonstrate that arguments
 are forwarded to the elevated process, too.
You may reuse the first line in any batch code which requires elevation.

:::::
@>nul 2>&1 %__APPDIR__%net.exe session||(setlocal EnableDelayedExpansion&set "cmdLn=!CMDCMDLINE!"&%LOCALAPPDATA%\ElevateMe.vbs cmdLn 1 0&exit /b)
@echo off
title ElevateMe Test
echo current directory: "%cd%"
echo arguments (if any): %*
pause
:::::

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example 2b:
The PowerShell code below has additional two first lines which do the same
 things like the first Batch line in Example 2a.

#####
If (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){
  $Env:cmdLn=(gwmi Win32_Process -Filter ProcessId=$PID).CommandLine;start "$Env:LOCALAPPDATA\ElevateMe.vbs" "cmdLn",1,0 -Wait;Exit}
$Host.UI.RawUI.WindowTitle = "ElevateMe Test"
"current directory: `"$PWD`""
"arguments (if any):`n $($ARGS -Join `"`n `")"
pause
#####

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example 2c:
VBScript code with the same behavior of Examples 2a and 2b.

'''''
Option Explicit
Dim oWSh, isElevated
Set oWSh = CreateObject("WScript.Shell")
On Error Resume Next : oWSh.RegRead "HKEY_USERS\S-1-5-19\Environment\TEMP" : isElevated = (Err.Number = 0) : On Error GoTo 0
If Not isElevated Then
  oWSh.Environment("PROCESS")("cmdLn") = GetOwnCmdLine : oWSh.Run oWSh.Environment("PROCESS")("LOCALAPPDATA") & "\ElevateMe.vbs cmdLn 1 0", 1, True
Else
  Dim out, arg
  out = "current directory: """ & oWSh.CurrentDirectory & """" & vbLf & "arguments (if any):"
  For Each arg In WScript.Arguments
    out = out & vbLf & "  " & arg
  Next
  oWSh.PopUp out, 0, "Elevated", vbSystemModal Or &h00040000& Or &h00200000&
End If

Function GetOwnCmdLine()
  Dim oWmi, colProcs, oProc, pid, guid
  guid = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
  oWSh.Run """winver.exe"" tag" & guid, 0, False
  Set oWmi = GetObject("winmgmts:\\.\root\cimv2")
  Set colProcs = oWmi.ExecQuery("SELECT * FROM Win32_Process WHERE CommandLine LIKE '% tag" & guid & "'")
  For Each oProc In colProcs : pid = oProc.ParentProcessID : oProc.Terminate : Next 
  Set colProcs = oWmi.ExecQuery("SELECT * FROM Win32_Process WHERE ProcessID=" & pid)
  For Each oProc In colProcs : GetOwnCmdLine = oProc.CommandLine : Next
End Function
'''''

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example 3a:
This Batch code runs itself elevated again and waits for the termination of the
 elevated process. It demonstrates how to get the return value of the elevated
 process.

:::::
@echo off &>nul 2>&1 %__APPDIR__%net.exe session &&goto elevatedBranch

setlocal EnableDelayedExpansion
set "cmdLn=!CMDCMDLINE!"
%LOCALAPPDATA%\ElevateMe.vbs cmdLn 1 1
endlocal &set "ret=%errorlevel%
echo Return value of the elevated process: %ret%
pause
exit /b

:elevatedBranch
echo Elevated process returning 5 to demonstrate the exit code behavior.
pause
exit /b 5
:::::

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example 3b:
PowerShell code with the same behavior like 3a.

#####
If (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
  $Env:cmdLn = (gwmi Win32_Process -Filter ProcessId=$PID).CommandLine
  $proc = start "$Env:LOCALAPPDATA\ElevateMe.vbs" "cmdLn",1,1 -Wait -PassThru
  "Return value of the elevated process: $($proc.ExitCode)"
  pause
  Exit
}

# elevated branch
"Elevated process returning 5 to demonstrate the exit code behavior."
pause
Exit 5
#####

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example 3b:
VBScript code with the same behavior like 3a and 3b.

'''''
Option Explicit
Dim oWSh, isElevated
Set oWSh = CreateObject("WScript.Shell")
On Error Resume Next : oWSh.RegRead "HKEY_USERS\S-1-5-19\Environment\TEMP" : isElevated = (Err.Number = 0) : On Error GoTo 0
If Not isElevated Then
  Dim ret
  oWSh.Environment("PROCESS")("cmdLn") = GetOwnCmdLine : ret = oWSh.Run(oWSh.Environment("PROCESS")("LOCALAPPDATA") & "\ElevateMe.vbs cmdLn 1 1", 1, True)
  oWSh.PopUp CStr(ret), 0, "Return value of the elevated process:", vbSystemModal Or &h00040000& Or &h00200000&
Else
  oWSh.PopUp "Elevated process returning 5 to demonstrate the exit code behavior.", 0, "Elevated", vbSystemModal Or &h00040000& Or &h00200000&
  WScript.Quit 5
End If

Function GetOwnCmdLine()
  Dim oWmi, colProcs, oProc, pid, guid
  guid = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
  oWSh.Run """winver.exe"" tag" & guid, 0, False
  Set oWmi = GetObject("winmgmts:\\.\root\cimv2")
  Set colProcs = oWmi.ExecQuery("SELECT * FROM Win32_Process WHERE CommandLine LIKE '% tag" & guid & "'")
  For Each oProc In colProcs : pid = oProc.ParentProcessID : oProc.Terminate : Next 
  Set colProcs = oWmi.ExecQuery("SELECT * FROM Win32_Process WHERE ProcessID=" & pid)
  For Each oProc In colProcs : GetOwnCmdLine = oProc.CommandLine : Next
End Function
'''''

