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


onsdag 13 september 2017

Your updater sucks!

You've released a new version of your awesome program, and you wan't everyone to upgrade! Unfortunately you are a Windows developer so you have to rely on your own wit and skill for automatic upgrades. Fortunately you have billions of investor dollars to spend, because you work at Spotify or whatever.

So you have made a mechanism on the client that checks for updates now and then and downloads them automatically. The massive amount of money you spent didn't make you God though:



You need to ASK before you FUCK. It's called consent.exe.



You didn't consider that your program might be located somewhere on the computer where the user doesn't have permission to write. You didn't consider that because billions of dollars still can't make you think.

There are uncountable number of solutions to this problem, some of which are:


  • Have settings to disable updates and let the admins (me) manage it.
  • Check for write permissions and only upgrade if you know you're able to.
  • Write a service or scheduled task, like Google does. A service means a service, not just a stupid user process. Lika Java does.
  • DON'T update automatically! This is the best option.




Humorously you might totally break your program if you can't even start it without upgrading once it downloaded a new version, unless you edit the registry or some stupid xml-file.

Breaking your own program, is that what you want? Is it?




Your silent install is not silent!

So, you've made your awesome program, built an installation that flawlessy installs said program and shipped the product. Great!

Except it is NOT great, because your stupid install cannot be installed silently!



A stupid dialog you might or might not get when trying to install
Siemens Sinamics Micromaster Starter "silent".


Read this sentence very carefully: Your install should be able to install silently, and this silent operation should be silent. Not NOT silent. Silent.

If your install is not silent I have to make it silent, and since I don't know and don't care about your program, this is not my job.

Well, actually it IS my job, but it should be yours.


Your Install Sucks

So, you have developed this awesome program, and you want to spread it to the world! You need to create a package to install the program. What should this installation do? I'll tell you!

Things your installation should do:


Install the program.


That's it! That is all! You know what your install should NOT do? Anything else! This includes but is not limited to:

Starting the program. When your install is finished, DO NOT start or allow the option to start the program you just installed! The user launches the program when he wants, and he does it in his own context, NOT the context in which the program was installed!

Actually, don't start ANY process after the install. Deploying a computer with, for instance, Microsoft System Center means installing dozens or hundreds of programs in a row. If your app needs running processes, launch them at startup or logon.

Don't suddenly restart the computer! This should go without saying, but apparently NOOOO. Respect REBOOT=ReallySuppress or other flags. If your app is not quite installed after your installer but needs a restart, exit with 3010.

About that, exit with proper exit codes! 0 (zero) means success. 255 does NOT mean success! Where did you get that idea? I'm talking to you, Adobe! Again: 0 means success! DON'T return zero if the installation failed! If you do, I'll hunt you down!

Don't install other programs! Definitely not malware, but ALSO not prerequirements for your app! If your program requires vc_redist 2027 or dotnet 19, put them in a prereq-folder. If you MUST, have a setup-wrapper that installs them. They are ALREADY outdated when you ship your product and MY environment already have them. Newer and better versions, actually. DON'T force them on me. Merge-modules are ok, of course.

If you have a wrapper, DON'T require it. "This install must be started from setup.exe" is not a valid argument! I don't want to figure out your stupid parameters to run the setup standalone.

Don't require internet during installation. Don't require a network at all. If you are stupid enough to have some stupid license activation requirement, manage that with a manager, NOT the installer. Do you know how ANNOYING it is to have a computer fail during sequencing because some paper pusher didn't add more licenses to some stupid pool of licenses?

Have a working unattended option. A way to silently install your program. And be sure it is SILENT. How hard can it be to NOT display a dialog box? Jeez...

To be continued...