How to get system drive volume serial number - nsis

I am building a setup which requires to retrieve the disk volume of the drive on which the Operating system is installed. I have tried the following code:
Function ShowDiskVolumeSerialNumber
!define GetVolumeInformation "Kernel32::GetVolumeInformation(t,t,i,*i,*i,*i,t,i) i"
System::Call '${GetVolumeInformation ("$0",,${NSIS_MAX_STRLEN},.r0,,,,${NSIS_MAX_STRLEN})'
IntFmt $0 "%08X" $0
MessageBox MB_OK $0
FunctionEnd
I call the above function by specifying the desired drive:
StrCpy $0 "C:\"
Call ShowDiskVolumeSerialNumber
The above gives the volume no of c:\ drive. However if my OS is installed on d:\ or e:\ it will not work. I want a function which will automatically detect the drive on which the OS is installed and retrieve its volume no.
Secondly, the volume number retrieved is without a hyphen (e.g. 349620C1). I need the volume serial number retrieved as 3496-20C1.
Could someone please give a complete NSIS function when will do both the things i.e:
Automatically detect the drive on which the OS is installed and retrieve its serial no with a hyphen in between.

To get the OS drive, StrCpy $0 $windir 3 is probably enough (You did not specify if you wanted the boot volume or the system volume, on most systems they are the same so $windir is a good starting point)
To edit the serial number:
StrCpy $0 $serial 4
StrCpy $serial $serial "" 4
StrCpy $serial "$0-$serial"

Related

How to show only local drives and folders from the Browse dialog when installing using NSIS?

Once opened the browse dialog when installing using NSIS, it is showing the list of drivers, folders, mapped drives, removable drives and network folders. How to filter it and show only the local drives and folders from the Browse dialog?
You cannot change the way the directory page works, you would have to write a custom page and a custom plug-in if you want to filter the folder dialog.
You can however validate the directory and block the user from moving to the next page:
!include LogicLib.nsh
Page Directory
Page InstFiles
!define /IfNDef DRIVE_FIXED 3
Function .onVerifyInstDir
StrCpy $0 $InstDir 1
System::Call 'KERNEL32::GetDriveType(t"$0:\")i.r0'
${If} $0 <> ${DRIVE_FIXED}
Abort
${EndIf}
FunctionEnd
In this specific case that might not be a good idea because the user has no idea why they can't click the next/install button.
Instead you should stop with a message when the user tries to leave the page:
!include LogicLib.nsh
Page Directory "" "" ValidateDirPage
Page InstFiles
!define /IfNDef DRIVE_FIXED 3
Function ValidateDirPage
StrCpy $0 $InstDir 1
System::Call 'KERNEL32::GetDriveType(t"$0:\")i.r0'
${If} $0 <> ${DRIVE_FIXED}
MessageBox MB_ICONSTOP "You must specify a local fixed drive for some reason!"
Abort
${EndIf}
FunctionEnd
Note: Some USB storage devices will identify as a fixed drive.
Even if you do all this you cannot stop people from installing to a different drive type. They could temporarily change their drive letters, install, and then change them back etc.

How to check the space in the drive by taking the path from the Destination Folder When installing the software using NSIS

How to check the space in the drive by taking the path from the Destination Folder When installing the software.
I am able to check the space of the specific drive (for eg, "C") using the below code snippet.
But I want to take the drive or path from the Destination folder dynamically and check the space of the drive whether there is enough space or not.
!define sysGetDiskFreeSpaceEx 'kernel32::GetDiskFreeSpaceExA(t, *l, *l, *l) i'
function CheckSpaceFunc
IntCmp $2 0 ignorequota
; obey quota
System::Call '${sysGetDiskFreeSpaceEx}(r1,.r2,,.)'
goto converttokb
; ignore quota
ignorequota:
System::Call '${sysGetDiskFreeSpaceEx}(r1,.,,.r2)'
converttokb:
; convert the large integer byte values into managable kb
System::Int64Op $2 / 1024
Pop $2
; check space
System::Int64Op $2 > $0
Pop $2
functionend
Section "TestApp"
SectionIn RO
StrCpy $0 40000 ; kb u need
StrCpy $1 'c:' ; check drive c: for space
Call CheckSpaceFunc
IntCmp $2 1 okay
MessageBox MB_OK "Error: Not enough disk space"
okay:
SectionEnd
Could anyone please help me
The built-in directory page (Page Directory or !insertmacro MUI_PAGE_DIRECTORY) will perform the free space check for you and takes care of all the details.
It might be tempting to just do StrCpy $1 $InstDir 3 to get the drive letter and perform the check yourself but this can give you the wrong result because NTFS supports mounting other volumes as a folder.
GetDiskFreeSpaceEx does support directory paths but I believe the path has to exist so if you want to use $InstDir before $InstDir has been created then you must chop off subfolders until GetDiskFreeSpaceEx succeeds (or you only have a invalid drive letter left of the path).
Your !define should also be changed from GetDiskFreeSpaceExA to GetDiskFreeSpaceEx because it uses the t string type. This will make it Unicode compatible.

NSIS ANTI EMULATOR

