Archive: How to detect Windows Installer version?


How to detect Windows Installer version?
Hi guys,

is there some easy way to detect which version
of M$ Windows Installer is present on user's system?
What I want to achieve is this:
I have created my NSIS installer which detects the version
of .NET Framework (via DotNET.nsh) and runs dotnetfx.exe externally if needed.
The problem is, however, that this package requires another M$ prerequisite to be there => WindowsInstaller-KB893803-v2-x86.exe
How can one find out whether this MSI 3.1 is already installed on user's system?


Do a GetDLLVersion (as described in the manual or here: http://nsis.sourceforge.net/GetDllVe...mand_Explained) on "$SYSDIR\msi.dll"
You probably don't need to check release & build parts though.


You can check the version of $SYSDIR\msi.dll using GetDLLVersion. It's hardly the official method, but I couldn't find anything about it in MSDN.


Originally posted by kichik
You can check the version of $SYSDIR\msi.dll using GetDLLVersion. It's hardly the official method, but I couldn't find anything about it in MSDN.
It's probably the closest thing to an official method that you are going to get. It's what the Windows Installer team says you should do.
http://blogs.msdn.com/windows_instal...09/458528.aspx

Many thanks to all of you for quick replies.
I'll try what you are suggesting.:)


i thank to all too.. :D


What happens if there is no Windows Installer installed at all? Will the script crash?


No, you'll just get no version from GetDLLVersion.


go to start=>run and type msiexec


I know this thread is old but here's a working example:

!macro CHECK_MSI_VERSION

GetDllVersion "$SYSDIR\msi.dll" $R0 $R1
IntOp $R2 $R0 >> 16
IntOp $R2 $R2 & 0x0000FFFF ; $R2 now contains major version
IntOp $R3 $R0 & 0x0000FFFF ; $R3 now contains minor version
IntOp $R4 $R1 >> 16
IntOp $R4 $R4 & 0x0000FFFF ; $R4 now contains release
IntOp $R5 $R1 & 0x0000FFFF ; $R5 now contains build
StrCpy $0 "$R2.$R3.$R4.$R5" ; $0 now contains string like "1.2.0.192"

MessageBox MB_ICONINFORMATION|MB_OK "The version is: $R2.$R3.$R4.$R5"

!macroend


How do you detect if you have an older version installed?
For example, .net framework 2.0 requires at least version 3.1 (the above installed is 3.1.4000.2435).

However I have 4.5.6001.22299 installed.

Doesn't seem to work.


GetDllVersion "$SYSDIR\msi.dll" $R0 $R1
IntOp $R2 $R0 >> 16
IntOp $R2 $R2 & 0x0000FFFF ; $R2 now contains major version
IntOp $R3 $R0 & 0x0000FFFF ; $R3 now contains minor version
IntOp $R4 $R1 >> 16
IntOp $R4 $R4 & 0x0000FFFF ; $R4 now contains release
IntOp $R5 $R1 & 0x0000FFFF ; $R5 now contains build
StrCpy $0 "$R2.$R3.$R4.$R5" ; $0 now contains string like "1.2.0.192"

MessageBox MB_ICONINFORMATION|MB_OK "The version is: $R2.$R3.$R4.$R5"
${If} $0 < "3.1.4000.2435"
MessageBox MB_ICONINFORMATION|MB_OK "Install 3.1"
${Else}
MessageBox MB_ICONINFORMATION|MB_OK "Go away!"
${EndIf}


How can I use the individual numbers since I need it to have at least 3.1 to go on, so anything less has to have it install.

Originally posted by jweinraub
For example, .net framework 2.0 requires at least version 3.1 (the above installed is 3.1.4000.2435).

However I have 4.5.6001.22299 installed.

Doesn't seem to work.

${If} $0 < "3.1.4000.2435"
Use ${VersionCompare} out of WordFunc.nsh (ships with NSIS) for comparing version numbers. Right now you're using a string comparison which can fail on version numbers.
( You can also replace the first part of your code with ${GetFileVersion} out of FileFunc.nsh (ships with NSIS) )

That said... even with the above deficiency, this particular case should work just fine and pop up the "Go away!" messagebox. try sticking a 'MessageBox MB_OK "[$0]"' just in front of the ${If} so you can double-check that the value of $0 is what you're expecting it to be.
( since you're reading from $SYSDIR, also keep in mind any filesystem redirection magic on 64bit platforms )

Is there a different installer needed if there is a 64-bit system in place?

Because I have both .net installers for 32-bit and 64-bit versions of Windows.
My software is installed on laptops that will not have internet access therefore it is critical all prerequisites are met from the installation media itself.

What should I be aware of for 64-bit versions for the checker?


${VersionCompare} $0 "3.1.4000.2435" $R0
MessageBox MB_ICONINFORMATION|MB_OK $R0 ; if R0 is 2 then install msi 3.1 installer, else go on?

Originally posted by jweinraub
Is there a different installer needed if there is a 64-bit system in place?
No, you can have both 32bit and 64bit installers in the same package.

Originally posted by jweinraub
What should I be aware of for 64-bit versions for the checker?
Specifically, the Sytem32 folder.
On x64 platforms, if a 32bit application reads from the system folder (i.e. $SYSDIR), it gets redirected to a different folder named SysWOW64 (Windows(32) On Windows64). So if you need to target the 64bit version of the windows installer for that part, then you will have to disable filesystem redirection (Windows XP) or read from the SysNative folder (Vista, 7) instead.
Have a search through the forums - this pops up a few times :)

Originally posted by jweinraub

${VersionCompare} $0 "3.1.4000.2435" $R0
MessageBox MB_ICONINFORMATION|MB_OK $R0 ; if R0 is 2 then install msi 3.1 installer, else go on?
correct - but, again, check whether the value of $0 is what you're expecting it to be with a little messagebox first.. if you have further scripts inbetween $0 might be overwritten, or perhaps the file doesn't exist and the version returned would be 0.0.0.0, etc.

Originally posted by Animaether
No, you can have both 32bit and 64bit installers in the same package.
No I meant the WindowsInstaller-KB893803-v2-x86.exe installer. Is ther an x64 version as well.

Because I do this for x64 when it comes to .net.

 ; Detect .net 2.0 here ;
System::Call `mscoree::GetCORVersion(w .R0, i ${NSIS_MAX_STRLEN}, *i) ?u`
StrCpy $R0 $R0 1 1

; Compare if $R0 < 2 using ${If}

${If} $R0 < 2
; First detect if this is a 64 bit OS because you will want to install that instead ...
GetVersion::WindowsPlatformArchitecture
Pop $R1
${AndIf} $R1 == 64
SetDetailsPrint textonly
DetailPrint "Installing Microsoft .NET 2.0 x64. This may take up to ten minutes, please be patient."
SetDetailsPrint none
ExecWait `"$INSTDIR\Misc\NetFx64.exe" /q:a /c:"install.exe /noaspupgrade /qb"` $R0
;SetRebootFlag true
${OrIf} $R1 == 32
SetDetailsPrint textonly
DetailPrint "Installing Microsoft .NET 2.0..."
SetDetailsPrint none
ExecWait `"$INSTDIR\Misc\dotnetfx.exe" /q:a /c:"install.exe /noaspupgrade /qb"` $R0
;SetRebootFlag true
${Else}
Goto go_on
${EndIf}


Originally Posted by Animaether Specifically, the Sytem32 folder.
On x64 platforms, if a 32bit application reads from the system folder (i.e. $SYSDIR), it gets redirected to a different folder named SysWOW64 (Windows(32) On Windows64). So if you need to target the 64bit version of the windows installer for that part, then you will have to disable filesystem redirection (Windows XP) or read from the SysNative folder (Vista, 7) instead.
I apologise but this part I am still unclear with. I understand the different sys dirs (like program dirs), but isnt the global variable determine the correct path at runtime?
Essentially if the user doesnt have msi installer, or an older version than 3.1 I like for it to be installed. Other wise if the version is equal to or newer than the minimum it should do nothing and go on.

why do i get all that whitespace at line breaks?!


There doesn't appear to be a 64-bit version of Windows Installer 3.1 - I assume 4.5 is for those platforms. The forum puts in those new lines. I did ask one of the administrators but they haven't done anything. Instead of using the GetVersion plug-in you can just !include x64.nsh and use ${If} ${RunningX64}.

Edit: If you're thinking of SetShellVarContext then that is something else. File system redirection is part of the OS and as already mentioned specifying for example C:\Windows\System32 when querying the file system from a 32-bit application, it will be redirected to C:\Windows\SysWOW64.

Edit #2: In other words MessageBox MB_OK $SYSDIR will print the expected value (System32) but if you write to it your file will be in SysWOW64 instead unless you do ${DisableX64FSRedirection} first.

Stu


What does SetShellVarContext have to do with this? I actually make use of it only so that it installs on desktop shortcuts for All Users rather than local user (i already assume its being installed by admin).

So for intents and purposes, I can use the VersionCompare the way I am so I can just call the installer if necessary?

I assume even if it 0.0.0.0, the 3.x.x.x is still greater so it'll still install if needed, correct?

So what am I missing here by just doing it the way above?

Can I just do this?

; ... snip
${VersionCompare} $0 "3.1.4000.2435" $R0
${If} $R0 == 2
SetDetailsPrint textonly
DetailPrint "Installing Microsoft Windows Installer 3.1..."
SetDetailsPrint none
ExecWait `"$INSTDIR\Misc\WindowsInstaller-KB893803-v2-x86.exe" /quiet"` $R0
${EndIf}
; ... snip


SetShellVarContext has nothing to do with it, hence why I said that. VersionCompare will read an empty string as 0 (so an empty $0 will not break the code).

Stu