fredag 29 september 2017

PendingFilerenameOperations

Some installs require a reboot when finished, either by asking nicely or by forcefully restarting the computer like an asshole. Most, but not all, msi-installations respect the REBOOT=ReallySuppress flag.

Some installs however, require a reboot before the install begins.

I like to call these installs arrogant or pretentious. Also cocksure. I like that word.

These installs are so convinced about their own superiority that they refuse to install if a restart is pending for any reason.

Heck, I can upgrade my network drivers and my graphics drivers on the fly without restarting the computer, but YOUR program is so advanced and important that it cannot fathom to just copy the damn binaries unless the computer is perfectly clean.

The way to work around this is the registry entry PendingFilerenameOperations. Tucked away far down the path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager this MULTI_SZ key containst files that supposedly could not be copied to their destination or deleted for some reason, locked files usually. They are now in TEMP limbo and needs to be renamed during the next boot.

If this key exists, at all, some installs refuse to run. It doesn't matter if the key contains nothing but crap files, installation becomes impossible. Unless you restart, of course, but that is the lame way.

Helpful pages on the web has the solution: Delete the damn key.

Well, I agree. But what if there is something important in there? Let's read it's content, remove the key, and then write everything back! This is somewhat easily done by using reg.exe in a script, if it's a simple before and after action.

I wan't to use AutoIt though, just because. First, my installer wrapper is written in AutoIt for obvious reasons. Laziness, if that wasn't obvious. Also, I wan't to clear it continously during long and complicated chains of dependent installs, many of which require restart inbetween.

It complicates things, because this key does not abide to standard MULTI_SZ rules, which does not allow empty rows, for instance.

The number of people caring about this, understanding it, having use for it and finally finding this page is propably fewer than the number of my Facebook friends (sobbing). Not even my mom reads this blog.

But here you go:


Global $PendingFilerenameOperations

Func GetPendingFilerenameOperations()

local $tmp=_RegReadPendingFilerenameOperations()

RegDelete("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager","PendingFilerenameOperations")

if $tmp<>"" Then

$tmp=StringTrimLeft($tmp,2); Remove "0x" from string, so that we can add binary strings together.

if StringRight($tmp,8)="00000000" Then
$tmp=StringTrimRight($tmp,4) ; Remove last LF if there are two.
EndIf

StringReplace($tmp,"000000","")
local $linefeeds = @extended ; Find out the number of linefeeds in the binary string. All characters ends in 00, plus 00 00 for the linefeed.

if Mod($linefeeds,2) <> 0 Then
$tmp=$tmp & "0000" ; Add back the linefeed if there was an odd number of linefeeds.
EndIf
$PendingFilerenameOperations = $PendingFilerenameOperations & $tmp
EndIf

EndFunc


Func SetPendingFilerenameOperations()

GetPendingFilerenameOperations()

if $PendingFilerenameOperations<>"" Then

;~ Count trailing Zeroes. There should always be exactly 10 when everything is said and done. 00 for the last character, 00 00 for the linefeed after the last row, and then a final 00 00 linefeed.

local $trailingZeroes=0

for $i = StringLen($PendingFilerenameOperations)-3 to 1 step -4
if StringMid($PendingFilerenameOperations,$i,4)="0000" then
$trailingZeroes = $trailingZeroes+1
Else
ExitLoop
EndIf
Next

;~ AutoIt RegWrite (or maybe BinaryToString) will add the trailing zeroes for us, contrary to documentation (or I'm lost somehow), so lets remove them:

$PendingFilerenameOperations = StringTrimRight($PendingFilerenameOperations, $trailingZeroes*4)

; Convert to string, and write it all back:

$PendingFilerenameOperations = StringReplace(BinaryToString("0x" & $PendingFilerenameOperations, 2), ChrW(0), @LF) ; According to AutoIt doc it should be @CRLF here, but that gives incorrect result.

RegWrite("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager","PendingFilerenameOperations","REG_MULTI_SZ",$PendingFilerenameOperations)

EndIf

EndFunc


Func _RegReadPendingFilerenameOperations()

Local $hKey = 0x80000002 ; HKEY_LOCAL_MACHINE
local $lpSubKey = "SYSTEM\CurrentControlSet\Control\Session Manager"
local $lpValueName = "PendingFilerenameOperations"

;~ Open key:

$ret = DllCall("advapi32.dll", "long", "RegOpenKeyExW", "ulong_ptr", $hKey, "wstr", $lpSubKey, "dword", 0, "ulong", 0x20019, "ulong_ptr*", 0)
If @error Or ($ret[0] <> 0) Then
return ""
EndIf
local $hKey2 = $ret[5]

;~ Check type and size of PendingFilerenameOperations:

Local $ret2 = DllCall("advapi32.dll", "long", "RegQueryValueExW", "ulong_ptr", $hKey2, "wstr", $lpValueName, "ptr", 0, "dword*", 0, "ptr", 0, "dword*", 0)
If @error Or ($ret2[0] <> 0) Then
DllCall("advapi32.dll", "long", "RegCloseKey", "ulong_ptr", $hKey2)
return ""
EndIf
if $ret2[4]<>7 Then
;~ Not a REG_MULTI_SZ... Something strange is going on...

return ""
EndIf

;~ Read the damn data:

Local $keyLen = $ret2[6]

Local $keyData = DllStructCreate("byte[" & $keyLen & "]")
$ret3 = DllCall("advapi32.dll", "long", "RegQueryValueExW", "ulong_ptr", $hKey2, "wstr", $lpValueName, "ptr", 0, "dword*", 0, "ptr", DllStructGetPtr($keyData), "dword*", DllStructGetSize($keyData))
DllCall("advapi32.dll", "long", "RegCloseKey", "ulong_ptr", $hKey2)
If (Not IsArray($ret3)) Or ($ret3[0] <> 0) Then
return ""
EndIf

Return DllStructGetData($keyData, 1)

EndFunc


Inga kommentarer:

Skicka en kommentar