I'm trying to make a anti emulator
allocating a big memory region and force the kernel to commit the pages to physical ram by filling the memory with values because the emulator can't allocate too much memory
But I don't know how to fill the memory with values of that region
this is what I made so far
System::Call "kernel32::LocalAlloc(i 0, i 143978374) p .r0" ; allocate 143 978 374 bytes and write pointer in $0
;I need to fill the memory with values here
System::Call "kernel32::LocalFree(p r0)"
If you call LocalAlloc with the LMEM_ZEROINIT flag Windows might write to the buffer for you. Technically speaking, this might be a implementation detail and in theory a future system could support such a feature in hardware. Your emulation thing, whatever that is, might ignore it as well.
You can write to memory with the system plug-in struct syntax:
!define PAGESIZE 4096
!define BLOBSIZE 143978374
!include Util.nsh
!ifndef IntPtrOp ; NSIS 2 compatibility
!define IntPtrOp IntOp
!endif
!ifndef IntPtrCmpU
!define IntCmpU
!endif
System::Alloc ${BLOBSIZE} ; This calls GlobalAlloc(GPTR, ...)
Pop $0
${IntPtrCmpU} $0 0 done "" "" ; Failure to allocate memory?
StrCpy $1 $0 ; Start
${IntPtrOp} $2 $1 + ${BLOBSIZE} ; End
loop:
System::Call '*$1(&i1 42)' ; Set the first byte in the page to 42
${IntPtrOp} $1 $1 + ${PAGESIZE}
${IntPtrCmpU} $1 $2 "" loop ""
System::Free $0
done:
The System plug-in is not super fast so a large memory block will take a while to fill.

get hard disk drive name in which windows OS installed using NSIS

I need to get hard disk drive letter like C: or D: or any in which the OS is installed on any windows.
Kindly help me get out of this.
Section
StrCpy $0 $sysdir 3
DetailPrint $0
SectionEnd
$SYSDIR is the Windows system directory (usually C:\Windows\System or C:\WinNT\System32, but that's detected at runtime).
StrCpy has the syntax StrCpy user_var(destination) str(source) [maxlen] [start_offset] where maxlen and start_offset are optional.
So the code above, copies the first three letters from $SYSDIR into the $0 variable.

How do you request administrator permissions using NSIS?

I am quite new with NSIS.
I am trying to request administrator permissions in order to run the installer, as it messes around a bit with registries.
My problem with "RequestExecutionLevel" and "MULTIUSER_EXECUTIONLEVEL" is that they both absolutely block any non-Admin user from opening the installer, even when selecting "Run as Administrator" in the context menu.
I have tried using the RunAs DLL, but I have not found a single thread as to what to put in the $command variable passed to "RunAsW" function.
Here is my (pretty hacked-up) code:
StrCpy $0 0
StrCpy $1 ""
System::Call 'RunAs::GetAdministrators(w r1, *i .r0) i .r2 ? u'
System::Alloc 64
Pop $4
StrCpy $4 $2
StrCpy $5 ""
loop:
IntCmp $0 0 endloop
System::Call '*$4(w .r3)'
StrCpy $5 "$5|$3"
endloop:
System::Free $4 ; we free the memory used by the array
StrCpy $5 "$5" "" 1
!insertmacro MUI_INSTALLOPTIONS_WRITE "Settings.ini" "Field 1" "ListItems" $5
!insertmacro MUI_INSTALLOPTIONS_DISPLAY "Settings.ini"
!insertmacro MUI_INSTALLOPTIONS_READ $1 "UserPass" "Field 1" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $2 "Settings.ini" "Field 2" "State"
StrCpy $3 "%%LOGONSERVER%%"
StrCpy $3 0
StrCpy $4 0
System::Call 'RunAs::RunAsW(w r1, w r2, w r3, *w .r4) i .r0 ? u'
MessageBox MB_OK $0
IntCmp $0 1 success
Quit
success:
!insertmacro MUI_LANGDLL_DISPLAY
A lot of it is just guess work and trial and error. (btw - I also tried running through a loop to get all Administrators, but it seems the DLL was intended only for 32-bit machines, so...).
Anyway, my question is:
Does anybody know of a way (using "RunAs" or otherwise) to open a dialog requesting Username and password, check the credentials and continue with the installation only if they check out?
Also, I know there is a way to set up an installer so that it comes with that nice shield icon on it that lets users know that Admin permission will be requested. Does anybody know how to do that?
Any help would be very much appreciated, as this is the only thing currently preventing the deployment of my app.
Outfile RequireAdmin.exe
RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
!include LogicLib.nsh
Function .onInit
UserInfo::GetAccountType
pop $0
${If} $0 != "admin" ;Require admin rights on NT4+
MessageBox mb_iconstop "Administrator rights required!"
SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
Quit
${EndIf}
FunctionEnd
Page InstFiles
Section
SectionEnd
is the basic code I usually recommend to make sure the installer is running as an Administrator.
IMHO it does not make sense to prompt for credentials on a custom page unless only parts of the install process requires administrator access and the other part requires access to the users profile. If this applies to you then you should take a look at the UAC plug-in (It is a bit complicated to use and makes it impossible for your exe file to get the shield overlay icon)
I don't think the RunAs plug-in works correctly on Vista+ when UAC is on so trying to get it to work might be a dead end...
The recommended way to get the shield is to request elevation in the exe manifest, RequestExecutionLevel admin does that. If you don't use RequestExecutionLevel at all in your script your installer might be detected as a legacy installer and it will also get the shield overlay.
In Windows Vista, if an executable file requires elevation to launch,
then the executable's icon should be "stamped" with a shield icon to
indicate this fact. The executable's application manifest must mark
"requireAdministrator" to designate the executable as requiring a full
administrative access token. The shield icon overlay will also be
automatically placed on executables that are deemed to require
elevation as per the installer detection heuristics. For example, a
file named setup.exe will automatically receive a shield icon overlay
even if the executable does not have an embedded application manifest.

Resources