How to run an HTA dialog with a UAC prompt and wait for a return value

261 Views Asked by At

Is it possible to run part of hta file with administrator's privilege? I will like to make it in a way that when i click on ADD TIME button it should prompt me to provide administrators details. I have tried several ways but not working. There are some of the ways i have tried but it gives me an error and others will not respond to the call to run as administrator. I will be grateful for your assistance.

Here is the file

<!DOCTYPE html>
<html>
<title >Time remaining</title>
<head>

<meta charset="UTF-8" http-equiv="X-UA-Compatible" content="IE=9">
<hta:application
  applicationname="Time remaining" 
  id=oHTA
  maximizebutton=no
  windowstate="normal"
  scroll=no
  SysMenu=no
>
<script language="VBScript">
    Const DefaultWait = 30 'minutes
    Const LogoffCmd = "Shutdown.exe /l /f"
    Const RestartCmd = "Shutdown.exe -r -f"
    Const ShutdownCmd = "shutdown.exe /s /t"
    Const Logoff = True
    Const Unattended = True
    Const TestMode = False
    Const HKCU = &H80000001
    Set oWSH = CreateObject("Wscript.Shell")
    Set oFSO = CreateObject("Scripting.FileSystemObject")
    Set oReg = GetObject("winmgmts:\\.\root\default:StdRegProv")
    Dim TimeLeftInSeconds,WaitTimer,Wait,PrevWait
    MyPath  = Mid(document.URL,8)
    MyName = oFSO.GetFileName(MyPath)
    MyFolder = oFSO.GetParentFolderName(MyPath)
    oWSH.CurrentDirectory = MyFolder
    document.Title = MyName
    Scale = GetScale()
    w = 300 * Scale: h = 250 * Scale
    Window.ResizeTo w, h
    Window.MoveTo (screen.availWidth - w)/2, (screen.availHeight - h)/2
    
    Sub RunAsAdmin
        If InStr(LCase(oHTA.commandLine), " /elevated") = 0 Then
            createobject("Shell.Application").ShellExecute "mshta.exe", oHTA.commandLine & " /elevated", "", "runas", 4
            self.close
        End If
    End Sub
    
    Sub window_onLoad
      ShutdownOption(0).style.zoom = Scale
      ShutdownOption(1).style.zoom = Scale
      ShutdownOption(2).style.zoom = Scale
      Wait = DefaultWait
      WaitBox.Value = Wait
      TimeLeftInSeconds = Wait * 60
      WaitBox.select
      If Unattended Then
        UpdateCountdown
        WaitTimer = Window.SetInterval("UpdateCountdown()", 1000)
      End If
      ShutdownOption(0).checked = True
      If Restart Then ShutdownOption(1).checked = True
      If Shutdown Then ShutdownOption(2).checked = True
    End Sub
    Sub document_onKeyDown
      If window.event.keyCode=13 Then RestartCountdown
    End Sub
    Sub ReSelectInput
      WaitBox.select
    End Sub
    Sub UpdateCountdown
      Hours = CInt(TimeLeftInSeconds \ 3600)
      Minutes = CInt((TimeLeftInSeconds Mod 3600) \ 60)
      Seconds = TimeLeftInSeconds Mod 60
      CountDown.innerHTML = Hours & ":" & Right("0" & Minutes,2) & ":" & Right("0" & Seconds,2)
      If TimeLeftInSeconds<=0 Then
        Cmd = LogoffCmd
        If ShutdownOption(1).checked Then Cmd = RestartCmd
        If ShutdownOption(2).checked Then Cmd = ShutdownCmd
        If TestMode Then
          MsgBox Cmd
        Else
          oWSH.Run Cmd,1,False
        End If
        self.Close
        Exit Sub
      End If
      TimeLeftInSeconds = TimeLeftInSeconds - 1
    End Sub
    Sub RestartCountdown
      If WaitTimer="" Then WaitTimer = Window.SetInterval("UpdateCountdown()", 1000)
      WaitBox.select
      If Not IsNumeric(Replace(WaitBox.Value,":",".")) Then
        WaitBox.Value = PrevWait
        WaitBox.select
        Exit Sub
      End If
      PrevWait = WaitBox.Value
      Wait = WaitBox.Value
      If InStr(Wait,":")>0 Then
        aWait = Split(Wait,":")
        Wait = aWait(0)*60 + aWait(1)
      End If
      TimeLeftInSeconds = Wait * 60
      UpdateCountdown
    End Sub
    
    Function GetScale()
      GetScale = 1.0
      Value = oWSH.RegRead("HKCU\Control Panel\Desktop\WindowMetrics\AppliedDPI")
      If Value > 96 Then
        'Custom scaling is set
        GetScale = Value/96
      Else
        'See if standard scaling is set
        Key = "Control Panel\Desktop\PerMonitorSettings"
        Result = oReg.EnumKey(HKCU, Key, ArrKeys)
        If Result=0 Then
          'Assume first monitor in list is the one we want
          For Each SubKey In ArrKeys
            Exit For
          Next
          Value = oWSH.RegRead("HKCU\" & Key & "\" & SubKey & "\DPIValue")
          If Value>0 Then GetScale = 1 + (Value * 0.25)
        End If
      End If
    End Function
    
    


</script>
<style>
  .body {background-color:Lavender; font-family:Segoe UI; font-size:11pt, justify-content: center;}
  h1 {color:red; text-align: center;}
  .button {width:6em}
  .radio {vertical-align:bottom}
  /* The Modal (background) */
.modal {
  display: none; /* Hidden by default */
  position: fixed; /* Stay in place */
  z-index: 1; /* Sit on top */
  padding-top: 0px; /* Location of the box */
  left: 0;
  top: 0;
  width: 90%; /* Full width */
  height: 60%; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(0,0,0); /* Fallback color */
  background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}

/* Modal Content */
.modal-content {
  background-color: #fefefe;
  margin: auto;
  padding: 20px;
  border: 1px solid #888;
  width: 80%;
  text-align: center;
}

/* The Close Button */
.close {
  color: #aaaaaa;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}

</style>
</head>
<body>
 <div class="timer"> <h1 id=CountDown>&nbsp </h1></div>

<button id="myBtn" value="Add Time" onClick=RunAsAdmin()> Add Time</button>

<div id="myModal" class="modal">
     

  <div class="modal-content">
    <span class="close">&times;</span>
 Enter minutes to be added<br><br>
  
  <input type=text size=8 id=WaitBox>
  <input type=button class=button id=OKButton value="OK" onClick=RestartCountdown()><br><br>
  <input type=radio class=radio name=ShutdownOption onClick=ReSelectInput()>Logoff&nbsp
  <input type=radio class=radio name=ShutdownOption onClick=ReSelectInput()>Restart&nbsp
  <br><input type=radio class=radio name=ShutdownOption onClick=ReSelectInput()>Shutdown
  </div>
</div>
  
 <script>

    var modal = document.getElementById("myModal");

    var btn = document.getElementById("myBtn");

    var span = document.getElementsByClassName("close")[0];

    btn.onclick = function() {
      modal.style.display = "block";
    }

    span.onclick = function() {
      modal.style.display = "none";
    }

    window.onclick = function(event) {
      if (event.target == modal) {
        modal.style.display = "none";
      }
    }
    
</script>
</body>
</html>
1

There are 1 best solutions below

0
LesFerch On BEST ANSWER

Here's your script rewritten to run a separate HTA for the "Add Time" dialog with elevated privileges.

The original version I posted used a flag file to determine when the called script exited. However that method has a fatal flaw. If the user clicks "No" at the UAC prompt, the called script will never run and the calling script will wait forever.

I solved this by using the elevate.exe command line tool to run AddTime.hta. Elevate.exe provides a -wait4exit option so we can just use the standard Run method with True set for the wait on return and elevate.exe takes care of the rest.

The data (minutes to add) and a timestamp are passed back via HKLM so we securely get the added time value but don't add it again if "No" is clicked at the UAC prompt.

The two main scripts are posted below.

The complete package is posted here.

TimeRemaining.hta

<!DOCTYPE html>
<html>
<title>Time remaining</title>
<head>
<meta charset="UTF-8" http-equiv="X-UA-Compatible" content="IE=9">
<hta:application
  id=oHTA
  icon=perfmon.exe
  applicationname=TimeRemaining
  scroll=no
  contextmenu=no
  showintaskbar=yes
  minimizeButton=yes
  maximizeButton=no
  singleinstance=yes
  SysMenu=no
>
<script language="VBScript">
Const DefaultWait = 30 'minutes
Const AddTimeStart = 120 'seconds
Const WarningTime = 120 'seconds

Set oWSH = CreateObject("Wscript.Shell")
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oNet = CreateObject("WScript.Network")

Dim TimeLeftInSeconds,WaitTimer
MyFolder = oFSO.GetParentFolderName(Mid(document.URL,8))
oWSH.CurrentDirectory = MyFolder
AddTimeHTA = MyFolder & "\AddTime.hta"
logoutcmd = "Shutdown.exe -l -f"
Scale = GetScale()
w = 300 * Scale: h = 110 * Scale
Window.ResizeTo w, h
Window.MoveTo (screen.availWidth - w)/2, (screen.availHeight - h)/2
Paused = False

If InStr(MyFolder," ")>0 Then
  MsgBox "This script must be run from a folder with no spaces in its name",vbExclamation,"Error"
  self.close
End If

Architecture = oWSH.environment("PROCESS").item("PROCESSOR_ARCHITECTURE")
SystemRoot = oWSH.ExpandEnvironmentStrings("%SystemRoot%")
WOWPath = SystemRoot & "\SysWOW64\"
ExePath = SystemRoot & "\System32\"
ElevateExe = "elevate.exe"
If Architecture="AMD64" Then ElevateExe = "elevate64.exe"
If Architecture="x86" And oFSO.FolderExists(WOWPath) Then ExePath = WOWPath

MySID = GetObject("winmgmts:\root\cimv2:Win32_UserAccount.Domain='" & oNet.UserDomain & "',Name='" & oNet.UserName & "'").SID

TimeLeftInSeconds = DefaultWait * 60
On Error Resume Next
RegTimeLeft = oWSH.RegRead("HKCU\Software\TimeRemainingHTA\TimeLeftInSeconds")
TimeStamp = oWSH.RegRead("HKLM\Software\TimeRemainingHTA\TimeStamp\" & MySID)
On Error Goto 0
If IsNumeric(RegTimeLeft) And IsDate(TimeStamp) Then
  TimeSinceLastEntry = DateDiff("s",TimeStamp,Now())
  If TimeSinceLastEntry<2 Then TimeLeftInSeconds = RegTimeLeft
End If

Sub window_onLoad
  UpdateCountdown
  WaitTimer = Window.SetInterval("UpdateCountdown()", 1000)
End Sub

Function document_onKeyDown()
  If window.event.keyCode=115 Or window.event.keyCode=116 Then
    window.event.keyCode = 0
    document_onKeyDown = False
  End If
End Function

Sub UpdateCountdown
  If Not Paused Then
    If TimeLeftInSeconds=AddTimeStart Then
      Window.ResizeTo w, 150 * Scale
      AddTimeButton.Style.Display = "inline"
    End If
    Hours = CInt(TimeLeftInSeconds \ 3600)
    Minutes = CInt((TimeLeftInSeconds Mod 3600) \ 60)
    Seconds = TimeLeftInSeconds Mod 60
    CountDown.innerHTML = Hours & ":" & Right("0" & Minutes,2) & ":" & Right("0" & Seconds,2)
    If TimeLeftInSeconds<0 Then
      oWSH.Run logoutcmd,1,False
      self.Close
      Exit Sub
    End If
    TimeLeftInSeconds = TimeLeftInSeconds - 1
    oWSH.RegWrite "HKCU\Software\TimeRemainingHTA\TimeLeftInSeconds",TimeLeftInSeconds
    If TimeLeftInSeconds=WarningTime Then oWSH.Run "Powershell.exe -NoLogo -ExecutionPolicy Bypass -File .\Warning.ps1",0,False
  End If
End Sub

Function GetScale()
  GetScale = 1.0
  On Error Resume Next
  'If user changes scale, they must logout/login for this registry value to change
  GetScale = oWSH.RegRead("HKCU\Control Panel\Desktop\WindowMetrics\AppliedDPI") / 96
  On Error Goto 0
End Function

Sub AddTime
  Paused = True
  oWSH.RegWrite "HKCU\Software\TimeRemainingHTA\TimeLeftInSeconds",0
  oWSH.Run ElevateExe & " -wait4exit " & ExePath & "mshta.exe """ & AddTimeHTA & """ " & window.screenLeft & " " & window.screenTop & " " & Scale & " " & MySID,0,True
  On Error Resume Next
  MinutesToAdd = oWSH.RegRead("HKLM\Software\TimeRemainingHTA\AddTime\" & MySID)
  TimeStamp = oWSH.RegRead("HKLM\Software\TimeRemainingHTA\TimeStamp\" & MySID)
  TimeSinceLastEntry = DateDiff("s",TimeStamp,Now())
  On Error Goto 0
  If IsNumeric(MinutesToAdd) And TimeSinceLastEntry<2 Then 
    TimeLeftInSeconds = TimeLeftInSeconds + Int(MinutesToAdd * 60)
  End If
  Paused = False
End Sub

</script>
<style>
body {background-color:black; font-family:Segoe UI; font-size:11pt}
h1 {color:red}
.timerDiv {text-align:center}
#AddTimeButton {width:6em; display:none}
</style>
</head>
<body>
  <div class=timerDiv>
    <h1 id=CountDown></h1>
    <input id=AddTimeButton type=button value='Add Time' OnClick=AddTime()>
  </div>
</body>
</html>

AddTime.hta

<!DOCTYPE html>
<html>
<head>
<title >Add Time</title>
<meta charset="UTF-8" http-equiv="X-UA-Compatible" content="IE=9">
<hta:application
  id=oHTA
  icon=perfmon.exe
  scroll=no
  contextmenu=no
  showintaskbar=no
  minimizeButton=no
  maximizeButton=no
  SysMenu=yes
>
<script language="VBScript">
Set oWSH = CreateObject("Wscript.Shell")

aCmd = Split(oHTA.commandLine)
If UBound(aCmd)<3 Then self.Close

x = aCmd(1)
y = aCmd(2)
Scale = aCmd(3)
MySID = aCmd(4)
w = 300 * Scale: h = 150 * Scale
Window.MoveTo x,y
Window.ResizeTo w,h

Sub document_onKeyDown
  If window.event.keyCode=13 Then window.event.keyCode = 0: Done
  If window.event.keyCode=27 Then window.event.keyCode = 0: self.Close
End Sub

Sub window_onLoad
  AddTime.focus
End Sub

Sub Done
  oWSH.RegWrite "HKLM\Software\TimeRemainingHTA\TimeStamp\" & MySID,Now()
  oWSH.RegWrite "HKLM\Software\TimeRemainingHTA\AddTime\" & MySID,AddTime.Value
  self.Close
End Sub

</script>
<style>
body {color:red; background-color:black; font-family:Segoe UI; font-size:11pt}
.timer {text-align:center}
</style>
</head>
<body>
  <div class=timer>
    Enter minutes to be added:<br><br>
    <input id=AddTime type=text size=4 maxlength=4>
    <input type=button Value=OK OnClick=Done()>
  </div>
</body>
</html>