StdUtils plug-in: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
No edit summary
Line 378: Line 378:
== ExecShellWait ==
== ExecShellWait ==


The ''${StdUtils.ExecShellWait}'' function works like the built-in ''ExecShell'' command, except that you can wait for the process to terminate. If the function failed, it returns "error". If the function succeeded, it will either return a process handle or "no_wait". The latter means that we cannot wait for the process, because ''ShellExecuteEx'' did '''not''' create a new process (but pass the file to a running instance). Only if a process handle was returned, you can call ${StdUtils.WaitForProc} in order to wait until the process has terminated.
The ''${StdUtils.ExecShellWait}'' function works like the built-in ''ExecShell'' command, except that you can wait for the process to terminate. If the function failed, it returns "error". If the function succeeded, it will either return a process handle or "no_wait". The latter means that we cannot wait for the process, because ''ShellExecuteEx'' did '''not''' create a new process (but pass the file/URL to a running instance). Only if a process handle was returned, you can call ''${StdUtils.WaitForProc}'' in order to wait until the process has terminated.


<highlight-nsis>!include 'StdUtils.nsh'
<highlight-nsis>!include 'StdUtils.nsh'

Revision as of 00:32, 9 October 2011

Author: Instructor (talk, contrib)


This plug-in provides access to a number of "standard" functions from C Standard Library, which programmers are used to from their C/C++ compilers (and other languages), but which are not available in NSIS by default. In order to keep the plug-in size as small as possible (~9 KB) and for maximum compatibility, the Visual C++ Run-Time v6.0 (MSVCRT.DLL), which is included with all versions of Windows (since Windows 2000), is used - instead of linking the Visual C++ Run-Time library into the plug-in DLL.

Additionally this plug-in provides wrappers for the SHFileOperation function. Moreover it provides a method for launching programs in a non-elevated way (user context) from an elevated installer (admin mode) on UAC-enabled systems - see this thread for details! Last but not least, a version of ExecShell with "wait for process termination" feature, based on ShellExecuteEx, is provided.

ANSI and Unicode builds available. Support operating systems: Windows 2000 and later.

Available Functions

!define StdUtils.Time            '!insertmacro _StdUtils_Time'         #time()
!define StdUtils.Rand            '!insertmacro _StdUtils_Rand'         #rand()
!define StdUtils.RandMax         '!insertmacro _StdUtils_RandMax'      #rand() with maximum
!define StdUtils.RandMinMax      '!insertmacro _StdUtils_RandMinMax'   #rand() with minimum/maximum
!define StdUtils.RandList        '!insertmacro _StdUtils_RandList'     #rand() with list support
!define StdUtils.FormatStr       '!insertmacro _StdUtils_FormatStr'    #sprintf() with one format tag (only %d supported!)
!define StdUtils.FormatStr2      '!insertmacro _StdUtils_FormatStr2'   #sprintf() with two format tags (only %d supported!)
!define StdUtils.FormatStr3      '!insertmacro _StdUtils_FormatStr3'   #sprintf() with three format tags (only %d supported!)
!define StdUtils.ScanStr         '!insertmacro _StdUtils_ScanStr'      #sscanf() with one format tag (only %d supported!)
!define StdUtils.ScanStr2        '!insertmacro _StdUtils_ScanStr2'     #sscanf() with two format tags (only %d supported!)
!define StdUtils.ScanStr3        '!insertmacro _StdUtils_ScanStr3'     #sscanf() with three format tags (only %d supported!)
!define StdUtils.TrimStr         '!insertmacro _StdUtils_TrimStr'      #Remove whitspaces from string (left and right)
!define StdUtils.TrimStrLeft     '!insertmacro _StdUtils_TrimStrLeft'  #Remove whitspaces from string (left side only)
!define StdUtils.TrimStrRight    '!insertmacro _StdUtils_TrimStrRight' #Remove whitspaces from string (right side only)
!define StdUtils.SHFileMove      '!insertmacro _StdUtils_SHFileMove'   #SHFileOperation with FO_MOVE
!define StdUtils.SHFileCopy      '!insertmacro _StdUtils_SHFileCopy'   #SHFileOperation with FO_COPY
!define StdUtils.ExecShellAsUser '!insertmacro _StdUtils_ExecShlUser'  #ShellExecute() with user context (for elevated installers)
!define StdUtils.ExecShellWait   '!insertmacro _StdUtils_ExecShlWait'  #ShellExecuteEx() with process handle to wait for
!define StdUtils.WaitForProc     '!insertmacro _StdUtils_WaitForProc'  #WaitForSingleObject() to wait for process termination
!define StdUtils.GetParameter    '!insertmacro _StdUtils_GetParameter' #Get the value of a specific commandline paramater
!define StdUtils.Unload          '!insertmacro _StdUtils_Unload'       #Unload DLL for proper clean-up (don't forget!)
!define StdUtils.SetVerbose      '!insertmacro _StdUtils_SetVerbose'   #Verbose mode (for debugging)
 
 
!macro _StdUtils_Time out
	StdUtils::Time /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_Rand out
	StdUtils::Rand /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_RandMax out max
	push ${max}
	StdUtils::RandMax /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_RandMinMax out min max
	push ${min}
	push ${max}
	StdUtils::RandMinMax /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_RandList count max
	push ${max}
	push ${count}
	StdUtils::RandList /NOUNLOAD
