This script is intended to be invoked via task scheduler, passing the name of a UEFI boot entry as a parameter. The name is used to look up the index of the boot entry, which is then stored in the BootNext UEFI variable. If successful, the script calls Shutdown 2 to reboot the system. Upon next boot, the firmware reads (and resets) this variable and uses its value to select a boot entry one time. In other words, it boots from a specific boot option (typically an OS or device) that might not be the usual default.
Requirements:
- The system must support UEFI.
- The OS you want to boot must be registered as a boot option in UEFI, with a known/unique descriptive string.
- Run as administrator and pass the boot entry description as a command line parameter.
Code: Select all
#Requires AutoHotkey v2.0-beta.4
Fail() => ExitApp(A_LastError)
if A_Args.Length < 1
ExitApp 87
target := A_Args[1]
if !DllCall("OpenProcessToken", "ptr", DllCall("GetCurrentProcess", "ptr")
, "uint", 0x20 ; TOKEN_ADJUST_PRIVILEGES
, "ptr*", &hToken:=0)
Fail
privs := Buffer(16, 0)
if !DllCall("advapi32\LookupPrivilegeValue", "ptr", 0
, "str", "SeSystemEnvironmentPrivilege"
, "ptr", privs.ptr + 4) ; &privs.Privileges[0].Luid
Fail
NumPut('uint', 1, privs)
NumPut('uint', 2, privs, 12) ; SE_PRIVILEGE_ENABLED
DllCall("advapi32\AdjustTokenPrivileges", "ptr", hToken, "int", false
, "ptr", privs, "uint", privs.size, "ptr", 0, "ptr", 0)
if A_LastError
Fail
DllCall("CloseHandle", "ptr", hToken)
buf := Buffer(512) ; arbitrary size
while DllCall("GetFirmwareEnvironmentVariable"
, "str", Format("Boot{:04x}", A_Index-1)
, "str", "{8be4df61-93ca-11d2-aa0d-00e098032b8c}" ; EFI_GLOBAL_VARIABLE
, "ptr", buf
, "uint", buf.size) {
if StrGet(buf.ptr + 6) = target {
if !DllCall("SetFirmwareEnvironmentVariable"
, "str", "BootNext"
, "str", "{8be4df61-93ca-11d2-aa0d-00e098032b8c}"
, "ushort*", A_Index-1
, "uint", 2)
Fail
Shutdown 2
ExitApp 0
}
}
ExitApp 2
Code: Select all
"C:\Program Files\AutoHotkey\v2\AutoHotkey64.exe" "X:\Example\RebootTo.ahk" "Windows 10"
- Windows tends to create boot entries named "Windows Boot Manager"; this version of the script currently can't differentiate duplicate entries and will just select the first one it sees. In my case, the firmware supports manually adding and renaming boot entries, so I have entries named "Windows 10" and "Windows 11".
- Any boot option present in the firmware should work; I have tested rebooting into Linux Mint.
- Errors are reported by exit code, since it is intended to be executed via task scheduler.
- Shutdown 2 can be removed if you just want to make a selection for the next boot, without rebooting right now.
Code: Select all
#Requires AutoHotkey v2.0-beta.4
if !A_IsAdmin {
if MsgBox("This script must be run as admin. Run as admin now?",, "y/n") = "no"
ExitApp
Run '*RunAs "' A_AhkPath '" "' A_ScriptFullPath '"'
ExitApp
}
if !DllCall("OpenProcessToken", "ptr", DllCall("GetCurrentProcess", "ptr")
, "uint", 0x28 ; TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES
, "ptr*", &hToken:=0)
throw OSError()
privs := Buffer(16, 0)
NumPut('uint', 1, privs)
if !DllCall("advapi32\LookupPrivilegeValue", "ptr", 0, "str", "SeSystemEnvironmentPrivilege"
, "ptr", privs.ptr + 4) ; &privs.Privileges[0].Luid
throw OSError()
NumPut('uint', SE_PRIVILEGE_ENABLED := 2, privs, 12)
if !DllCall("advapi32\AdjustTokenPrivileges", "ptr", hToken, "int", false, "ptr", privs, "uint", privs.size, "ptr", 0, "ptr", 0)
|| A_LastError
throw OSError()
DllCall("CloseHandle", "ptr", hToken)
order := Buffer(64) ; arbitrary - 64 allows 32 boot entries
n := DllCall("GetFirmwareEnvironmentVariable"
, "str", "BootOrder"
, "str", "{8be4df61-93ca-11d2-aa0d-00e098032b8c}"
, "ptr", order
, "uint", order.size)
boots := ""
buf := Buffer(512) ; arbitrary size
Loop n//2 {
if DllCall("GetFirmwareEnvironmentVariable"
, "str", Format("Boot{:04x}", NumGet(order, (A_Index-1)*2, "ushort"))
, "str", "{8be4df61-93ca-11d2-aa0d-00e098032b8c}"
, "ptr", buf
, "uint", buf.size)
boots .= StrGet(buf.ptr + 6) "`n"
}
MsgBox boots
Code: Select all
; (prerequisite: run as admin and enable SeSystemEnvironmentPrivilege)
DllCall("GetFirmwareEnvironmentVariable"
, "str", "BootCurrent"
, "str", "{8be4df61-93ca-11d2-aa0d-00e098032b8c}"
, "ushort*", ¤t:=0
, "uint", 2)