powershellforeachparallel.foreachpowershell-core

How to force a EXE Powershell Script compiled with Win-PS2EXE to run with Powershell 7


I have a PowerShell script that I converted to an EXE with Win-PS2EXE.The problem is that I'm using Foreach -parallel and it doesn't work with PowerShell 5.0, I need this EXE to run with the latest shell and i already tried using #Requires -Version 7.0 and it stills running with the old one.

Tried with: #Requires -Version 7.0 #Requires -PSEdition Desktop


Solution

  • PS2EXE as well as its GUI front-end, Win-PS2EXE, are designed to work only with Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1), and not also with PowerShell (Core) 7 (the modern, cross-platform, install-on-demand edition), at least as of this writing.


    If you can assume the presence of PowerShell (Core) 7 on all machines where your executable must run, there is an - inefficient - workaround:

    Sample source-code script:

    # Content of a sample *.ps1 file to be compiled to an *.exe file
    # with Invoke-ps2exe
    
    # Sample parameter declarations.
    param(
      [string] $Foo,
      [int] $Bar
    )
    
    # Not running in PowerShell (Core) 7? Re-invoke with the latter's CLI.
    if (-not $IsCoreCLR) {
      # Create a helper script block that incorporates this script's,
      # to facilitate passing arguments through faithfully.
      $scriptBlock = [scriptblock]::Create(@"
       param(`$bound, `$undeclared)
       . { $($MyInvocation.MyCommand.ScriptBlock) } @bound @undeclared
    "@)
      pwsh.exe -ExecutionPolicy Bypass -NoProfile $scriptBlock -args $PSBoundParameters, (@(), $args)[$null -ne $args]
      # Exit and pass pwsh.exe's exit code through.
      exit $LASTEXITCODE
    }
    
    # Getting here means that the code is run by PowerShell (Core) 7 now.
    
    # == Place your PowerShell (Core) 7 code here. ==
    # Any arguments originally passed to your *.exe file
    # have been passed through, irrespective of whether the were bound
    # to formally declared parameters or anonymously positionally.
    
    # Sample command that uses ForEach-Object -Parallel
    1..3 | ForEach-Object -Parallel {
      "ID of thread #${_}: " + [System.Threading.Thread]::CurrentThread.ManagedThreadId
    }
    

    Note:


    If your get an error stating that The filename or extension is too long:
    # Content of a sample *.ps1 file to be compiled to an *.exe file
    # with Invoke-ps2exe
    
    # Sample parameter declarations.
    param(
      [string] $Foo,
      [int] $Bar
    )
    
    # Not running in PowerShell (Core) 7? Re-invoke with the latter's CLI.
    if (-not $IsCoreCLR) {
      # Create a temp. *.ps1 file and save this script's source code to it.
      # Note: NO *.ps1 file is involved in a ps2exe-compiled script; instead
      #       the source code is embedded in the *.exe file.
      $tmpScriptFile = (New-TemporaryFile).FullName
      Remove-Item -LiteralPath $tmpScriptFile; $tmpScriptFile += '.ps1'
      $MyInvocation.MyCommand.ScriptBlock | Set-Content -Encoding utf8 $tmpScriptFile
      # Now invoke the temp. *.ps1 file, passing all arguments through.
      pwsh.exe -ExecutionPolicy Bypass -NoProfile { param($scriptFile, $bound, $undeclared) . $scriptFile @bound @undeclared } -args $tmpScriptFile, $PSBoundParameters, (@(), $args)[$null -ne $args]
      Remove-Item -LiteralPath $tmpScriptFile # clean up
      # Exit and pass pwsh.exe's exit code through.
      exit $LASTEXITCODE
    }
    
    # Getting here means that the code is run by PowerShell (Core) 7 now.
    
    # == Place your PowerShell (Core) 7 code here. ==
    # Any arguments originally passed to your *.exe file
    # have been passed through, irrespective of whether the were bound
    # to formally declared parameters or anonymously positionally.
    
    # Sample command that uses ForEach-Object -Parallel
    1..3 | ForEach-Object -Parallel {
      "ID of thread #${_}: " + [System.Threading.Thread]::CurrentThread.ManagedThreadId
    }