【问题标题】:How to run an HTA dialog with a UAC prompt and wait for a return value如何使用 UAC 提示运行 HTA 对话框并等待返回值
【发布时间】:2023-02-13 23:58:18
【问题描述】:

是否可以以管理员权限运行部分 hta 文件?我想以一种方式实现,当我单击“添加时间”按钮时,它应该提示我提供管理员详细信息。我尝试了几种方法但没有用。我尝试了一些方法,但它给了我一个错误,其他人不会响应以管理员身份运行的调用。我将感谢你的帮助。

这是文件

<!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>

【问题讨论】:

  • 那是很多代码。您应该发布MRE。我认为最简单的方法是将添加时间提示放在单独的 HTA 中并使用 Runas 运行它并通过 HKLM 中的条目返回值。我将很快发布一个最小的例子。
  • 顺便说一句,GetScale 函数中有一个错误。可以在here 找到该函数的更新版本。
  • 我对此非常感激。这是我尝试了一段时间的方法,但效果很好。我也在问是否可以运行计时器,因为当我尝试更改值时它保持静态。提前致谢。
  • 请参阅下面的答案以获取完整的解决方案。这为您提供了一个完全可用的倒计时计时器,并带有 UAC 提示以增加更多时间,但我很好奇您打算如何防止用户使用任务管理器(或命令提示符下的 TaskKill)终止主脚本?
  • @user692942 我检查了重复项,但许多技术是特定于 VBScript 的(例如,使用 wscript 对象),而且我看到,没有一个技术会导致用户在 UAC 提示符下单击“否”。

标签: vbscript hta


【解决方案1】:

这是重写的脚本,用于为具有提升权限的“添加时间”对话框运行单独的 HTA。

我发布的原始版本使用标志文件来确定被调用脚本何时退出。然而,这种方法有一个致命的缺陷。如果用户在 UAC 提示符下单击“否”,被调用的脚本将永远不会运行,调用脚本将永远等待。

我通过使用elevate.exe 命令行工具运行 AddTime.hta 解决了这个问题。 Elevate.exe 提供了一个-wait4exit 选项,所以我们可以使用标准的Run 方法和True 设置等待返回,elevate.exe 会处理剩下的事情。

数据(要添加的分钟数)和时间戳通过 HKLM 传回,因此我们可以安全地获取添加的时间值,但如果在 UAC 提示符下单击“否”则不会再次添加。

下面贴出两个主要的脚本。

完整的包发布在here

剩余时间.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:
ootcimv2:Win32_UserAccount.Domain='" & oNet.UserDomain & "',Name='" & oNet.UserName & "'").SID

TimeLeftInSeconds = DefaultWait * 60
On Error Resume Next
RegTimeLeft = oWSH.RegRead("HKCUSoftwareTimeRemainingHTATimeLeftInSeconds")
TimeStamp = oWSH.RegRead("HKLMSoftwareTimeRemainingHTATimeStamp" & 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 "HKCUSoftwareTimeRemainingHTATimeLeftInSeconds",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("HKCUControl PanelDesktopWindowMetricsAppliedDPI") / 96
  On Error Goto 0
End Function

Sub AddTime
  Paused = True
  oWSH.RegWrite "HKCUSoftwareTimeRemainingHTATimeLeftInSeconds",0
  oWSH.Run ElevateExe & " -wait4exit " & ExePath & "mshta.exe """ & AddTimeHTA & """ " & window.screenLeft & " " & window.screenTop & " " & Scale & " " & MySID,0,True
  On Error Resume Next
  MinutesToAdd = oWSH.RegRead("HKLMSoftwareTimeRemainingHTAAddTime" & MySID)
  TimeStamp = oWSH.RegRead("HKLMSoftwareTimeRemainingHTATimeStamp" & 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>

添加时间.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 "HKLMSoftwareTimeRemainingHTATimeStamp" & MySID,Now()
  oWSH.RegWrite "HKLMSoftwareTimeRemainingHTAAddTime" & 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>

【讨论】: