- AutoHotkey must be installed with the "Run as UI Access" option introduced with 1.1.24.02 enabled
- sas.dll's SendSAS function requires the system be configured to let UIA programs simulate C+A+D. If this script is launched elevated, it will change the setting itself and then put it back to what it was previously when done.
- Works only with Windows 7 and above. On Windows Vista, this might work if you find your own copy of sas.dll. On XP, running as SYSTEM (or using the Task Scheduler) and doing this should work:
Spoiler
Code: Select all
SendSAS(processWaitTimeout := 5)
{
SysGet, isRdp, 4096
if (isRdp) {
;try ShellDispatch4 := ComObjCreate("{13709620-C279-11CE-A49E-444553540000}", "{efd84b2d-4bcf-4298-be25-eb542a59fbda}")
;if (ShellDispatch4) {
; DllCall(NumGet(NumGet(ShellDispatch4+0)+40*A_PtrSize), "Ptr", ShellDispatch4)
; ObjRelease(ShellDispatch4)
;}
ComObjCreate("Shell.Application").WindowsSecurity
return
}
isUiAccess := writtenSsasg := False
RegRead SoftwareSASGeneration, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System, SoftwareSASGeneration
if ((ssasgNotExisting := ErrorLevel) || !(SoftwareSASGeneration & 0x2)) {
if (!A_IsAdmin) {
return
} else {
RegWrite REG_DWORD, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System, SoftwareSASGeneration, 3 ; Enable services and Ease of Access apps (i.e., uiAccess applications) to simulate SAS
writtenSsasg := !ErrorLevel
}
}
if (DllCall("Advapi32\OpenProcessToken", "Ptr", DllCall("GetCurrentProcess", "Ptr"), "UInt", TOKEN_QUERY := 0x0008, "Ptr*", hToken)) {
DllCall("Advapi32\GetTokenInformation", "Ptr", hToken, "UInt", TokenUIAccess := 26, "UInt*", isUiAccess, "UInt", 4, "UInt*", dwLengthNeeded)
DllCall("CloseHandle", "Ptr", hToken)
}
if (isUiAccess) {
DllCall("sas\SendSAS", "Int", True)
} else {
VarSetCapacity(rawUiaCommand, (260 + 1) * (A_IsUnicode + 1))
if (!DllCall("Shlwapi\AssocQueryString", "UInt", ASSOCF_INIT_IGNOREUNKNOWN := 0x00000400, "UInt", ASSOCSTR_COMMAND := 1, "Str", ".ahk", "Str", "uiAccess", "Ptr", &rawUiaCommand, "UInt*", 260)) {
ahkUiaCommand := SubStr(ahkUiaCommand, 1 + (isQuoted := NumGet(rawUiaCommand,, "Char") == Asc("""")), InStr((ahkUiaCommand := StrGet(&rawUiaCommand, "UTF-16")), ".exe") + StrLen("exe") - isQuoted)
if (FileExist(ahkUiaCommand)) {
PIPE_ACCESS_OUTBOUND := 2, FILE_FLAG_OVERLAPPED := 0x40000000, PIPE_TYPE_BYTE := 0, INVALID_HANDLE_VALUE := -1
; MsgWaitForMultipleObjectsEx constants:
WAIT_OBJECT_0 := 0x00000000, WAIT_FAILED := INFINITE := 0xFFFFFFFF, WAIT_TIMEOUT := 258, MWMO_ALERTABLE := 0x0002, MWMO_INPUTAVAILABLE := 0x0004
QS_INPUT := (QS_MOUSE := (QS_MOUSEMOVE := 0x0002 | QS_MOUSEBUTTON := 0x0004)) | QS_KEY := 0x0001 | QS_RAWINPUT := 0x0400 ; QS_TOUCH and QS_POINTER are included on Windows 8+
QS_ALLINPUT := QS_INPUT | QS_POSTMESSAGE := 0x0008 | QS_TIMER := 0x0010 | QS_PAINT := 0x0020 | QS_HOTKEY := 0x0080 | QS_SENDMESSAGE := 0x0040
pipe_name := "AHK_" . A_ThisFunc . "_" . A_TickCount
szCmdline := """" . ahkUiaCommand . """" . " /ErrorStdOut \\.\pipe\" . pipe_name
; Based on Lexikos' code @ https://autohotkey.com/board/topic/23575-how-to-run-dynamic-script-through-a-pipe/ and YMP's @ http://forum.script-coding.com/viewtopic.php?id=12103
if ((pipe_ga := DllCall("CreateNamedPipe", "Str", "\\.\pipe\" . pipe_name, "UInt", PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, "UInt", PIPE_TYPE_BYTE, "UInt", 255, "UInt", 0, "UInt", 0, "UInt", 0, "Ptr", 0, "Ptr")) != INVALID_HANDLE_VALUE) {
uiAccessAhkPid := 0
if ((pipe := DllCall("CreateNamedPipe", "Str", "\\.\pipe\" . pipe_name, "UInt", PIPE_ACCESS_OUTBOUND, "UInt", PIPE_TYPE_BYTE, "UInt", 255, "UInt", 0, "UInt", 0, "UInt", 0, "Ptr", 0, "Ptr")) != INVALID_HANDLE_VALUE) {
if ((hEvent := DllCall("CreateEvent", "Ptr", 0, "Int", True, "Int", False, "Ptr", 0, "Ptr"))) {
VarSetCapacity(OVERLAPPED, 8 + 3 * A_PtrSize, 0)
,NumPut(hEvent, OVERLAPPED, 8 + 2 * A_PtrSize, "ptr")
Run *open %szCmdline%, %A_WinDir%\system32, UseErrorLevel Hide, uiAccessAhkPid
if (ErrorLevel != "ERROR") {
DllCall("ConnectNamedPipe", "Ptr", pipe_ga, "Ptr", &OVERLAPPED)
beginTick := A_TickCount
while (True) {
timeout := ((processWaitTimeout * 1000) + 250) - (A_TickCount - beginTick)
if (timeout <= 0)
break
r := DllCall("MsgWaitForMultipleObjectsEx", "UInt", 1, "Ptr*", hEvent, "UInt", timeout, "UInt", QS_ALLINPUT, "UInt", MWMO_ALERTABLE | MWMO_INPUTAVAILABLE, "UInt")
Sleep -1
if (r == WAIT_OBJECT_0) {
DllCall("ConnectNamedPipe", "Ptr", pipe, "Ptr", 0)
Script := (A_IsUnicode ? chr(0xfeff) : chr(239) chr(187) chr(191)) . "#NoEnv`r`n#NoTrayIcon`r`n#SingleInstance Off`r`nDllCall(""sas\SendSAS"", ""Int"", True)"
DllCall("WriteFile", "Ptr", pipe, "Str", Script, "UInt", (StrLen(Script)+1)*(A_IsUnicode + 1), "UInt*", 0, "Ptr", 0)
break
} else if (r == WAIT_FAILED || r == WAIT_TIMEOUT) {
break
}
}
}
DllCall("CloseHandle", "Ptr", hEvent)
}
DllCall("CloseHandle", "Ptr", pipe)
}
DllCall("CloseHandle", "Ptr", pipe_ga)
if (uiAccessAhkPid)
Process, WaitClose, %uiAccessAhkPid%, %processWaitTimeout%
}
}
}
}
if (writtenSsasg) {
if (ssasgNotExisting)
RegDelete HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System, SoftwareSASGeneration
else
RegWrite REG_DWORD, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System, SoftwareSASGeneration, %SoftwareSASGeneration%
}
}