!macroend
 
!macro _StdUtils_FormatStr out format val
	push '${format}'
	push ${val}
	StdUtils::FormatStr /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_FormatStr2 out format val1 val2
	push '${format}'
	push ${val1}
	push ${val2}
	StdUtils::FormatStr2 /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_FormatStr3 out format val1 val2 val3
	push '${format}'
	push ${val1}
	push ${val2}
	push ${val3}
	StdUtils::FormatStr3 /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_ScanStr out format input default
	push '${format}'
	push '${input}'
	push ${default}
	StdUtils::ScanStr /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_ScanStr2 out1 out2 format input default1 default2
	push '${format}'
	push '${input}'
	push ${default1}
	push ${default2}
	StdUtils::ScanStr2 /NOUNLOAD
	pop ${out1}
	pop ${out2}
!macroend
 
!macro _StdUtils_ScanStr3 out1 out2 out3 format input default1 default2 default3
	push '${format}'
	push '${input}'
	push ${default1}
	push ${default2}
	push ${default3}
	StdUtils::ScanStr3 /NOUNLOAD
	pop ${out1}
	pop ${out2}
	pop ${out3}
!macroend
 
!macro _StdUtils_TrimStr var
	push ${var}
	StdUtils::TrimStr /NOUNLOAD
	pop ${var}
!macroend
 
!macro _StdUtils_TrimStrLeft var
	push ${var}
	StdUtils::TrimStrLeft /NOUNLOAD
	pop ${var}
!macroend
 
!macro _StdUtils_TrimStrRight var
	push ${var}
	StdUtils::TrimStrRight /NOUNLOAD
	pop ${var}
!macroend
 
!macro _StdUtils_SHFileMove out from to hwnd
	push '${from}'
	push '${to}'
	push ${hwnd}
	StdUtils::SHFileMove /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_SHFileCopy out from to hwnd
	push '${from}'
	push '${to}'
	push ${hwnd}
	StdUtils::SHFileCopy /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_ExecShlUser out file verb args
	push '${file}'
	push '${verb}'
	push '${args}'
	StdUtils::ExecShellAsUser /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_ExecShlWait out file verb args
	push '${file}'
	push '${verb}'
	push '${args}'
	StdUtils::ExecShellWait /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_WaitForProc handle
	push '${handle}'
	StdUtils::WaitForProc /NOUNLOAD
!macroend
 
!macro _StdUtils_GetParameter out name default
	push '${name}'
	push '${default}'
	StdUtils::GetParameter /NOUNLOAD
	pop ${out}
!macroend
 
!macro _StdUtils_Unload
	StdUtils::Unload
!macroend
 
!macro _StdUtils_SetVerbose on
	!if "${on}" != "0"
		StdUtils::EnableVerboseMode /NOUNLOAD
	!else
		StdUtils::DisableVerboseMode /NOUNLOAD
	!endif
!macroend

Example

This example tests miscellaneous functions of the StdUtils plug-in:

!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
 
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
 
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
 
Section
	${StdUtils.RandList} 50 100
	Pop $1
	StrCmp $1 EOL +3
	DetailPrint "RandList: $1"
	Goto -3
