I'm making a simple NSIS installator on Win 10 and I'm having some issues with a function ReadRegStr. ReadRegStr returns an empty string and sets a error flag which means the value could not be found. The value definitely exists (it was made by me) and is of a proper type REG_SZ.
The same behavior occurs even with SOME other keys:
HKLM SOFTWARE\FooBar (not working)
HKLM SOFTWARE\Docker Inc.\Docker\1.0 (not working)
HKLM SOFTWARE\Classes/.3gp (working)
HKCU Software\Python\PythonCore\3.6\InstallPath (working)
Powershell finds the values without any problems.
PS C:\Users\Admin\test> Get-ItemProperty -Path HKLM:\SOFTWARE\FooBar
(default) : fb
Here is a lightweight nsi script which I'm using
OutFile "Installer.exe"
Var FOO_VAR
!include LogicLib.nsh
Section
ReadRegStr $FOO_VAR HKLM "SOFTWARE\FooBar" ""
${If} ${Errors}
MessageBox MB_OK "Value not found"
${Else}
MessageBox MB_OK "FooBar $FOO_VAR"
${EndIf}
SectionEnd
All the keys above have at least read permission for every user/installer.
What else could be causing this?
64-bit Windows has two registry "views" and 32-bit applications see the 32-bit view by default. You can use the SetRegView instruction to force a 32-bit NSIS installer to use to the 64-bit view:
!include x64.nsh
!include LogicLib.nsh
Section
${If} ${RunningX64}
SetRegView 64
ReadRegStr ... value on 64-bit systems
SetRegView LastUsed
${Else}
ReadRegStr ... value on 32-bit systems
${EndIf}
SectionEnd
Related
I'm making use of optional installation of components in NSIS installer as shown in Controlling Available Install Options on the NSIS wiki.
This works fine, dependent on what options the user has chosen, the related parts are installed.
But now there is a problem with the start menu entries which come in a separate section:
Section -startmenu
SetShellVarContext all
CreateDirectory "$SMPROGRAMS\MyInstall"
CreateShortCut "$SMPROGRAMS\MyInstallOpenAPC\Editor1.lnk" "$INSTDIR\Editor1.exe" "" "$INSTDIR\icon.ico" 0
CreateShortCut "$SMPROGRAMS\MyInstallOpenAPC\Editor2.lnk" "$INSTDIR\Editor2.exe" "" "$INSTDIR\icon.ico" 0
CreateShortCut "$SMPROGRAMS\MyInstallOpenAPC\Editor3.lnk" "$INSTDIR\Editor3.exe" "" "$INSTDIR\icon.ico" 0
CreateShortCut "$SMPROGRAMS\MyInstallOpenAPC\Editor4.lnk" "$INSTDIR\Editor4.exe" "" "$INSTDIR\icon.ico" 0
Sectionend
Dependent on the optionally installed packages I want to create only some of the start menu entries.
How can this be done? How can I create a relation between start menu entries and the installed sections?
You can check if the relevant component (section) is selected:
!include LogicLib.nsh
InstallDir "$ProgramFiles\MyApp"
Page Components
Page InstFiles
Section "Foo" SID_FOO
SetOutPath "$InstDir\Foo"
; ...
SectionEnd
Section "Bar" SID_BAR
SetOutPath "$InstDir\Bar"
; ...
SectionEnd
Section "Start menu shortcuts"
SetShellVarContext all
${If} ${SectionIsSelected} ${SID_FOO}
CreateDirectory "$SMPROGRAMS\MyApp"
CreateShortcut "$SMPROGRAMS\MyApp\Foo.lnk" "$InstDir\Foo\Foo.exe"
${EndIf}
${If} ${SectionIsSelected} ${SID_BAR}
CreateDirectory "$SMPROGRAMS\MyApp"
CreateShortcut "$SMPROGRAMS\MyApp\Bar.lnk" "$InstDir\Bar\Bar.exe"
${EndIf}
SectionEnd
Is there a NSIS variable for %ALLUSERSPROFILE%?
If not do you know how I can obtain this environment variable using NSIS code?
Note: If I use ReadEnvStr $R7 "ALLUSERSPROFILE", $R7 contains C:/ProgramData because the installer has requested elevated privileges (RequestExecutionLevel admin). This is sooo frustrating!
Starting with Vista %ALLUSERSPROFILE% is %SystemDrive%\ProgramData. Some of the things that used to be under All Users was moved to %Public% and the rest is in %ProgramData%.
There are several ways to get this directory but they should all give you the same answer:
ReadEnvStr $0 "ALLUSERSPROFILE"
DetailPrint %ALLUSERSPROFILE%=$0
System::Call 'userenv::GetAllUsersProfileDirectory(t.r0,*i${NSIS_MAX_STRLEN})i.r1'
DetailPrint GetAllUsersProfileDirectory=$0
; In Vista+ %ALLUSERSPROFIL% and CSIDL_COMMON_APPDATA is the same directory:
SetShellVarContext all
DetailPrint All:Appdata=$AppData
!define FOLDERID_ProgramData {62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}
System::Call 'shell32::SHGetKnownFolderIDList(g"${FOLDERID_ProgramData}", i0x1000, i0, *i.r1)i.r0'
${If} $0 == 0
System::Call 'shell32::SHGetPathFromIDList(ir1,t.r0)'
System::Call 'ole32::CoTaskMemFree(ir1)'
DetailPrint SHGetKnownFolderIDList=$0
${EndIf}
To expand on #Anders reply, you could also use SHGetSpecialFolderPath and make a simple one line call to receive the path to a folder on the operating system.
System::Call 'shell32::SHGetSpecialFolderPath(i $HWNDPARENT, t .r1, i 0x23, i0)i.r0'
Use it with CSIDL Values, and you can simply call a function and pop the return.
Function ".OnInit"
System::Call 'shell32::SHGetSpecialFolderPath(i $HWNDPARENT, t .r1, i 0x23, i0)i.r0'
pop $1
MessageBox MB_OK|MB_ICONINFORMATION "$1"
FunctionEnd
Section ""
; blank section (so the script runs)
SectionEnd
This will return C:\ProgramData (Vista+) or C:\Documents and Settings\All Users\Application Data (XP) into $1 and display a Message Box showing the path.
By switching up the CSIDL Value (0x23) you can return the path to a bunch of different system folders.
Here are some common CSIDL Values that you can use to return the path:
0x0 Desktop
0x2 Programs
0x5 My document
0x6 Favorites
0x7 Startup
0x8 Recent documents
0x9 Sendto documents
0x10 Desktop Directory
0x11 My Computer
0x14 Fonts directory
0x15 Windows Template
0x20 Internet Cache
0x21 Cookies
0x22 History
0x23 Common Application Data
0x25 System
0x26 Program Files
0x27 My Pictures
0xb StartMenu
0xd My Music
0x1a Application Data
0x1c Local Application Data
0x2b Common Program Files
I have an NSIS script that has a show readme function that launches a pdf successfully in all OS's using the following line:
ExecShell "open" "$0\$(APP_DATA_PATH)\AppData\Readme\readme_$(LOCAL_CODE).pdf"
BUT - not in Windows 8. If any other reader other than Adobe is the default reader, it seems to be ok. We have to support Adobe and there seems to be nothing wrong with launching the pdf file elsewhere in Windows 8.
Is there any other NSIS command I can try to launch the file that I can try? Any other suggestions?
Try using the default verb: ExecShell "" "c:\full\path\to\file.pdf"
Edit:
Section
StrCpy $0 "$desktop\test.pdf"
; This should be the same as using ExecShell
System::Call 'shell32::ShellExecute(i$hwndparent,i0,t"$0",i0,i0,i5)i.r1'
DetailPrint "ShellExecute: Return=$1 (> 32 for success)"
; Let's try really hard by using SEE_MASK_INVOKEIDLIST
!define SEE_MASK_INVOKEIDLIST 0x0000000C
!define SEE_MASK_FLAG_DDEWAIT 0x00000100
System::Call '*(i60,i${SEE_MASK_INVOKEIDLIST}|${SEE_MASK_FLAG_DDEWAIT},i$hwndparent,i0,t"$0",i0,i0,i5,i,i0,i,i,i,i,i)i.r2' ; Allocate SHELLEXECUTEINFO
System::Call 'shell32::ShellExecuteEx(ir2)i.r1'
System::Free $2
DetailPrint "ShellExecuteEx: Success=$1"
SectionEnd
In NSIS is there a way to determine what version of windows the user is currently running?
The reason I want to do this is because my installer looks different on a Windows XP computer. My installer uses MUI2 but I dont seem to have the same GUI buttons(I think its called XP Style) as I do in Windows 7 and the main installer window is much larger than in Windows 7(where its about 500 by 400 pixels). Is it normal to a have these differences in an installer using MUI2? I thought MUI2 made the look consistant in windows versions XP and up?
To overcome the difference in installer window size, my solution is to detect if the user is using Windows XP and resize the window accordingly. Is this possible?
I need to have the window a specific size because I have a background image and the image is 500px wide so if the installer window is bigger I have a blank gap. I can change the background image to be wider but the easiest solution for myself is the one I explained above
In case Anders' answer is not explicit enough (took me a few hours to get it right), here is a more "beginner's friendly" version.
You will need to add !include WinVer.nsh to the top section of the cd.nsi file.
You then can use code like this:
${If} ${IsWinXP}
MessageBox MB_OK|MB_ICONEXCLAMATION "We have Win XP"
${EndIf}
This is the only function I tested, but the WinVer.nsh file starts with a mini-manual with its functions, which include:
AtLeastWin<version> which checks if the installer is running on Windows version at least as specified.
IsWin<version> which checks if the installer is running on Windows version exactly as specified.
AtMostWin<version> which checks if the installer is running on Windows version at most as specified.
<version> can be replaced with the following values (and maybe more, depending on how recent your WinVer.nsh file is): 95, 98, ME, NT4, 2000, XP, 2003, Vista, 2008, 7, 2008R2
There are some more functions and some usage examples in the WinVer.nsh file, which is probably located somewhere like C:\Program Files\NSIS\Include, like:
AtLeastServicePack which checks if the installer is running on Windows service pack version at least as specified.
IsServicePack which checks if the installer is running on Windows service pack version exactly as specified.
AtMostServicePack which checks if the installer is running on Windows service version pack at most as specified.
IsWin2003R2 (no more details supplied)
IsStarterEdition (no more details supplied)
OSHasMediaCenter (no more details supplied)
OSHasTabletSupport (no more details supplied)
MUI does not resize the window based on the Windows version. The window size is affected by the font and DPI settings however.
Use WinVer.nsh to detect the Windows version. This module is included in the NSIS includes folder by default.
The snippet bellow shows how to identify the Windows Version with as many detail as I could imagine to be useful:
!include WinVer.nsh
!include "LogicLib.nsh"
Function LogWinVer
${WinVerGetMajor} $R0
${WinVerGetMinor} $R1
${WinVerGetBuild} $R2
${WinVerGetServicePackLevel} $R3
; determine windows product name
${If} $R0 == 5
${If} $R1 == 0
DetailPrint "Windows 2000 SP $R3"
${ElseIf} $R1 == 1
DetailPrint "Windows XP SP $R3"
${ElseIf} $R1 == 2
DetailPrint "Windows Server 2003 SP $R3"
${EndIf}
${ElseIf} $R0 == 6
${If} $R1 == 0
${If} ${IsServerOS}
DetailPrint "Windows Server 2008 SP $R3"
${Else}
DetailPrint "Windows Vista SP $R3"
${EndIf}
${ElseIf} $R1 == 1
${If} ${IsServerOS}
DetailPrint "Windows Server 2008 R2 SP $R3"
${Else}
DetailPrint "Windows 7 SP $R3"
${EndIf}
${ElseIf} $R1 == 2
${If} ${IsServerOS}
DetailPrint "Windows Server 2012 SP $R3"
${Else}
DetailPrint "Windows 8 SP $R3"
${EndIf}
${ElseIf} $R1 == 3
${If} ${IsServerOS}
DetailPrint "Windows Server 2012 R2 SP $R3"
${Else}
DetailPrint "Windows 8.1 SP $R3"
${EndIf}
${EndIf}
${EndIf}
; version
DetailPrint "Kernel $R0.$R1 build $R2"
; x86 or x64:
Call LogWinVer
System::Call "kernel32::GetCurrentProcess() i .s"
System::Call "kernel32::IsWow64Process(i s, *i .r0)"
StrCmp $0 "0" is32bit is64bit
is32bit:
DetailPrint "32 bit"
Goto exit
is64bit:
DetailPrint "64 bit"
exit:
FunctionEnd
You could also read from the registry directly:
ReadRegStr $WinEdition HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "ProductName"
Next, you can compare it using "==", for example:
${If} $WinEdition == "Windows XP"
or you could use StrContains to check if the windows version contains "Windows XP"
I am newbie to nsis installer. I would like to detect the country of the user(via ip) and then depending on the country want to do some actions. I searched in nsis plugins directory but I found only a plugin to detect the ip.
How can I get the country code via ip in nsis ?
Thank you
Detecting country from IP means you need:
Internet connection
A way to determine the external IP (not counting proxies?)
Access to a IP/Geo database
Why not use the Windows configuration on the local machine:
!include LogicLib.nsh
!define LOCALE_SCOUNTRY 6 ; Localized
!define LOCALE_SENGCOUNTRY 4098
!define LOCALE_SENGLANGUAGE 0x00001001
!define GEOCLASS_NATION 16
!define GEOID_NOT_AVAILABLE -1
!define GEO_ISO2 4
!define GEO_ISO3 5
Section
System::Call 'KERNEL32::GetUserDefaultLangID()i.r0'
DetailPrint LANGID=$0
System::Call 'KERNEL32::GetLocaleInfo(i$0,i${LOCALE_SENGCOUNTRY},t.r1,i1000)'
DetailPrint LOCALE_SENGCOUNTRY=$1
System::Call 'KERNEL32::GetLocaleInfo(i$0,i${LOCALE_SCOUNTRY},t.r1,i1000)'
DetailPrint LOCALE_SCOUNTRY=$1
System::Call 'KERNEL32::GetLocaleInfo(i$0,i${LOCALE_SENGLANGUAGE},t.r1,i1000)'
DetailPrint LOCALE_SENGLANGUAGE=$1
System::Call 'KERNEL32::GetUserGeoID(i${GEOCLASS_NATION})i.r0'
DetailPrint GEOID=$0
${If} $0 <> ${GEOID_NOT_AVAILABLE} ; Only available if the user has set a country/location
${AndIf} $0 != "error" ; GetUserGeoID is WinXP+
System::Call 'KERNEL32::GetGeoInfo(i$0,i${GEO_ISO2},t.r1,i1000,i0)'
DetailPrint GEO_ISO2=$1
System::Call 'KERNEL32::GetGeoInfo(i$0,i${GEO_ISO3},t.r1,i1000,i0)'
DetailPrint GEO_ISO3=$1
${EndIf}
SectionEnd