http://muldersoft.com/ | http://sourceforge.net/projects/muldersoft/ | http://nsis.sourceforge.net/Category:Plugins | Earth Heals Herself |
This plug-in provides access to a number of "standard" functions from the C Standard Library, which programmers are used to from C/C++ and other "high level" languages, but which are not normally available in NSIS. In order to keep the plug-in size as small as possible and for maximum compatibility, the Visual C++ Runtime v6.0 "MSVCRT.DLL" is used, which is an integral part of all versions of Windows (since Windows 2000). This means that the C++ Runtime neither needs to be shipped as a separate DLL nor does it need to be linked statically into the plug-in.
Many additional functions, not directly related to the C Standard Library, have been added over the years: For example, this plug-in provides a number convenience functions to deal with strings, such as trimming whitespaces or validating a given file name. There also are some functions to conveniently access the command-line parameters that have been passed to the installer. Furthermore, there is a wrapper for the SHFileOperation function, which can be used to copy or move files using the Windows Shell, as well as a function to efficiently append the contents of one file to another file. Moreover, the plug-in provides a method for launching programs in a non-elevated way (aka "user mode") from an installer that is running in elevated context (aka "admin mode"). In addition to that, there is a set of functions that can be used to detect the real Windows version that the installer is running on, which still work correctly/reliably on Windows 8.1 (and later) where Microsoft has broken the GetVersionEx() system function. And, as if this wasn't enough, the plug-in can compute the cryptographic hash of a given file or text message, using various state-of-the-art hash functions, including SHA-{1,2,3}. Last but not least, the plug-in provides a variant of ExecShell with "wait for process termination" feature, based on ShellExecuteEx, as well as a function for invoking "shell verbs" – useful for programmatically pinning shortcuts to the Taskbar.
Overall I use this plug-in as my "Swiss Army Knife" for all the small things I needed in my NSIS-based installers but that NSIS didn't provide out-of-the-box. ANSI and Unicode builds are provided. Supports all Windows versions, starting with Windows XP.
The following functions are provided by the StdUtils plug-in:
!define StdUtils.Time #time(), as in C standard library !define StdUtils.GetMinutes #GetSystemTimeAsFileTime(), returns the number of minutes !define StdUtils.GetHours #GetSystemTimeAsFileTime(), returns the number of hours !define StdUtils.GetDays #GetSystemTimeAsFileTime(), returns the number of days !define StdUtils.Rand #rand(), as in C standard library !define StdUtils.RandMax #rand(), as in C standard library, with maximum value !define StdUtils.RandMinMax #rand(), as in C standard library, with minimum/maximum value !define StdUtils.RandList #rand(), as in C standard library, with list support !define StdUtils.FormatStr #sprintf(), as in C standard library, one '%d' placeholder !define StdUtils.FormatStr2 #sprintf(), as in C standard library, two '%d' placeholders !define StdUtils.FormatStr3 #sprintf(), as in C standard library, three '%d' placeholders !define StdUtils.ScanStr #sscanf(), as in C standard library, one '%d' placeholder !define StdUtils.ScanStr2 #sscanf(), as in C standard library, two '%d' placeholders !define StdUtils.ScanStr3 #sscanf(), as in C standard library, three '%d' placeholders !define StdUtils.TrimStr #Remove whitspaces from string, left and right !define StdUtils.TrimStrLeft #Remove whitspaces from string, left side only !define StdUtils.TrimStrRight #Remove whitspaces from string, right side only !define StdUtils.RevStr #Reverse a string, e.g. "reverse me" <-> "em esrever" !define StdUtils.ValidFileName #Test whether string is a valid file name - no paths allowed !define StdUtils.ValidPathSpec #Test whether string is a valid full(!) path specification !define StdUtils.SHFileMove #SHFileOperation(), using the FO_MOVE operation !define StdUtils.SHFileCopy #SHFileOperation(), using the FO_COPY operation !define StdUtils.AppendToFile #Append contents of an existing file to another file !define StdUtils.ExecShellAsUser #ShellExecute() as NON-elevated user from elevated installer !define StdUtils.InvokeShellVerb #Invokes a "shell verb", e.g. for pinning items to the taskbar !define StdUtils.ExecShellWaitEx #ShellExecuteEx(), returns the handle of the new process !define StdUtils.WaitForProcEx #WaitForSingleObject(), e.g. to wait for a running process !define StdUtils.GetParameter #Get the value of a specific command-line option !define StdUtils.TestParameter #Test whether a specific command-line option has been set !define StdUtils.ParameterCnt #Get number of command-line tokens, similar to argc in main() !define StdUtils.ParameterStr #Get the n-th command-line token, similar to argv[i] in main() !define StdUtils.GetAllParameters #Get complete command-line, but without executable name !define StdUtils.GetRealOSVersion #Get the *real* Windows version number, even on Windows 8.1+ !define StdUtils.GetRealOSBuildNo #Get the *real* Windows build number, even on Windows 8.1+ !define StdUtils.GetRealOSName #Get the *real* Windows version, as a "friendly" name !define StdUtils.GetOSEdition #Get the Windows edition, i.e. "workstation" or "server" !define StdUtils.VerifyOSVersion #Compare *real* operating system to an expected version number !define StdUtils.VerifyOSBuildNo #Compare *real* operating system to an expected build number !define StdUtils.HashText #Compute hash from text string (CRC32, MD5, SHA1/2/3, BLAKE2) !define StdUtils.HashFile #Compute hash from file (CRC32, MD5, SHA1/2/3, BLAKE2) !define StdUtils.TimerCreate #Create a new event-timer that will be triggered periodically !define StdUtils.TimerDestroy #Destroy a running timer created with TimerCreate() !define StdUtils.GetLibVersion #Get the current StdUtils library version (for debugging) !define StdUtils.SetVerbose #Enable or disable "verbose" mode (for debugging)
Please see the descriptions below for details on the individual functions!
Depending on whether you are using the Unicode or the ANSI (non-Unicode) variant of NSIS, you must copy either Plugins\Release_Unicode\StdUtils.dll or Plugins\Release_ANSI\StdUtils.dll into the Plugins sub-directory inside your NSIS installation. Using the Unicode version is highly recommended these days! Also, in both cases, you must copy Include\StdUtils.nsh into the Include sub-directory inside your NSIS installation. Please note that NSIS v2.x does not officially support Unicode, but you can (and should!) use Unicode NSIS. Unicode support will officially be added to NSIS v3.x, which currently is still in development stage. Therefore, the Unicode version of this plug-in has been developed and tested with Unicode NSIS v2.x, not with NSIS v3.x. Support for NSIS v3.x is going to be added as soon as it will be released.
Hint: The release package now also contains a "tiny" version of this plug-in. That version has about 1/3 the size of the "normal" (full) version, but lacks support for hash computation. The "tiny" version is provided as Unicode build only!
In order to use the StdUtils plug-in in your script, simply include StdUtils.nsh and then use the pre-defined ${StdUtils.FunctionName} macros like this:
!include 'StdUtils.nsh' Section ${StdUtils.Rand} $1 DetailPrint "Random number obtained via StdUtils::Rand is: $1" SectionEnd
Note: We highly recommend to not call the plug-in functions directly. Instead, use the pre-defind macros, which ensures that the plug-in functions are used in the "proper" way.
For more details, please have a look at the example scripts located in the StdUtils\Examples\StdUtils directory!
In this chapter the individual functions provided by the StdUtils plug-in are described in detail.
${StdUtils.Time} user_var(output)
Returns the number of seconds that have elapsed since 00:00, Jan 1, 1970 (UTC), also known as "Unix time", just like the time() function:
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.Time} $1 DetailPrint "Time: $1" Sleep 500 ${StdUtils.Time} $1 DetailPrint "Time: $1" Sleep 500 ${StdUtils.Time} $1 DetailPrint "Time: $1" SectionEnd
${StdUtils.GetMinutes} user_var(output)
${StdUtils.GetHours} user_var(output)
${StdUtils.GetDays} user_var(output)
Retrieves the current system date and time, using the GetSystemTimeAsFileTime() function. Returns the number of minutes, hours or days since 00:00, January 1, 1601 (UTC).
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.GetMinutes} $1 DetailPrint "UTC time in minutes: $1" ${StdUtils.GetHours} $1 DetailPrint "UTC time in hours: $1" ${StdUtils.GetDays} $1 DetailPrint "UTC time in days: $1" SectionEnd
${StdUtils.Rand} user_var(output)
${StdUtils.RandMax} user_var(output) max
${StdUtils.RandMinMax} user_var(output) min max
Returns a pseudo-random integral number, similar to the rand() function, but without the need to call srand(). Optionally the minimum and/or maximum value can be specified, so a random number in the min to max range will be returned. If no minimum is specified, the minimum defaults to zero. And if no maximum is specified, the maximum defaults to INT_MAX. Note that this function will use RtlGenRandom(), where possible; otherwise it falls back to a method based on rand(). In the latter case, srand() will be initialized with a suitable seed automatically.
Section ${StdUtils.Rand} $1 DetailPrint "Random: $1" ${StdUtils.Rand} $1 DetailPrint "Random: $1" ${StdUtils.Rand} $1 DetailPrint "Random: $1" ${StdUtils.Rand} $1 DetailPrint "Random: $1" ${StdUtils.Rand} $1 DetailPrint "Random: $1" ${StdUtils.Rand} $1 DetailPrint "Random: $1" SectionEnd Section ${StdUtils.RandMax} $1 42 DetailPrint "Random Max: $1" ${StdUtils.RandMax} $1 42 DetailPrint "Random Max: $1" ${StdUtils.RandMax} $1 42 DetailPrint "Random Max: $1" ${StdUtils.RandMax} $1 42 DetailPrint "Random Max: $1" ${StdUtils.RandMax} $1 42 DetailPrint "Random Max: $1" ${StdUtils.RandMax} $1 42 DetailPrint "Random Max: $1" SectionEnd Section ${StdUtils.RandMinMax} $1 -4 -2 DetailPrint "Random Min/Max: $1" ${StdUtils.RandMinMax} $1 -4 -2 DetailPrint "Random Min/Max: $1" ${StdUtils.RandMinMax} $1 -4 -2 DetailPrint "Random Min/Max: $1" ${StdUtils.RandMinMax} $1 -4 -2 DetailPrint "Random Min/Max: $1" ${StdUtils.RandMinMax} $1 -4 -2 DetailPrint "Random Min/Max: $1" ${StdUtils.RandMinMax} $1 20 21 DetailPrint "Random Min/Max: $1" SectionEnd
${StdUtils.RandList} count max
Pushes a list of pseudo-random numbers onto the stack. The string "EOL" is pushed beforehand and thus will indicate the end of the list when popping the numbers off the stack. The count of the random numbers and the max value can be specified; the minimum value is zero.
Section ${StdUtils.RandList} 50 100 Pop $1 StrCmp $1 EOL +3 DetailPrint "RandList: $1" Goto -3 SectionEnd
${StdUtils.FormatStr} user_var(output) format_str val1
${StdUtils.FormatStr2} user_var(output) format_str val1 val2
${StdUtils.FormatStr3} user_var(output) format_str val1 val2 val3
Returns a formatted string, similar to the sprintf() function. Only the %d placeholder is currently supported. There are versions for one, two and three placeholders:
Section ${StdUtils.FormatStr} $1 "Hello World is %05d woha!" 89 DetailPrint "FormatStr: $1" ${StdUtils.FormatStr2} $1 "Hello World is %05d and %05d woha!" 89 384 DetailPrint "FormatStr: $1" ${StdUtils.FormatStr3} $1 "Hello World is %05d and %05d or even %05d woha!" 89 384 2384 DetailPrint "FormatStr: $1" ${StdUtils.FormatStr} $1 "Hello World is %09000d." 89 DetailPrint "FormatStr: $1" SectionEnd
${StdUtils.ScanStr} user_var(output) format_str input default
${StdUtils.ScanStr2} user_var(output1) user_var(output2) format_str input default1 default2
${StdUtils.ScanStr3} user_var(output1) user_var(output2) user_var(output3) format_str input default1 default2 default3
Parses input from a string according to a format specification similar to the sscanf() function. Only the %d placeholder is currently supported. There are versions for one, two and three placeholders:
Section ${StdUtils.ScanStr} $0 "Der Test sagt %d ist toll!" "Der Test sagt 571 ist toll!" 42 DetailPrint "ScanStr: $0" ${StdUtils.ScanStr} $0 "Der Hund sagt %d ist toll!" "Der Test sagt 571 ist toll!" 42 DetailPrint "ScanStr: $0" SectionEnd Section ${StdUtils.ScanStr2} $0 $1 "Der Test sagt %d sowie %d ist toll!" "Der Test sagt 571 sowie 831 ist toll!" 42 43 DetailPrint "ScanStr2: $0, $1" ${StdUtils.ScanStr2} $0 $1 "Der Test sagt %d sowie %d ist toll!" "Der Test sagt 571 horch 831 ist toll!" 42 43 DetailPrint "ScanStr2: $0, $1" ${StdUtils.ScanStr2} $0 $1 "Der Test sagt %d sowie %d ist toll!" "Der Hund sagt 571 horch 831 ist toll!" 42 43 DetailPrint "ScanStr2: $0, $1" SectionEnd Section ${StdUtils.ScanStr3} $0 $1 $2 "Der Test sagt %d sowie %d ist toll! Und %d." "Der Test sagt 571 sowie 831 ist toll! Und 325" 42 43 44 DetailPrint "ScanStr3: $0, $1, $2" ${StdUtils.ScanStr3} $0 $1 $2 "Der Test sagt %d sowie %d ist toll! Und %d." "Der Test sagt 571 sowie 831 ist toll! OMG 325" 42 43 44 DetailPrint "ScanStr3: $0, $1, $2" ${StdUtils.ScanStr3} $0 $1 $2 "Der Test sagt %d sowie %d ist toll! Und %d." "Der Test sagt 571 horch 831 ist toll! OMG 325" 42 43 44 DetailPrint "ScanStr3: $0, $1, $2" ${StdUtils.ScanStr3} $0 $1 $2 "Der Test sagt %d sowie %d ist toll! Und %d." "Der Hund sagt 571 horch 831 ist toll! OMG 325" 42 43 44 DetailPrint "ScanStr3: $0, $1, $2" SectionEnd
${StdUtils.TrimStr} user_var(input/output)
${StdUtils.TrimStrLeft} user_var(input/output)
${StdUtils.TrimStrRight} user_var(input/output)
Trims a string by removing all leading and/or trailing whitespace characters. A characters is considered to be a "whitespace" character by this function, if (and only if) it is accepted by either iscntrl() or isspace(). The function operates in-place.
Section StrCpy $1 " Some Text " DetailPrint "String: '$1'" StrCpy $0 $1 ${StdUtils.TrimStr} $0 DetailPrint "TrimStr: '$0'" StrCpy $0 $1 ${StdUtils.TrimStrLeft} $0 DetailPrint "TrimStrLeft: '$0'" StrCpy $0 $1 ${StdUtils.TrimStrRight} $0 DetailPrint "TrimStrRight: '$0'" StrCpy $1 "Some Text" DetailPrint "String: '$1'" StrCpy $0 $1 ${StdUtils.TrimStr} $0 DetailPrint "TrimStr: '$0'" StrCpy $0 $1 ${StdUtils.TrimStrLeft} $0 DetailPrint "TrimStrLeft: '$0'" StrCpy $0 $1 ${StdUtils.TrimStrRight} $0 DetailPrint "TrimStrRight: '$0'" StrCpy $1 "" DetailPrint "String: '$1'" StrCpy $0 $1 ${StdUtils.TrimStr} $0 DetailPrint "TrimStr: '$0'" StrCpy $0 $1 ${StdUtils.TrimStrLeft} $0 DetailPrint "TrimStrLeft: '$0'" StrCpy $0 $1 ${StdUtils.TrimStrRight} $0 DetailPrint "TrimStrRight: '$0'" StrCpy $1 " " DetailPrint "String: '$1'" StrCpy $0 $1 ${StdUtils.TrimStr} $0 DetailPrint "TrimStr: '$0'" StrCpy $0 $1 ${StdUtils.TrimStrLeft} $0 DetailPrint "TrimStrLeft: '$0'" StrCpy $0 $1 ${StdUtils.TrimStrRight} $0 DetailPrint "TrimStrRight: '$0'" StrCpy $1 "$\tFoobar$\r$\n" DetailPrint "String: '$1'" StrCpy $0 $1 ${StdUtils.TrimStr} $0 DetailPrint "TrimStr: '$0'" StrCpy $0 $1 ${StdUtils.TrimStrLeft} $0 DetailPrint "TrimStrLeft: '$0'" StrCpy $0 $1 ${StdUtils.TrimStrRight} $0 DetailPrint "TrimStrRight: '$0'" SectionEnd
${StdUtils.RevStr} user_var(input/output)
Reverses the character order of a specified string in-place. For example, it converts "reverse me" to "em esrever", or vice versa.
Section StrCpy $0 "ABC" DetailPrint "String: $0" ${StdUtils.RevStr} $0 DetailPrint "RevStr: $0" StrCpy $0 "ABCD" DetailPrint "String: $0" ${StdUtils.RevStr} $0 DetailPrint "RevStr: $0" StrCpy $0 "Just a very long text with no specific meaning at all!" DetailPrint "String: $0" ${StdUtils.RevStr} $0 DetailPrint "RevStr: $0" SectionEnd
${StdUtils.ValidFileName} user_var(output) input
${StdUtils.ValidPathSpec} user_var(output) input
The ${StdUtils.ValidFileName} function checks whether the given input is a valid file name, and the ${StdUtils.ValidPathSpec} function checks whether the given input is a valid fully-qualified path. Both functions return "ok", if the given string is valid, or "invalid" otherwise.
Note: File names must not contain any <>:"/\|?* characters or any characters accepted by iscntrl(). For paths the same limitations apply, except that a path may contain / and \ characters. Additionally, the first character in a path must be accepted by isalpha() and the second character in a path must be a : character. However, the : character must not appear at any other location in the path. Furthermore, the last character in a file name or path must not be a . or whitespace character. Last but not least, an empty string is never accepted as a valid file name or path.
Section ${StdUtils.ValidFileName} $0 "My Document.txt" DetailPrint 'ValidFileName("My Document.txt") = $0' ${StdUtils.ValidFileName} $0 "Is this a valid name?" DetailPrint 'ValidFileName("Is this a valid name?") = $0' ${StdUtils.ValidPathSpec} $0 "C:\Folder\File.foo" DetailPrint 'ValidPathSpec("C:\Folder\File.foo") = $0' SectionEnd
${StdUtils.SHFileMove} user_var(output) from to hwnd
${StdUtils.SHFileCopy} user_var(output) from to hwnd
Copies or moves a file system object (e.g. a file or a complete folder) from path from to path to, by using the SHFileOperation() function. The function requires a window handle hwnd and usually the NSIS variable $HWNDPARENT is used for this purpose.
Section InitPluginsDir SetOutPath "$PLUGINSDIR\TestDirA" File "${NSISDIR}\Contrib\Graphics\Checks\*.*" SetOutPath "$PLUGINSDIR\TestDirA\SubDir" File "${NSISDIR}\Contrib\Graphics\Header\*.*" CreateDirectory "$PLUGINSDIR\SubDirX" CreateDirectory "$PLUGINSDIR\SubDirY" ${StdUtils.SHFileCopy} $0 "$PLUGINSDIR\TestDirA" "$PLUGINSDIR\SubDirX\TestDirB" $HWNDPARENT DetailPrint "SHFileCopy: $0" ${StdUtils.SHFileMove} $0 "$PLUGINSDIR\TestDirA" "$PLUGINSDIR\SubDirY\TestDirC" $HWNDPARENT DetailPrint "SHFileMove: $0" ExecShell "explore" "$PLUGINSDIR" SectionEnd Section MessageBox MB_ICONINFORMATION "The next three operations are going to fail!$\nBut only one will be verbose..." ${StdUtils.SHFileCopy} $0 "$PLUGINSDIR\TestDirXYZ" "$PLUGINSDIR\SubDirX\TestDirZ" $HWNDPARENT DetailPrint "SHFileCopy: $0" ${StdUtils.SetVerbose} 1 ${StdUtils.SHFileCopy} $0 "$PLUGINSDIR\TestDirXYZ" "$PLUGINSDIR\SubDirX\TestDirZ" $HWNDPARENT DetailPrint "SHFileCopy: $0" ${StdUtils.SetVerbose} 0 ${StdUtils.SHFileCopy} $0 "$PLUGINSDIR\TestDirXYZ" "$PLUGINSDIR\SubDirX\TestDirZ" $HWNDPARENT DetailPrint "SHFileCopy: $0" SectionEnd
${StdUtils.AppendToFile} user_var(output) from dest offset maxlen
Appends the contents of the existing source file specified by from to the output file specified by dest. If the output file does not exist yet, it will be created; otherwise the data is appended to the back-end of the existing file. Furthermore, offset specifies the number of bytes to be skipped in the source file, before the copying begins. If the offset is equal to or greater than the size of the source file, then no data is copied, which is not considered an error. Specify an offset of 0 in order to start copying right at the beginning of the source file. Finally, maxlen specifies the maximum number of bytes to be copied. Copying will stop when either the end of the source file is reached or when the specified maximum number of bytes have been copied. Set maxlen to 0, if you want to copy the source file all the way to the end. If the function succeeds, it will return the number of bytes that have actually been copied. Note, however, that if more than MAX_INT (2,147,483,647) bytes have been copied, the return value will still be at most MAX_INT. If anything went wrong, e.g. a file could not be opened or a read/write operation failed, "error" is returned.
Note: This function is implemented via native Win32 file I/O functions. It also uses a sufficiently large buffer (currently 8 KB) to speed up the copying process. This should be a lot faster than copying the data "byte by byte", using the built-in FileReadByte and FileWriteByte functions.
Section ${StdUtils.AppendToFile} $0 "$EXEDIR\SourceFile1.bin" "$OUTDIR\OutputFile.bin" 0 0 DetailPrint "AppendToFile: $0" ${StdUtils.AppendToFile} $0 "$EXEDIR\SourceFile2.bin" "$OUTDIR\OutputFile.bin" 0 0 DetailPrint "AppendToFile: $0" SectionEnd
${StdUtils.ExecShellAsUser} user_var(output) file verb args
The ${StdUtils.ExecShellAsUser} function allows for launching a child process with normal user privileges (user level), directly from an elevated installer instance (admin level). This is in contrast to the built-in Exec, ExecWait and ExecShell instructions, which all cause the child process to be elevated too. Consequently, the ${StdUtils.ExecShellAsUser} function provides a simple and more lightweight alternative to the UAC plug-in. The function expects three arguments: The path to the file to be executed, the verb that shall be used to execute the file (e.g. "open") and the argument string args to be passed to the new process. The last two arguments are optional and can be specified as empty strings (""). If the function succeeded, then it returns either "ok" or "fallback". Otherwise it returns either "einval", "timeout", "not_found" or "error".
Please note that "einval" indicates that the function was called with invalid parameters, "not_found" indicates that the specified file doesn't exist, and "timeout" indicates that the function encountered a deadlock. Furthermore, note that "ok" indicates that the process was created using the IShellDispatch2 COM interface, which is the default behaviour on modern systems (allows the new process to not be elevated), while "fallback" indicates that the normal ShellExecute() method was used, which is the expected behaviour on legacy systems without UAC support.
!include 'StdUtils.nsh'
; make sure the installer will get elevated rights on UAC-enabled system (Vista+)
RequestExecutionLevel admin
ShowInstDetails show
Section
DetailPrint 'ExecShell: "$SYSDIR\mspaint.exe"'
; this instance of MS Paint will be *elevated*, just like the installer!
ExecShell "open" "$SYSDIR\mspaint.exe"
MessageBox MB_TOPMOST "Close Paint and click 'OK' to continue..."
SectionEnd
Section
DetailPrint 'ExecShellAsUser: "$SYSDIR\mspaint.exe"'
Sleep 1000
; now launch a *non-elevated* instance of MS Paint by using ExecShellAsUser
${StdUtils.ExecShellAsUser} $0 "$SYSDIR\mspaint.exe" "open" ""
; expected result is "ok" on UAC-enabled systems or "fallback" otherwise
DetailPrint "Result: $0"
SectionEnd
${StdUtils.ExecShellWaitEx} user_var(output_1) user_var(output_2) file verb args
${StdUtils.WaitForProcEx} user_var(output) handle
The ${StdUtils.ExecShellWaitEx} function works like the built-in ExecShell command, except that you can wait for the process to terminate. The function expects three arguments: The path to the file to be executed, the verb that shall be used to execute the file (e.g. "open") and the arguments to be passed to the new process. The last two arguments are optional and can be specified as empty strings (""). Furthermore, the function returns two values: The first value is either "ok", "no_wait" or "error", while the second value provides additional info. "ok" indicates that the process was created successfully and can be waited for, "no_wait" indicates that we cannot wait for the process (because ShellExecuteEx did not create a new process, but passed the file/URL to a running instance) and "error" indicates that something went wrong.
If the first return value is "ok", the second return value contains the handle of the new process. If the first return value is "error", the second return value contains the Win32 error code. And if the first return value is "no_wait", the second return value is zero. Only if "ok" and a process handle were returned, you can call ${StdUtils.WaitForProcEx} in order to wait until the process has terminated. This means that you must always carefully check the first return value of ${StdUtils.ExecShellWaitEx} before you pass the second return value to ${StdUtils.WaitForProcEx}. The behavior of ${StdUtils.WaitForProcEx} is undefined if you pass something that isn't a valid process handle! If successfull, ${StdUtils.WaitForProcEx} returns the exit code of the process after it has terminated. The function returns "error" if something went wrong.
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section DetailPrint 'ExecShellWait: "$SYSDIR\mspaint.exe"' Sleep 1000 ${StdUtils.ExecShellWaitEx} $0 $1 "$SYSDIR\mspaint.exe" "open" "" ;try to launch the process DetailPrint "Result: $0 -> $1" ;returns "ok", "no_wait" or "error". StrCmp $0 "error" ExecFailed ;check if process failed to create StrCmp $0 "no_wait" WaitNotPossible ;check if process can be waited for - always check this! StrCmp $0 "ok" WaitForProc ;make sure process was created successfully Abort WaitForProc: DetailPrint "Waiting for process. ZZZzzzZZZzzz..." ${StdUtils.WaitForProcEx} $2 $1 DetailPrint "Process just terminated (exit code: $2)" Goto WaitDone ExecFailed: DetailPrint "Failed to create process (error code: $1)" Goto WaitDone WaitNotPossible: DetailPrint "Can not wait for process." Goto WaitDone WaitDone: SectionEnd
${StdUtils.TestParameter} user_var(output) name
${StdUtils.GetParameter} user_var(output) name default
With the ${StdUtils.TestParameter} function you can check for the presence of the command-line parameter specified by name. If that parameter is present on the command-line, the function returns true, otherwise it returns false. Additionally, the ${StdUtils.GetParameter} function can be used to get the value of the command-line parameter specified by name. If that parameter is present on the command-line, then the function returns the corresponding value. This might be an empty string! If the parameter is not present, then the default value is returned.
Hint: If the same parameter appears on the command-line multiple times, only the first occurrence will be returned. If the parameter value is too long to fit into an NSIS string, it will be truncated as needed. In any case, the parameter value will automatically be trimmed by this function.
Parameters are passed to the installer using the following syntax:
* Using this syntax is not recommended, but it is supported anyway, for consistency with the way that Visual C++ handles command-line arguments!
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.TestParameter} $R0 "Foobar" StrCmp "$R0" "true" 0 +3 DetailPrint 'Command-line parameter /Foobar is specified!' Goto +2 DetailPrint 'Command-line parameter /Foobar is *not* specified!' SectionEnd Section ${StdUtils.GetParameter} $R0 "Foobar" "<MyDefault>" DetailPrint 'Value of command-line parameter /Foobar is: "$R0"' SectionEnd
${StdUtils.ParameterCnt} user_var(output)
${StdUtils.ParameterStr} user_var(output) index
The ${StdUtils.ParameterCnt} function returns then number of command-line tokens. This is equivalent to the argc parameter passed to the main() function. Note that this value may be zero, in which case no command-line tokens are available. If something went wrong, "error" is returned. Accordingly, the ${StdUtils.ParameterStr} function returns the n-th command-line token. This is equivalent to the n-th element of the argv array passed to the main() function. Note that the token may be an empty string. Also note that this function uses zero-based indexing. Thus, an index of 0 returns the first token, an index of 1 returns the second token, and so on. The index must be in the [0,N) range (i.e. N not included), where N is the value returned by ${StdUtils.ParameterCnt}. If something went wrong, e.g. index is out of range, the function returns "error".
Hint: In contrast to the main() function, where the first token (index = 0) always contains the executable name, the ${StdUtils.ParameterCnt} and ${StdUtils.ParameterStr} functions will omit the executable name. Simply use the built-in $EXEFILE or $EXEPATH constants instead!
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section StrCpy $R0 0 #Init counter to zero ${StdUtils.ParameterCnt} $R1 #Get number of command-line tokens IntCmp $R1 0 0 0 LoopNext #Any tokens available? DetailPrint 'No command-line tokens!' #Print some info Goto LoopExit #Exit LoopNext: ${StdUtils.ParameterStr} $R2 $R0 #Read next command-line token DetailPrint 'Command-line token #$R0 is "$R2"' #Print command-line token IntOp $R0 $R0 + 1 #counter += 1 IntCmp $R0 $R1 0 LoopNext #Loop while more tokens available LoopExit: SectionEnd
${StdUtils.GetAllParameters} user_var(output) truncate
Furthermore you can use ${StdUtils.GetAllParameters} to get the complete command-line string, but without the executable name. This is useful, for example, to forward all command-line parameters to an embedded installer. The truncate parameters controls the behavior of this function, if the command-line is too long to fit into an NSIS string. With truncate set to 1, the command-line will be truncated to a length of NSIS_MAX_STRLEN characters. With truncate set to 0, the function returns "too_long", if the command-line doesn't fit into an NSIS string.
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.GetAllParameters} $R0 0 DetailPrint "Complete command-line: '$R0'" ${StdUtils.GetAllParameters} $R0 1 DetailPrint "Truncated command-line: '$R0'" SectionEnd
${StdUtils.InvokeShellVerb} user_var(output) path file verb_id
The ${StdUtils.InvokeShellVerb} function can be used to invoke a so-called "Shell Verb" on an arbitary item. The most common use for this is (un)pinning an item to/from the Taskbar or the Startmenu, on Windows 7 and later. The function expects three arguments: The directory where the item (e.g. executable file or shortcut) is located, the name of the item, and the id of the shell verb to be invoked. We need the id of the verb, because the verb's name (string), which is used to select the desired verb from the list of available verbs, is language-specific; it depends on the Windows system language. However, we certainly do not want to make our installer specific to a certain system language. Resource id's, on the other hand, are language-independant. By using resource id's, we can load the actual verb name (string) at runtime. Note that while this function works on Windows 7 (and later) only, it still is perfectly safe to call on older versions of Windows. If the function succeeded, then it returns "ok"; if the function is called with invalid parameters, then it returns "einval"; if the requested verb could not be found, if the request verb is unavailable for the specified item, or if the specified item does not exist, then "not_found" will be returned; if this function is used on Windows versions prior to Windows 7 (e.g. Vista or XP), then it will return "unsupported"; and if the function failed for another reason, then it will return "error".
Supported shell verbs IDs are:
Hint: If you are getting the "not_found" error for a verb that is supposed to exists, then it's probably because the desired action isn't currently available for the item (e.g. it could be that you are trying to pin an item that already is pinned).
!include 'StdUtils.nsh' RequestExecutionLevel user ;no elevation needed for this test ShowInstDetails show Section IfFileExists "$SYSDIR\mspaint.exe" +3 MessageBox MB_ICONSTOP 'File does not exist:$\n"$SYSDIR\mspaint.exe"$\n$\nExample cannot run!' Quit SectionEnd Section DetailPrint "Going to pin MSPaint..." ${StdUtils.InvokeShellVerb} $0 "$SYSDIR" "mspaint.exe" ${StdUtils.Const.ShellVerb.PinToTaskbar} DetailPrint "Result: $0" StrCmp "$0" "ok" 0 +3 MessageBox MB_TOPMOST "Paint should have been pinned to Taskbar now!" Goto +2 MessageBox MB_TOPMOST "Failed to pin, see log for details!" SectionEnd Section DetailPrint "Going to un-pin MSPaint..." ${StdUtils.InvokeShellVerb} $0 "$SYSDIR" "mspaint.exe" ${StdUtils.Const.ShellVerb.UnpinFromTaskbar} DetailPrint "Result: $0" StrCmp "$0" "ok" 0 +3 MessageBox MB_TOPMOST "Paint should have been un-pinned from Taskbar now!" Goto +2 MessageBox MB_TOPMOST "Failed to un-pin, see log for details!" SectionEnd
${StdUtils.GetRealOSVersion} user_var(out_major) user_var(out_minor) user_var(out_spack)
The ${StdUtils.GetRealOSVersion} function returns the real Windows NT version installed on the computer. Note that starting with Windows 8.1 (Windows NT 6.3), Microsoft has broken the GetVersion() and GetVersionEx() functions! These function will now return the Windows version that the calling application has been compiled for (as indicated by the program's Compat Manifest), not the actual Windows version that we are running on! So these functions are effectively a NOP now – you don't need to call the Win32 API to determine which Windows version your own program has been compiled for, as you already know it. This has the consequence that Windows 8.1 and Windows 10 will now identify themselves as Windows 8.0, unless the calling application has been specifically compiled for Windows 8.1 or 10. Bummer!
Workarounds to "reveal" the actual Windows version exist. For example, one may try guestimating the real Windows version from certain Registry entries or from the file versions of certain system libraries. But, while these methods may seem to work in most cases, they still are dubious. It is also possible to obtain the real Windows version via WMI (Windows Management Instrumentation), but this is complex and requires a lot of COM hackery. At the same time, the provided ${StdUtils.GetRealOSVersion} function manages to reveal the actual Windows version strictly by using "official" Win32 API calls. Also, this function works regardless of the application Manifest, i.e. it will continue to work correctly in future Windows versions! Finally, this function still returns the correct Windows version, when the installer runs in "compatibility mode".
The function will return the major and minor Windows NT version (e.g. "6.3" on Windows 8.1, or "10.0" on Windows 10) plus the corresponding Service Pack version spack. It returns "error", if something went wrong.
Note: This function uses an iterative approach: It first calls GetVersionEx() to get the "fake" Windows version. Then it tries to refine the Windows version, step by step, using the VerifyVersionInfo() function - until the "real" version has been revealed. Furthermore, because the VerifyVersionInfo() function has been broken too on Windows 10 (facepalm!), we now try to call the "native" functions RtlGetVersion() and RtlVerifyVersion() directly from NTDLL.DLL, instead of using the "crippled" counterparts from KERNEL32.DLL.
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.GetRealOSVersion} $1 $2 $3 DetailPrint "Real Windows NT Version: $1,$2 (Service Pack: $3)" SectionEnd
${StdUtils.GetRealOSBuildNo} user_var(out)
The ${StdUtils.GetRealOSBuildNo} function returns the real Windows NT build number installed on the computer. The function will return the Windows NT build number (e.g. "7600" on Windows 7 RTM). It returns "error", if something went wrong.
Note: This function uses the same algorithm as ${StdUtils.GetRealOSVersion} to determine the real Windows build number, so it will give the expected result on Windows 8.1 and later.
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.GetRealOSBuildNo} $1 DetailPrint "Real Windows NT Build No.: $1" SectionEnd
${StdUtils.GetRealOSName} user_var(out)
${StdUtils.GetRealOSName} is a convenience function that returns the installed Windows version as a friendly name string. Currently the return value can be "Windows NT 4.0", "Windows 2000", "Windows XP", "Windows XP (x64)", "Windows Vista", "Windows 7", "Windows 8", "Windows 8.1" or "Windows 10". If an unknown Windows version is encountered, e.g. some future version that is not yet supported, the function will return "unknown". And it will return "error", if something went wrong.
Note: This function uses the same algorithm as ${StdUtils.GetRealOSVersion} to determine the real Windows version. Subsequently, the real version will be converted to a friendly name, so it will give the expected result on Windows 8.1 and later.
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.GetRealOSName} $1 DetailPrint "Real Windows NT Friendly Name: $1" SectionEnd
${StdUtils.VerifyOSVersion} user_var(out) expected_major expected_minor expected_spack
${StdUtils.VerifyOSVersion} is a convenience function to compare the installed Windows version against some expected one. The expected Windows NT version (e.g. "6.2" for Windows 8.0) is specified by the expected_major, expected_minor and expected_spack parameters. The function returns "ok" when the installed Windows version matches the expected one exactly; it returns "older" when the installed version is older than expected; it returns "newer" when the installed version is newer than expected; and it retruns "error" if something went wrong.
Note: This function uses the same algorithm as ${StdUtils.GetRealOSVersion} to determine the real Windows version that will be compared to the expected version, so it will give the expected result on Windows 8.1 and later.
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.VerifyOSVersion} $1 5 1 0 DetailPrint "Check for Windows XP (RTM): $1" ${StdUtils.VerifyOSVersion} $1 5 1 3 DetailPrint "Check for Windows XP (SP3): $1" ${StdUtils.VerifyOSVersion} $1 6 1 0 DetailPrint "Check for Windows 7 (RTM): $1" ${StdUtils.VerifyOSVersion} $1 6 1 1 DetailPrint "Check for Windows 7 (SP1): $1" ${StdUtils.VerifyOSVersion} $1 6 2 0 DetailPrint "Check for Windows 8.0: $1" ${StdUtils.VerifyOSVersion} $1 6 3 0 DetailPrint "Check for Windows 8.1: $1" SectionEnd
${StdUtils.VerifyOSBuildNo} user_var(out) expected_build
${StdUtils.VerifyOSBuildNo} is a convenience function to compare the installed Windows version against some expected version. The expected Windows NT build number is specified by expected_build (e.g. "7600" on Windows 7 RTM). The function will retrun "ok" when the installed Windows build matches the expected one exactly; it returns "older" when the installed build is older than the expected one; it returns "newer" when the installed version is newer than the expected one; and it retruns "error" if something went wrong.
Note: This function uses the same algorithm as ${StdUtils.GetRealOSVersion} to determine the real Windows build number that will be compared to the expected build number, so it will give the expected result on Windows 8.1 and later.
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.VerifyOSBuildNo} $1 2600 DetailPrint "Check for Build #2600, Windows XP: $1" ${StdUtils.VerifyOSBuildNo} $1 7600 DetailPrint "Check for Build #7600, Windows 7 (RTM): $1" ${StdUtils.VerifyOSBuildNo} $1 7601 DetailPrint "Check for Build #7601, Windows 7 (SP1): $1" ${StdUtils.VerifyOSBuildNo} $1 9600 DetailPrint "Check for Build #9600, Windows 8.1: $1" SectionEnd
${StdUtils.GetOSEdition} user_var(out)
${StdUtils.GetOSEdition} returns the Windows edition, i.e. either "workstation" or "server". It retruns "error" if something went wrong.
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.GetOSEdition} $1 DetailPrint "Windows Edition is: $1" SectionEnd
List of Windows NT versions:
Friendly Name | Server Edition | NT Version | Build No. |
Windows NT 4.0 | - | 4.0 | 1381 |
Windows 2000 | - | 5.0 | 2195 |
Windows XP | - | 5.1 | 2600 |
Windows XP (x64) | Windows Server 2003 | 5.2 | 3790 |
Windows Vista | Windows Server 2008 | 6.0 | 6000 - 6002 |
Windows 7 | Windows Server 2008 R2 | 6.1 | 7600 - 7601 |
Windows 8 | Windows Server 2012 | 6.2 | 9200 |
Windows 8.1 | Windows Server 2012 R2 | 6.3 | 9600 |
Windows 10 | Windows Server 2016 | 10.0 | 10240 |
${StdUtils.HashText} user_var(out) type text
The ${StdUtils.HashText} function computes the cryptographic hash of the message given by text, using the hash function specified by type. Currently CRC32, MD5, SHA-1, the SHA-2 family as well as the SHA-3 family are supported (using RHash implementation). See below for details! If the function succeeds, it will return the hash value, as a hexadecimal string. The length of the result depends on the chosen hash function. If an invalid hash function was specified, the function returns "invalid". And, if anything else went wrong, it returns "error".
Note: For improved consistency between the ANSI and Unicode installers, the Unicode version of this function will convert the given message to UTF-8 before hash computation!
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.HashText} $0 "SHA3-224" "The quick brown fox jumps over the lazy dog" DetailPrint 'SHA3-224("The quick brown fox jumps over the lazy dog") = "$0"' ${StdUtils.HashText} $0 "SHA3-256" "The quick brown fox jumps over the lazy dog" DetailPrint 'SHA3-256("The quick brown fox jumps over the lazy dog") = "$0"' ${StdUtils.HashText} $0 "SHA3-384" "The quick brown fox jumps over the lazy dog" DetailPrint 'SHA3-384("The quick brown fox jumps over the lazy dog") = "$0"' ${StdUtils.HashText} $0 "SHA3-512" "The quick brown fox jumps over the lazy dog" DetailPrint 'SHA3-512("The quick brown fox jumps over the lazy dog") = "$0"' SectionEnd
${StdUtils.HashFile} user_var(out) type path
The ${StdUtils.HashText} function computes the cryptographic hash of the contents of the file specified by path, using the hash function specified by type. Currently CRC32, MD5, SHA-1, the SHA-2 family as well as the SHA-3 family are supported (using RHash implementation). See below for details! If the function succeeds, it will return the hash value, as a hexadecimal string. The length of the result depends on the chosen hash function. If an invalid hash function was specified, the function returns "invalid". And, if anything else went wrong, it returns "error".
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Section ${StdUtils.HashFile} $0 "SHA3-224" "$WINDIR\Explorer.exe" DetailPrint 'SHA3-224($WINDIR\Explorer.exe) = "$0"' ${StdUtils.HashFile} $0 "SHA3-256" "$WINDIR\Explorer.exe" DetailPrint 'SHA3-256($WINDIR\Explorer.exe) = "$0"' ${StdUtils.HashFile} $0 "SHA3-384" "$WINDIR\Explorer.exe" DetailPrint 'SHA3-384($WINDIR\Explorer.exe) = "$0"' ${StdUtils.HashFile} $0 "SHA3-512" "$WINDIR\Explorer.exe" DetailPrint 'SHA3-512($WINDIR\Explorer.exe) = "$0"' SectionEnd
List of supported hash functions:
Hash Function | Hash Length in Bit (in Byte) | Algorithm Identifier |
CRC* | 32 (4) | CRC-32 |
MD5* | 128 (16) | MD5-128 |
SHA-1* | 160 (20) | SHA1-160 |
SHA-2 | 224 (28) | SHA2-224 |
256 (32) | SHA2-256 | |
384 (48) | SHA2-384 | |
512 (64) | SHA2-512 | |
SHA-3 | 224 (28) | SHA3-224 |
256 (32) | SHA3-256 | |
384 (48) | SHA3-384 | |
512 (64) | SHA3-512 | |
BLAKE2 | 224 (28) | BLAKE2-224 |
256 (32) | BLAKE2-256 | |
384 (48) | BLAKE2-384 | |
512 (64) | BLAKE2-512 |
* Please do not use these hash functions for security critical code nowadays, as they they have known collisions!
${StdUtils.TimerCreate} user_var(out) callback interval
${StdUtils.TimerDestroy} user_var(out) timer_id
The ${StdUtils.TimerCreate} function creates a new window timer, using the SetTimer API, which is going to periodically execute the NSIS function specified by callback. The interval (delay), in milliseconds, is specified by interval. If the timer was created successfully, the function returns a unique timer id. And, if anything went wrong, it returns "error". The ${StdUtils.TimerDestroy} function stops and destroys the timer specified by timer_id. Every timer that has been created successfully must be destroyed before the installer unloads the plug-in DLL!
Important: The SetTimer function works by appending WM_TIMER messages to the message queue of the thread which has created the timer. These messages will be dispatched to the window procedure, which eventually calls the callback function (TIMERPROC). Consequently, the thread, which creates the timer, must be running a message loop – otherwise the timer is never going to fire! For NSIS this means that the timer must be created (and destroyed) from the "main" GUI thread. You probably want to do this in the .onGUIInit and .onGUIEnd functions.
!include 'StdUtils.nsh' RequestExecutionLevel user ShowInstDetails show Var TimerId Var MyCount Function MyCallback IntOp $MyCount $MyCount + 1 DetailPrint "Timer event has been triggered! (#$MyCount)" FunctionEnd Function .onGUIInit ${StdUtils.TimerCreate} $TimerId MyCallback 1500 StrCmp $TimerId "error" 0 +2 MessageBox MB_ICONSTOP "Failed to create timer!" FunctionEnd Function .onGUIEnd StrCmp $TimerId "error" 0 +2 Return ${StdUtils.TimerDestroy} $0 $TimerId StrCmp $0 "ok" +2 MessageBox MB_ICONSTOP "Failed to destroy timer!" FunctionEnd Section DetailPrint "Hello, world!" SectionEnd
${StdUtils.GetLibVersion} user_var(out_ver) user_var(out_tst)
The ${StdUtils.GetLibVersion} function returns the version of the StdUtils library that is being used. The version string (in the "w.x.y.z" format) is returned in out_ver; the build time-stamp is returned in out_tst.
${StdUtils.SetVerbose} on
The ${StdUtils.SetVerbose} function enables or disables verbose error messages. Set on to '1' to enable verbose outputs or set it to '0' to disable verbose outputs. Verbose outputs are disabled by default. Do not enabled them for release versions of your installer!
The StdUtils plug-in for NSIS was created by LoRd_MuldeR <mulder2@gmx.de>. It is distribiuted under the GNU LGPL v2.1.
StdUtils plug-in for NSIS Copyright (C) 2004-2016 LoRd_MuldeR <mulder2@gmx.de> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
The author of the StdUtils plug-in Library for NSIS adds the following clarification to the GNU Lesser General Public License version 2.1: Installer programs (executables) created with NSIS (Nullsoft Scriptable Install System) that make use of the StdUtils plug-in Library (strictly through the NSIS plug-in interface) and that contain/distribute verbatim copies of the StdUtils plug-in Library are considered a "work that uses the Library"; they do not represent a derivative of the Library.
This plug-in has been inspired, partly, by the InvokeShellVerb plug-in, created by Robert Strong.
This plug-in has been inspired, partly, by the ShellExecAsUser plug-in, created by installer32.
Special thanks to Afrow UK for providing his excellent plug-ins (his code helped me to understand how to write NSIS plug-ins).
The following third-party code has been incorporated into StdUtils plug-in:
RHash
Copyright (c) 2005-2014 Aleksey Kravchenko <rhash.admin@gmail.com>
https://github.com/rhash/RHash
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so. The Software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk!
BLAKE2 reference source code package
Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
https://github.com/BLAKE2/BLAKE2
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
The source codes for the StdUtils plug-in are available from the code repository at:
You can download pre-compiled binaries (i.e. ready-to-use DLL files) of the plug-in from here:
For help and support, please use the "NSIS Discussion" sub-forum of the Winamp forums:
http://forums.winamp.com/forumdisplay.php?f=65
If you have any feature requests or bug reports, please submit them directly to our GitHub bug-tracker:
https://github.com/lordmulder/stdutils/issues
Also you may wish to check the official NSIS Wiki for the latest information and updates:
http://nsis.sourceforge.net/StdUtils_plug-in
http://muldersoft.com/ | http://sourceforge.net/projects/muldersoft/ | http://nsis.sourceforge.net/Category:Plugins | Earth Heals Herself |