NSIS - check if registry key value exists - nsis

I need to check, if a registry value exists. How can I do that?
My first approach:
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports" "NUL:"
${IF} $0 == ""
MESSAGEBOX MB_OK "NUL exists"
${ELSE}
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports" "NUL:" ""
${ENDIF}
But this also works, when the value doesn’t exist. I guess, because "doesn’t exist" and empty string are handled the same way.
With Registry.nsh I did it like this:
${registry::Read} "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports" "NUL:" $var1 $var2
${IF} $var2 == "REG_SZ"
But I get an error, because the Pop ${_STRING} in the registry.nsh doesn’t work.
Help and suggestions welcome!

You should check the error flag after reading:
ClearErrors
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports" "NUL:"
${If} ${Errors}
MessageBox MB_OK "Value not found"
${Else}
${IF} $0 == ""
MESSAGEBOX MB_OK "NUL exists and it's empty"
${ELSE}
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports" "NUL:" ""
${ENDIF}
${EndIf}
Also, you may be interested in EnumRegValue before trying to read it.

Related

NSIS script doesn't display service status

I created section at the end of script which suppose to display myservice status, but no messagebox displays after exec.
What might be the reason?
Section "Create Service"
ExecShellWait '' 'sc.exe' 'create myservice error= "severe" displayname= "myservice" type= "own" start= "auto" binpath= "$INSTDIR\MyService.exe"' SW_HIDE
SectionEnd
Section "Start Service"
ExecShellWait '' 'sc.exe' 'start myservice' SW_HIDE
SectionEnd
Section "Ensure Running"
StrCpy $R0 '"$SYSDIR\cmd.exe" /c "sc QUERY myservice | FIND /C "RUNNING""'
nsExec::ExecToStack '$R0'
Pop $R1 # contains return code
Pop $R2 # contains output
${If} $R1 == "0"
DetailPrint "checking if command is success"
${If} $R2 == "1"
MessageBox mb_ok "myservice is Running" # it's running
${Else}
MessageBox mb_ok "Not Running" # it's not running
${EndIf}
${Else}
DetailPrint "command failed"
${EndIf}
SectionEnd ```
nsExec::ExecToStack /OEM '$R0' didn't change anything. During installation it rushes to finish page after sc exec ..Do I need to add additional page page here ?
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE ..\license.rtf
; Page custom pageRegistration
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE English
Without seeing the output I can only guess that there is some sort of Unicode issue.
Change nsExec::ExecToStack '$R0' to nsExec::ExecToStack /OEM '$R0'.
That being said, I don't know if the RUNNING string is always in English. There are several service plug-ins and macros on the NSIS wiki you can use instead.
Or you can use this:
!include "LogicLib.nsh"
!include "Win\COM.nsh"
!include "Win\Propkey.nsh"
Function IsServiceRunning
Exch $0
Push $1
StrCpy $1 $0
!insertmacro ComHlpr_CreateInProcInstance {13709620-C279-11ce-A49E-444553540000} {A4C6892C-3BA9-11D2-9DEA-00C04FB16162} r0 "" ; IE5+?
${If} $0 P<> 0
System::Call "$0->36(wr1,#r1)i.s"
${IUnknown::Release} $0 ""
Pop $0
${If} $0 >= 0
StrCpy $0 0
Push $2
${V_GetVT} $1 r2
${V_GetBOOL} $1 r1
${If} $2 = ${VT_BOOL}
${AndIf} $1 <> ${VARIANT_FALSE}
StrCpy $0 1
${EndIf}
Pop $2
${Else}
StrCpy $0 0
${EndIf}
${EndIf}
Pop $1
Exch $0
FunctionEnd
Section "Example"
Push "RemoteRegistry"
Call IsServiceRunning
Pop $0
DetailPrint "RemoteRegistry running=$0"
Push "Themes"
Call IsServiceRunning
Pop $0
DetailPrint "Themes running=$0"
SectionEnd

NSIS GetVolumeInformation Results

Ok I found this code from another question asked and I'm a newbie for NSIS. Question Below
!define FILE_SUPPORTS_ENCRYPTION 0x00020000
!define FILE_READ_ONLY_VOLUME 0x00080000
!define FILE_VOLUME_QUOTAS 0x00000020
!macro MakeBitFlagYesNo flags bit outvar
IntOp ${outvar} ${flags} & ${bit}
${IfThen} ${outvar} <> 0 ${|} StrCpy ${outvar} "Yes" ${|}
${IfThen} ${outvar} == 0 ${|} StrCpy ${outvar} "No" ${|}
!macroend
StrCpy $0 "c:\"
System::Call 'Kernel32::GetVolumeInformation(t "$0",t,i ${NSIS_MAX_STRLEN},*i,*i,*i.r1,t,i ${NSIS_MAX_STRLEN})i.r0'
${If} $0 <> 0
!insertmacro MakeBitFlagYesNo $1 ${FILE_SUPPORTS_ENCRYPTION} $2
!insertmacro MakeBitFlagYesNo $1 ${FILE_READ_ONLY_VOLUME} $3
!insertmacro MakeBitFlagYesNo $1 ${FILE_VOLUME_QUOTAS} $4
MessageBox mb_ok "flags=$1 $\nFILE_SUPPORTS_ENCRYPTION=$2$\nFILE_READ_ONLY_VOLUME=$3$\nFILE_VOLUME_QUOTAS=$4"
${EndIf}
Here is what I need to get:
Drive Volume Label:
File System: (Fat32, NTFS, exFat, ...)
Disk Capacity: (7.27TB, 5.59TB, ...)
Free Space: (4.62TB , 632GB, ...)
Allocation Unit Size: (4096b, 16kb, 32kb, ...)
and then I need to use that info in a if then statement
Just not sure how to turn the above code and results into code list below.
I have tried searching google about NSIS and GetVolumeInformation, and with the GetVolumeInformation I could not find out how to get and read results anywhere.
${If} $File_System <> "NTFS"
${EndIf}
${If} $Disk_Capacity < "1.86TB"
${EndIf}
${If} $Free_Space < "1.25TB"
${EndIf}
${If} $Allocation_Unit_Size <> "4096 bytes"
${EndIf}
MessageBox mb_ok "$Drive_Volume_Label$\n$File_System$\n$Disk_Capacity$\n$Free_Space$\n$Allocation_Unit_Size"
And if you could post the answer code for me and then point me where you got the info (the explanation of the) code. This help me learn the code faster if I know what its doing for what I need.
Thanks,
Albert
The code in the answer you found does actually return the name of the file system and then it goes on to tell you that that is probably not the best way to do things. You should use the file system flags if possible. Case in point, Microsofts new file system ReFS supports some of the NTFS features and adds some new reliability features.
!include LogicLib.nsh
StrCpy $9 "c:\"
System::Call 'Kernel32::GetVolumeInformation(t "$9",t.r3,i ${NSIS_MAX_STRLEN},*i,*i,*i.r1,t.r2,i ${NSIS_MAX_STRLEN})i.r0'
${If} $0 <> 0
IntFmt $1 "%#.8x" $1
MessageBox MB_OK " FileSystemFlags=$1 $\n FileSystemName=$2 $\n VolumeName=$3"
${If} $2 == "NTFS"
MessageBox MB_OK "$9 is NTFS"
${Else}
MessageBox MB_OK "$9 is $2 and not NTFS but hopefully you don't care"
${EndIf}
${EndIf}
Your other properties are not exposed by this function which would be obvious if you looked at its documentation on MSDN.
!include LogicLib.nsh
StrCpy $9 "c:\"
System::Call 'Kernel32::GetDiskFreeSpaceEx(t "$9", *l.r1, *l.r2, *l.r3)i.r0' ; This function does not work on Windows 95
${If} $0 <> 0
MessageBox MB_OK " FreeBytesAvailable=$1 $\n TotalNumberOfBytes=$2 $\n TotalNumberOfFreeBytes=$3"
System::Call 'ShLwApi::StrFormatByteSizeW(l $1, w.r4, i ${NSIS_MAX_STRLEN})'
MessageBox MB_OK "Friendly FreeBytesAvailable: $4" ; For display in UI only!
!define /math MYSIZE 0x100000 * 666 ; 666 MiB
${If} $1 L>= ${MYSIZE}
MessageBox MB_OK "Disk quota on $9 allows you to write at least ${MYSIZE} bytes on this volume"
${Else}
MessageBox MB_OK|MB_ICONSTOP "Not enough space on volume, ${MYSIZE} bytes required!"
Quit
${EndIf}
${EndIf}
NSIS already provides a directory page that checks the disk space, use that if possible. Note: You cannot compare numbers of different scales (TB vs GB etc.), you must compare a 64-bit count of bytes.
By allocation unit I assume you are talking about the cluster size?
!include LogicLib.nsh
StrCpy $9 "c:\"
System::Call 'Kernel32::GetDiskFreeSpace(t "$9", *i.r1, *i.r2, *i, *i)i.r0'
${If} $0 <> 0
IntOp $1 $1 * $2
System::Call 'ShLwApi::StrFormatByteSizeW(l $1, w.r3, i ${NSIS_MAX_STRLEN})'
MessageBox MB_OK "Cluster size: $3 ($1 bytes)"
${If} 4096 <> $1
; ...
${EndIf}
${EndIf}
(The reported cluster size is probably the logical size, not the physical size)

How to get the correct return value/status of VC++ 2005 in NSIS

I want to silently install Visual C++ Redistributable Packages for Visual Studio 2013 with NSIS.
However, the correct exit code is not being returned. I tested with several types of variations. The best I came up with is:
DetailPrint "Installing VC++ 2005"
File "${STOCKINPUTFOLDER}\bin\Debug\vcredist_x86.EXE"
ClearErrors
ExecWait "$OUTDIR\vcredist_x86.EXE /q:a /c:$\"msiexec /i vcredist.msi /qb /norestart$\"" $0 ; also can use /qn
;do not delete the file out-rightly so that evaluation of IfErrors below is clean
IfErrors execError noError
execError:
Goto ShortEndInstall
ShortEndInstall:
MessageBox MB_OK|MB_ICONEXCLAMATION "ShortEndInstall"
Delete "$OUTDIR\vcredist_x86.EXE" ;we did not delete the file outrightly so that evaluation of IfErrors is clean
Quit
noError:
${If} $0 != 0
MessageBox MB_OK|MB_ICONEXCLAMATION "You did not complete the installation of the VC++ 2005 re-distributable. Installation will now abort. Return code $0"
Goto ShortEndInstall
${EndIf}
MessageBox MB_OK|MB_ICONEXCLAMATION "No error"
Delete "$OUTDIR\vcredist_x86.EXE" ;we did not delete the file outrightly so that evaluation of IfErrors is clean
But when I intentionally clicked on the "Cancel" button of the VC++2005 installation, NSIS still took it as being successful installation (I got the dialog box at instruction MessageBox MB_OK|MB_ICONEXCLAMATION "No error")
How can I accurately and correctly get the returned error (e.g. when I "Cancel"ed the installation myself)?
EDIT:
Following the help by #Anders I now have the following, which still fails (i.e. not installed even while the VC++ 2005 installation was completed. I also tried different command line options for the installation VC++2005)
!include LogicLib.nsh
!include WordFunc.nsh
!define VERSION "8.1.0.0"
!define SHORTVERSION "8.1"
!define NAME "SchoolServer MainHost"
!define SHARPDEVFOLDER "..\..\..\.."
!define STOCKINPUTFOLDER "${SHARPDEVFOLDER}\SchoolServer_100\SS-Server"
!define PARENTSTOCKFOLDER "${SHARPDEVFOLDER}\SchoolServer_100"
!define MSVC2005_X86REDIST_PRODUCTCODE {A49F249F-0C91-497F-86DF-B2585E8E76B7}
!define MSVC2005_X86REDIST_PRODUCTCODE_SP1 {7299052B-02A4-4627-81F2-1818DA5D550D}
!define MSVC2005_X86REDIST_PRODUCTCODE_SP1_UP {837B34E3-7C30-493C-8F6A-2B0F04E2912C}
!define INSTALLSTATE_DEFAULT 5
!define MACRO_check_successful_install "!insertmacro check_vc_install"
!define MACRO_check_successful_install_exclusive_2005 "!insertmacro check_vc_install_exclusive_2005"
Var function_call_return
Var tested_vc_key
!macro check_vc_install install_key
DetailPrint "Copying ${install_key} to tested_vc_key"
StrCpy $tested_vc_key ${install_key}
call check_vc_install_normal
!macroend
!macro check_vc_install_exclusive_2005 install_key
StrCpy $tested_vc_key ${install_key}
call check_vc_install_exclusive_2005
!macroend
OutFile "${NAME} Lite Installer - ${SHORTVERSION}.exe"
#Sections
Section "Installation"
SetShellVarContext all
SetOutPath "$INSTDIR"
SetRebootFlag false
DetailPrint "Installing VC++ 2005"
File "${STOCKINPUTFOLDER}\bin\Debug\vcredist_x86.EXE"
ClearErrors
ExecWait "$OUTDIR\vcredist_x86.EXE /q:a /c:$\"msiexec /i vcredist.msi /qb /norestart$\" " $0 ; also can use /qn
;we will not delete the file out-rightly so that evaluation of IfErrors below is clean
IfErrors execError noError
execError:
MessageBox MB_OK|MB_ICONEXCLAMATION "First line error!"
Goto ShortEndInstall
ShortEndInstall:
MessageBox MB_OK|MB_ICONEXCLAMATION "Installation of Microsoft Visual C++ 2005 runtime libraries failed!"
Delete "$OUTDIR\vcredist_x86.EXE" ;we did not delete the file outrightly so that evaluation of IfErrors is clean
;Quit
Return
noError:
;if no error, then check the exit code (https://sourceforge.net/p/nsis/mailman/message/22965694/ [use timemachine if 404 error. Page was accessed June 26, 2017])
${If} $0 != 0
MessageBox MB_OK|MB_ICONEXCLAMATION "You did not complete the installation of the VC++ 2005 re-distributable. Installation will now abort. [Error $0]"
Goto ShortEndInstall
${EndIf}
ClearErrors
${MACRO_check_successful_install} ${MSVC2005_X86REDIST_PRODUCTCODE}
${If} $function_call_return == "installed"
Goto ContinueInstallation
${Else}
${MACRO_check_successful_install} ${MSVC2005_X86REDIST_PRODUCTCODE_SP1}
${If} $function_call_return == "installed"
Goto ContinueInstallation
${Else}
${MACRO_check_successful_install} ${MSVC2005_X86REDIST_PRODUCTCODE_SP1_UP}
${If} $function_call_return == "installed"
Goto ContinueInstallation
${Else}
${MACRO_check_successful_install_exclusive_2005} ${MSVC2005_X86REDIST_PRODUCTCODE}
${If} $function_call_return == "installed"
Goto ContinueInstallation
${Else}
${MACRO_check_successful_install_exclusive_2005} ${MSVC2005_X86REDIST_PRODUCTCODE_SP1}
${If} $function_call_return == "installed"
Goto ContinueInstallation
${Else}
${MACRO_check_successful_install_exclusive_2005} ${MSVC2005_X86REDIST_PRODUCTCODE_SP1_UP}
${If} $function_call_return == "installed"
Goto ContinueInstallation
${Else}
DetailPrint "Quiting... [Returned value: $function_call_return]"
Goto ShortEndInstall
${EndIf}
${EndIf}
${EndIf}
${EndIf}
${EndIf}
${EndIf}
ContinueInstallation:
Delete "$OUTDIR\vcredist_x86.EXE" ;we did not delete the file outrightly so that evaluation of IfErrors is clean
CreateDirectory "$INSTDIR\idcard"
File /r "${STOCKINPUTFOLDER}\bin\Debug\idcard"
SectionEnd
Function check_vc_install_normal
DetailPrint "check_vc_install_normal: Checking installation of $tested_vc_key"
System::Call 'MSI::MsiGetProductInfo(t "$tested_vc_key", t "ProductName", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0'
ClearErrors
${If} $0 == 0
DetailPrint "ProductName: $1"
System::Call 'MSI::MsiGetProductInfo(t "$tested_vc_key", t "AssignmentType", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0'
DetailPrint "AssignmentType: $1"
System::Call 'MSI::MsiGetProductInfo(t "$tested_vc_key", t "PackageCode", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0'
DetailPrint "PackageCode: $1"
System::Call 'MSI::MsiGetProductInfo(t "$tested_vc_key", t "VersionString", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0'
DetailPrint "VersionString: $1"
StrCpy $function_call_return "installed"
${Else}
DetailPrint "Not installed [$0] [from check_vc_install_normal]"
StrCpy $function_call_return "not installed"
${EndIf}
FunctionEnd
Function check_vc_install_exclusive_2005
DetailPrint "check_vc_install_exclusive_2005: Checking special vc2005 installation of $tested_vc_key"
ClearErrors
System::Call 'MSI::MsiQueryProductState(t "$tested_vc_key")i.r0'
${If} ${INSTALLSTATE_DEFAULT} = $0
StrCpy $function_call_return "installed"
${Else}
DetailPrint "Not installed [$0] [from check_vc_install_exclusive_2005]"
StrCpy $function_call_return "not installed"
${EndIf}
FunctionEnd
Perhaps vcredist_x86.exe is not returning the exit code correctly from MsiExec?
If ExecWait is able to find and start the process (IfErrors is "false") then NSIS is guaranteed to return the correct exit code from the child process.
$0 != 0 is actually a string comparison and you should be using $0 <> 0 to compare 32-bit numbers but it is not the problem in this case.
!include LogicLib.nsh
Section
ExecWait '"cmd" /c exit 0' $0
${If} ${Errors}
${OrIf} $0 <> 0
Abort "Error, child process exited with $0" # Will not abort here
${EndIf}
ExecWait '"cmd" /c exit 1234' $0
${If} ${Errors}
${OrIf} $0 <> 0
Abort "Error, child process exited with $0" # Will abort here
${EndIf}
SectionEnd

How can I find an application if i don't know its GUID

In order to compare versions, I have to find out if my app has been already installed.
I use registry to store whole necessary inforamtion and it would be very useful if i could somehow read strings from registry. Main issue here is that I don't know my own GUID which was randomized during previous installation.
To generate my registry path I wrote following script:
Function .onInit
${If} ${RunningX64}
StrCpy $R0 "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
${Else}
StrCpy $R0 "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
${EndIf}
FunctionEnd
MainSection:
Section "Main" sec
System::Call 'ole32::CoCreateGuid(g .s)'
Pop $0
WriteRegStr HKLM "$R0\$0" 'DisplayVersion' '${AppVersion}'
SectionEnd
So, basically I need to find a way to read DisplayVersion string. I wish there was some variation of FindFirst but for registry.
Use EnumRegKey to enumerate registry keys:
!include LogicLib.nsh
Section
StrCpy $0 0
loop:
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" $0
StrCmp $1 "" done
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "DisplayName"
${If} $2 == "My Application Name"
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "DisplayVersion"
DetailPrint "TODO: Compare $2 to version here..."
${EndIf}
IntOp $0 $0 + 1
Goto loop
done:
SectionEnd

compound string comparison in nsis

How do I do a compound string comparison in NSIS?
essentially something like: if ( str1 == "" || str2 == "" ) ...
strcpy $1 "c:\foo"
strcpy $2 "d:\bar"
${if} strcmp $1 ""
${orif} strcmp $2 ""
MessageBox MB_OK "one or both are empty"
${else}
messagebox mb_ok "both are not"
${endif}
SectionEnd
StrCmp is the low-level instruction at the heart of NSIS string comparisons but when using the LogicLib you must use the correct operators: ==, !=, S== or S!= (all of them are listed at the top of LogicLib.nsh and the case-insensitive operators use StrCmp internally)
!include LogicLib.nsh
Section
StrCpy $1 "c:\foo"
StrCpy $2 "d:\bar"
${If} $1 == ""
${OrIf} $2 == ""
MessageBox MB_OK "one or both are empty"
${Else}
MessageBox MB_OK "both are not"
${EndIf}
SectionEnd

Resources