SectionEnd
 
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
 
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
	${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
 
Section
	StrCpy $1 "        Some Text            "
 
	StrCpy $0 $1
	DetailPrint "String: '$0'"
	${StdUtils.TrimStr} $0
	DetailPrint "TrimStr: '$0'"
 
	StrCpy $0 $1
	DetailPrint "String: '$0'"
	${StdUtils.TrimStrLeft} $0
	DetailPrint "TrimStrLeft: '$0'"
 
	StrCpy $0 $1
	DetailPrint "String: '$0'"
	${StdUtils.TrimStrRight} $0
	DetailPrint "TrimStrRight: '$0'"
SectionEnd
 
Section
	${StdUtils.Unload}
SectionEnd

ExecShellAsUser

The ${StdUtils.ExecShellAsUser} function allows you to launch a process with normal user privileges (user level), directly from an elevated installer instance (admin level). This way it provides a simple/lightweight alternative to the UAC plug-in. If the function succeeded, it returns either "ok" or "fallback". The former means that the process was crated via COM interface (which is required to allow the new process to not be elevated), while the latter means that the normal ShellExecute was used instead. Note that "fallback" is the expected result on systems that do not support UAC (like Windows XP and older). If the function failed, it returns "error".

!include 'StdUtils.nsh'
 
RequestExecutionLevel admin ;make sure our installer will get elevated on Vista+ with UAC enabled
ShowInstDetails show
 
Section
	DetailPrint 'ExecShell: "$SYSDIR\mspaint.exe"'
	ExecShell "open" "$SYSDIR\mspaint.exe" ;this instance of MS Paint will be elevated too!
	MessageBox MB_TOPMOST "Close Paint and click 'OK' to continue..."
SectionEnd
 
Section
	DetailPrint 'ExecShellAsUser: "$SYSDIR\mspaint.exe"'
	Sleep 1000
	${StdUtils.ExecShellAsUser} $0 "$SYSDIR\mspaint.exe" "open" "" ;launch a *non-elevated* instance of MS Paint
	DetailPrint "Result: $0" ;expected result is "ok" on UAC-enabled systems or "fallback" otherwise. Failure indicated by "error" or "timeout".
	${StdUtils.Unload} ;please do not forget to unload!
SectionEnd

ExecShellWait

The ${StdUtils.ExecShellWait} function works like the built-in ExecShell command, except that you can wait for the process to terminate. If the function failed, it returns "error". If the function succeeded, it will either return a process handle or "no_wait". The latter means that we cannot wait for the process, because ShellExecuteEx did not create a new process (but pass the file/URL to a running instance). Only if a process handle was returned, you can call ${StdUtils.WaitForProc} in order to wait until the process has terminated.

!include 'StdUtils.nsh'
 
RequestExecutionLevel user
ShowInstDetails show
 
Section
	DetailPrint 'ExecShellWait: "$SYSDIR\mspaint.exe"'
	Sleep 1000
	${StdUtils.ExecShellWait} $0 "$SYSDIR\mspaint.exe" "open" "" ;try to launch the process
	DetailPrint "Result: $0" ;returns process handle. Might be "no_wait". Failure indicated by "error".
	StrCmp $0 "error" WaitFailed ;check if process failed to create.
	StrCmp $0 "no_wait" WaitNotPossible ;check if process can be waited for. Always check this!
 
	DetailPrint "Waiting for process. ZZZzzzZZZzzz..."
	${StdUtils.WaitForProc} $0
	DetailPrint "Process just terminated."
	Goto WaitDone
 
	WaitFailed:
	DetailPrint "Failed to create process !!!"
	Goto WaitDone
 
	WaitNotPossible:
	DetailPrint "Can not wait for process."
	Goto WaitDone
 
	WaitDone:
	${StdUtils.Unload} ;please do not forget to unload!
SectionEnd

GetParameter

With ${StdUtils.GetParameter} you can check for the presence of a specific command-line parameter.

If the parameter was specified with a (non-empty) value, then the parameter's value is return. If the parameter was specified without a value, then an empty string is return. If the parameter has not been specified, then the default value is returned.

Parameters can be passed to the installer like this:

  • Installer.exe /Foobar
  • Installer.exe /Foobar=SomeValue
  • Installer.exe "/Foobar=Some Value With Whitespaces"
!include 'StdUtils.nsh'
 
RequestExecutionLevel user
ShowInstDetails show
 
Section
	${StdUtils.GetParameter} $R0 "Foobar" "<N/A>"
 
	StrCmp "$R0" "<N/A>" 0 +3
	DetailPrint "Parameter /Foobar is *not* specified!"
	Goto Finished
 
	StrCmp "$R0" "" 0 +3 ;'Installer.exe [...] /Foobar'
	DetailPrint "Parameter /Foobar specified without a value." 
	Goto Finished
 
	;'Installer.exe /Foobar=Foo' or 'Installer.exe "/Foobar=Foo Bar"'
	${StdUtils.TrimStr} $R0
	DetailPrint "Value of parameter /Foobar is: '$R0'"
 
	Finished:
	${StdUtils.Unload} ;please do not forget to unload!
SectionEnd

Download

Download:
StdUtils.2011-10-09.zip (104 KB)

SVN Repository:
http://code.google.com/p/mulder/source/browse/trunk/Utils/nsis_stdutils/