PowerShell support

From NSIS Wiki
Jump to navigationJump to search
Author: CharlesB (talk, contrib)


Description

These macros simplify PowerShell use in NSIS, and overcome a bug of PowerShell v2, described here, and reported here).

PowerShellExec and PowerShellExecLog respectively place the script output on the stack and in the log window.

The same goes for PowerShellExecFile and PowerShellExecFileLog, which are used to execute a PowerShell script file.

If execution fails, the error level is set to 2.

How to use

Save the script in a file named psexec.nsh, and place it next to your .nsi (or in the NSIS include directory). Then in your script, put an !include psexec.nsh, and call it like this

${PowerShellExec} "echo 'hello powershell'"  
Pop $R1 ;$R1 is "hello powershell"
 
InitPluginsDir
SetOutput $PLUGINSDIR\Powershell
File script.ps1
${PowerShellExecFileLog} "$PLUGINSDIR\Powershell\script.ps1"

In your PS commands, remember to escape the dollar sign by doubling it:

${PowerShellExec} "echo $$ProgramFiles"

Pitfalls

Be aware that as long as the installer is a 32 bit process, the PowerShell interpreter is also in 32 bit. This can cause redirections in filesystem, registry and environment variables when running or 64 bit systems (see wikipedia:WoW64)

Also I found that executing a file directly from $PLUGINSDIR caused errors in script execution, and that saving it in a $PLUGINSDIR subfolder prevented the errors.

The Script

!ifndef PSEXEC_INCLUDED
!define PSEXEC_INCLUDED
 
!macro PowerShellExecMacro PSCommand
  InitPluginsDir
  ;Save command in a temp file
  Push $R1
  FileOpen $R1 $PLUGINSDIR\tempfile.ps1 w
  FileWrite $R1 "${PSCommand}"
  FileClose $R1
  Pop $R1
 
  !insertmacro PowerShellExecFileMacro "$PLUGINSDIR\tempfile.ps1"
!macroend
 
!macro PowerShellExecLogMacro PSCommand
  InitPluginsDir
  ;Save command in a temp file
  Push $R1
  FileOpen $R1 $PLUGINSDIR\tempfile.ps1 w
  FileWrite $R1 "${PSCommand}"
  FileClose $R1
  Pop $R1
 
  !insertmacro PowerShellExecFileLogMacro "$PLUGINSDIR\tempfile.ps1"
!macroend
 
!macro PowerShellExecFileMacro PSFile
  !define PSExecID ${__LINE__}
  Push $R0
 
  nsExec::ExecToStack 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "${PSFile}"  '
 
  Pop $R0 ;return value is first on stack
  ;script output is second on stack, leave on top of it
  IntCmp $R0 0 finish_${PSExecID}
  SetErrorLevel 2
 
finish_${PSExecID}:
  Exch ;now $R0 on top of stack, followed by script output
  Pop $R0
  !undef PSExecID
!macroend
 
!macro PowerShellExecFileLogMacro PSFile
  !define PSExecID ${__LINE__}
  Push $R0
 
  nsExec::ExecToLog 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "${PSFile}"  '
  Pop $R0 ;return value is on stack
  IntCmp $R0 0 finish_${PSExecID}
  SetErrorLevel 2
 
finish_${PSExecID}:
  Pop $R0
  !undef PSExecID
!macroend
 
!define PowerShellExec `!insertmacro PowerShellExecMacro`
!define PowerShellExecLog `!insertmacro PowerShellExecLogMacro`
!define PowerShellExecFile `!insertmacro PowerShellExecFileMacro`
!define PowerShellExecFileLog `!insertmacro PowerShellExecFileLogMacro`
 
!endif

Version History

[Update 2014-01-07]
Initial version