Last active
July 9, 2026 08:06
-
-
Save scriptingstudio/2e7a6c1aa61406ca7dd24b47cb8eb1f7 to your computer and use it in GitHub Desktop.
Version 2. PS2EXE script deeply refactored replacing outdated techniques and overloaded logics with modern look and feel. Source compressor added to reduce output size. Source encoding, the return of the king. Implemented as a dotsource module.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #Requires -Version 5 | |
| # Insipred by and credits to PS2EXE by Markus Scholts (https://github.com/MScholtes/PS2EXE) | |
| # Deeply refactored original code replacing outdated techniques and overloaded logics with modern look and feel. Full backward compatibility provided. | |
| # Source compressor added to significantly reduce output size. Source encoding, the return of the king. Experimental support for PowerShell Core introduced. | |
| <# TODO: | |
| - remove "extract" facility (from C#) | |
| #> | |
| <# | |
| .SYNOPSIS | |
| Converts powershell scripts to standalone executables. | |
| .DESCRIPTION | |
| Converts powershell scripts to standalone executables. GUI output and input is activated with one switch, real windows executables are generated. You may use the graphical counterpart Ps2exedotNet for convenience. | |
| Please see Remarks on original project page for topics "GUI mode output formatting", "Config files", "Password security", "Script variables" and "Window in background in -noConsole mode". | |
| A generated executable has the following service parameters: | |
| -? [<MODIFIER>] PowerShell help text of the script inside the executable. The optional parameter combination "-? -detailed", "-? -examples", or "-? -full" can be used to get the appropriate help text. | |
| -debug Forces the executable to be debugged. It calls "System.Diagnostics.Debugger.Launch()" method. | |
| -extract:<FILENAME> Extracts the PowerShell script inside the executable and saves it as FileName. | |
| The script will not be executed. | |
| -wait At the end of the script execution it writes "Hit any key to exit..." and waits for a key to be pressed. | |
| -end All following options will be passed to the script inside the executable. | |
| All preceding options are used by the executable itself. | |
| .PARAMETER InputFile | |
| Specifies a PowerShell script to convert to executable (file has to be UTF8 or UTF16 encoded). | |
| .PARAMETER ScriptBlock | |
| Specifies a scriptblock to convert to executable. | |
| .PARAMETER OutputFile | |
| Destination executable file name or folder, defaults to inputFile with extension '.exe'. | |
| .PARAMETER Platform | |
| Specifies a target platform for the compiled executable. Valid values are x64, x86, any. | |
| .PARAMETER Lcid | |
| Specifies a locale ID for the compiled executable. Current user culture if not specified. | |
| .PARAMETER MTA | |
| Indicates to use Multi Thread Apartment mode, otherwise - STA. | |
| .PARAMETER NoConsole | |
| Indicates that the resulting executable will be a Windows Forms app without a console window. | |
| You might want to pipe your output to Out-String to prevent a message box for every line of output | |
| (example: dir C:\ | Out-String). | |
| .PARAMETER ConHost | |
| Indicates to force start with conhost as console instead of Windows Terminal. If necessary a new console window will appear. | |
| Important: Disables redirection of input, output or error channel! | |
| .PARAMETER Unicode | |
| Specifies encode output as Unicode in console mode, useful to display special encoded chars. | |
| .PARAMETER CredentialGUI | |
| Indicates to use GUI for prompting credentials in console mode instead of console input. | |
| .PARAMETER IconFile | |
| Specifies icon file name for the compiled executable. To set default enter "system", otherwise the icon from powershell.exe will be used | |
| .PARAMETER EmbedFiles | |
| Specifies paths to files to embed given as hashtable, will be extracted at runtime to the keys of the hashes, source file names must be unique, e.g. -embedFiles @{'Targetfilepath'='Sourcefilepath'}. | |
| Absolute and relative paths are allowed. For target paths a relative path beginning with '.\' is interpreted as relative to the executable, without the leading '.\' as relative to the current path at runtime. | |
| Directories are created automaticly on startup if necessary. | |
| In the target path environment variables in cmd.exe notation like %TEMP% or %APPDATA% are expanded at runtime. | |
| .PARAMETER Title | |
| Specifies title information (displayed in details tab of Windows Explorer's properties dialog). | |
| .PARAMETER Description | |
| Specifies description information (not displayed, but embedded in executable). | |
| .PARAMETER Company | |
| Specifies company information (not displayed, but embedded in executable). | |
| .PARAMETER Product | |
| Specifies product information (displayed in details tab of Windows Explorer's properties dialog). | |
| .PARAMETER Copyright | |
| Specifies copyright information (displayed in details tab of Windows Explorer's properties dialog). | |
| .PARAMETER Trademark | |
| Specifies trademark information (displayed in details tab of Windows Explorer's properties dialog). | |
| .PARAMETER Version | |
| Specifies version information (displayed in details tab of Windows Explorer's properties dialog). | |
| .PARAMETER ConfigFile | |
| Indicates to write a config file (<outputfile>.exe.config). | |
| .PARAMETER PrepareDebug | |
| Indicates to create helpful information for debugging of generated executable. See parameter -debug there. | |
| .PARAMETER NoOutput | |
| Indicates that the resulting executable will generate no standard output (includes verbose and information channel). | |
| .PARAMETER NoError | |
| Indicates that the resulting executable will generate no error output (includes warning and debug channel). | |
| .PARAMETER NoVisualStyles | |
| Indicates to disable visual styles for a generated windows GUI application. Only applicable with parameter -noConsole. | |
| .PARAMETER ExitOnCancel | |
| Indicates to exit program when Cancel or "✕" is selected in a Read-Host input box. Only applies with parameter -noConsole. | |
| .PARAMETER DPIAware | |
| Indicates that if display scaling is enabled, GUI controls will be scaled if possible. | |
| .PARAMETER WinFormsDPIAware | |
| Indicates to create an entry in the config file for WinForms to use DPI scaling. Forces -configFile and -supportOS. | |
| .PARAMETER RequireAdmin | |
| Indicates that if UAC is enabled, compiled executable will run only in elevated context (UAC dialog appears if required). | |
| .PARAMETER SupportOS | |
| Indicates to use features of latest Windows versions (run [Environment]::OSVersion to see the difference). | |
| .PARAMETER Virtualize | |
| Indicates that application virtualization is activated (forcing x86 runtime). | |
| .PARAMETER LongPaths | |
| Indicates to enable long paths (> 260 characters) if enabled on OS (works only with Windows 10 or up). | |
| .PARAMETER NoCompress | |
| Indicates to not use a PS compression for the source. | |
| .PARAMETER Basic | |
| Indicates to use a light compression mode. Omits comments and indented formatting in the output string. Ignored when -noCompress parameter is specified. | |
| .PARAMETER Core | |
| Experimental. Indicates to compile executable for .NET to support PowerShell Core. | |
| Deployment strategy for output file: single executable file | |
| Ways to accomplish: | |
| - Framework-dependent Deployment: PublishSingleFile (default) | |
| - Self-contained Deployment + Framework-independent: PublishSingleFile + PublishSelfContained [+ PublishTrimmed] | |
| .PARAMETER TargetOS | |
| Option for -Core. Specifies target operating system. Valid values are w[indows], l[inux], m[acos]. | |
| .PARAMETER Arm | |
| Option for -Core. Indicates to compile executable for ARM platforms. | |
| .PARAMETER DotnetVersion | |
| Option for -Core. Specifies .NET version for target system. | |
| .PARAMETER Solo | |
| Option for -Core. Enables 'SelfContained' mode. | |
| .PARAMETER NoCache | |
| Option for -Core. Indicates to omit checking for changes of build parameters. | |
| .PARAMETER RtProfile | |
| Experimental. | |
| .PARAMETER Run | |
| Indicates to invoke output file immediately after creation. | |
| .EXAMPLE | |
| Invoke-ps2exe C:\Data\MyScript.ps1 | |
| Converts C:\Data\MyScript.ps1 to C:\Data\MyScript.exe as console executable | |
| .EXAMPLE | |
| ps2exec -inputFile C:\Data\MyScript.ps1 -outputFile C:\Data\MyScriptGUI.exe -iconFile C:\Data\Icon.ico -noConsole -title "MyScript" -version 1.0 | |
| Converts C:\Data\MyScript.ps1 to C:\Data\MyScriptGUI.exe as graphical executable, icon and meta data | |
| .NOTES | |
| Version: 0.5.0.33 | |
| Date: 2025-08-19 | |
| Author: Ingo Karstein, Markus Scholtes | |
| Initial refactor release: 2026-03-08 | |
| Input source encoding caveat: hackers and bad guys can employ this tool to hide malware in EXE files and then Microsoft can block the script via Defender. | |
| .LINK | |
| https://github.com/MScholtes/PS2EXE | |
| https://gist.github.com/scriptingstudio/2e7a6c1aa61406ca7dd24b47cb8eb1f7 | |
| #> | |
| function Invoke-Ps2exe { | |
| [CmdletBinding(DefaultParameterSetName = 'File')] | |
| [Alias('ConvertTo-Exe','cvpe','ipe','ps2exe','p2e')] | |
| param ( | |
| # Input objects | |
| [Parameter(Position = 0, ParameterSetName = 'File')] | |
| [string] $inputFile, | |
| [Parameter(Position = 0, ParameterSetName = 'ScriptBlock')] | |
| [scriptblock] $scriptBlock, | |
| [string] $outputFile, | |
| [hashtable] $embedFiles, | |
| # Application metadata | |
| [string] $iconFile, | |
| [string] $title = 'PowerShell', | |
| [string] $description, [string] $company, [string] $product, | |
| [string] $copyright, [string] $trademark, | |
| [string] $version, # [string] because of buggy [version] not accepting a single decimal | |
| # Application runtime options | |
| [alias('profile')][string[]] $rtprofile, # experimental | |
| [ValidateSet('x86','x64','Anycpu')] | |
| [string] $platform = 'x64', | |
| [switch] $noConsole, [switch] $conHost, | |
| [switch] $noOutput, [switch] $noError, | |
| [alias('locale')][int] $lcid, | |
| [switch] $MTA, | |
| [alias('UNICODEEncoding')][switch] $unicode, | |
| [switch] $configFile, | |
| [alias('admin')][switch] $requireAdmin, | |
| [switch] $exitOnCancel, | |
| [switch] $credentialGUI, | |
| [switch] $supportOS, # confusing | |
| [switch] $longPaths, # confusing | |
| [switch] $virtualize, | |
| [switch] $prepareDebug, [switch] $raw, # prepareDebug's option | |
| [switch] $noCompress, [switch] $basic, # use basic compression | |
| [switch] $run, | |
| #[alias('silent')][switch] $quiet, # experimental | |
| # PowerShell Core options; experimental | |
| [switch] $core, # mode activator | |
| [string] $targetOS, # w[indows], l[inux], m[acos]; option for $core | |
| [switch] $arm, # option for $core | |
| [alias('dnv')][string] $dotnetversion, # option for $core | |
| [alias('aio','sc','selfcontained')][switch] $solo, # self-contained | |
| [switch] $nocache, # build option; option for $core | |
| # useless rubbish | |
| [switch] $noVisualStyles, # obsolete | |
| [switch] $DPIAware, # confusing | |
| # Legacy/obsolete options (remains for backward compatibility) | |
| [Parameter(DontShow)] [switch] $x86, | |
| [Parameter(DontShow)] [switch] $x64, | |
| [Parameter(DontShow)] [switch] $STA, | |
| [Parameter(DontShow)] [switch] $noConfigFile, | |
| # Internal use (service) | |
| [Parameter(DontShow)] | |
| [switch] $winFormsDPIAware, # confusing | |
| [Parameter(DontShow)] | |
| [alias('nested')][switch] $shellmode # don't use it manually! | |
| ) | |
| if (-not $shellmode) { | |
| Write-Host "PowerShell script converter to standalone executable v2.0`n" | |
| } | |
| if ($isMacos -or $islinux) { # experimental | |
| Write-Warning 'Operation is not supported on this platform. Windows only.' | |
| return | |
| } | |
| if (-not $inputFile -and -not $scriptblock) { | |
| Show-ModuleHelp | |
| return | |
| } | |
| # PS host router for PS.Core mode | |
| Resolve-PSHost $PSBoundParameters -shellmode:$shellmode | |
| # Resolve target paths | |
| if ($inputFile) { | |
| $inputFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($inputFile) # good staff | |
| if (-not (Test-Path -LiteralPath $inputFile -PathType Leaf)) { | |
| Write-Error "Input file '$inputfile' not found!" | |
| return | |
| } | |
| if ($inputFile -match 'RevShell|Update-KB4524147|Rek4m2ell|UpdatxK1q24147') { | |
| Write-Error "PS2EXEC did not convert this because PS2EXEC does not like malware." -Category ParserError -ErrorId RuntimeException # legacy bullshit | |
| return | |
| } | |
| $outputFile = if (-not $outputFile) { | |
| [System.IO.Path]::ChangeExtension($inputFile,'exe') | |
| } | |
| else { | |
| $fn = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($outputFile) | |
| if (Test-Path -Literalpath $fn -PathType Container) { | |
| Join-Path $outputFile ([System.IO.Path]::GetFileNameWithoutExtension($inputFile) + '.exe') | |
| } | |
| else {$fn} | |
| } | |
| } | |
| else { # scriptblock | |
| if (-not $outputFile) {Write-Warning 'Output file not specified. A random name will be created.'} | |
| $fn = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($outputFile) | |
| if (Test-Path -Literalpath $fn -PathType Container) { | |
| $fn = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetRandomFileName()) | |
| $outputFile = Join-Path $PWD.Path "$fn.exe" | |
| } | |
| } | |
| $workingDirectory = [System.IO.Path]::GetDirectoryName($outputFile) | |
| if ($outputFile -notmatch '\.exe$') { | |
| Write-Error 'Output file must have ".exe" extension!' | |
| return | |
| } | |
| if ($inputFile -and ($inputFile -eq $outputFile)) { | |
| Write-Error 'Input and output filenames cannot be the same!' | |
| return | |
| } | |
| if ($iconFile -match '^(%|system)$') {$iconFile = ''} # system icon | |
| elseif ($iconFile) { | |
| $iconFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($iconFile) | |
| if (-not (Test-Path -LiteralPath $iconFile -PathType Leaf)) { | |
| Write-Error "Icon file '$iconFile' not found!" | |
| return | |
| } | |
| } else { # default icon; [System.Drawing.Icon] | |
| $iconFile = Get-DefaultIcon | |
| } | |
| # end Resolve target paths | |
| #region Resolve parameterset collisions/dependencies | |
| Set-ParamProfile # experimental | |
| if ($winFormsDPIAware) { | |
| $supportOS = $true | |
| } | |
| if ($noConsole -and $conHost) { | |
| Write-Error "-noConsole cannot be used with -conHost." | |
| return | |
| } | |
| if ($requireAdmin -and $virtualize) { | |
| Write-Error "-requireAdmin cannot be used with -virtualize." | |
| return | |
| } | |
| if ($supportOS -and $virtualize) { | |
| Write-Error "-supportOS cannot be used with -virtualize." | |
| return | |
| } | |
| if ($longPaths -and $virtualize) { | |
| Write-Error "-longPaths cannot be used with -virtualize." | |
| return | |
| } | |
| if ($noConfigFile -and $configFile) { | |
| Write-Error "-configFile cannot be used with -noConfigFile." | |
| return | |
| } | |
| if (-not $configFile -and $longPaths) { | |
| Write-Warning "Forcing generation of a config file since the option -longPaths required." | |
| $configFile = $true | |
| } | |
| if (-not $configFile -and $winFormsDPIAware) { | |
| Write-Warning "Forcing generation of a config file since the option -winFormsDPIAware required." | |
| $configFile = $true | |
| } | |
| if ($noConsole) {$MTA = $false} | |
| $STA = -not $MTA # STA flag applies in C# | |
| # normalize metadata | |
| $Title = $Title -replace '\\', '\\' # WTF? | |
| $Product = $Product -replace '\\', '\\' | |
| $Copyright = $Copyright -replace '\\', '\\' | |
| $Trademark = $Trademark -replace '\\', '\\' | |
| $Description = $Description -replace '\\', '\\' | |
| $Company = $Company -replace '\\', '\\' | |
| # normalize version | |
| if (-not [string]::IsNullOrEmpty($version)) {$version = $version.trim()} | |
| if ($version) { | |
| if ($version -notmatch '\.') {$version = "$version.0"} # fix dotnet flaw | |
| if ($version -match '\.$') {$version = "${version}0"} # fix dotnet flaw | |
| if ($version -match '\.\.') {$version = $version.replace('..','.0.').replace('..','.0.')} | |
| if (-not ($version -as [version])) { | |
| Write-Error "Invalid version specified. Valid version format is n.n.n.n, n.n.n, n.n, or n ('n' is numeric val)!" | |
| return | |
| } | |
| } else { | |
| Write-Warning 'Version not specified. Defaults to 1.0.0.0' | |
| $version = '1.0.0.0' | |
| } | |
| #endregion parameter resolving | |
| if ($core) { | |
| Invoke-DotnetCompiler | |
| } else { | |
| Invoke-DomCompiler | |
| } | |
| if (Test-Path "$env:TEMP\ps2exedefault.ico") {Remove-Item "$env:TEMP\ps2exedefault.ico" -Force} | |
| } # END Invoke-Ps2exe | |
| function Set-ParamProfile { # experimental | |
| if ($null -eq $script:rtprofile) {return} | |
| switch ($script:rtprofile) { | |
| 'gui' { | |
| $script:noconsole = $true | |
| $script:nooutput = $true | |
| $script:noerror = $true | |
| } | |
| 'console' { | |
| $script:mta = $true | |
| $script:unicode = $true | |
| $script:supportOS = $true | |
| $script:longpaths = $true | |
| } | |
| 'winforms' { | |
| $script:credentialGUI = $true | |
| $script:exitOnCancel = $true | |
| $script:winFormsDPIAware = $true | |
| } | |
| default { | |
| if ($_) {Write-Warning "[$_] Invalid profile ID specified. Valid ID are gui, console, winforms"} | |
| } | |
| } | |
| } # END Set-ParamProfile | |
| function Add-Assembly ([switch]$noConsole) { | |
| $rawlist = [System.AppDomain]::CurrentDomain.GetAssemblies() | |
| $asmlist = [System.Collections.Generic.List[object]]::new() | |
| $asmlist.Add("System.dll") | |
| $asmlist.Add($rawlist.where({$_.ManifestModule.Name -eq "System.Management.Automation.dll"},'first').Location) | |
| $n = [System.Reflection.AssemblyName]::new("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") | |
| $null = [System.AppDomain]::CurrentDomain.Load($n) | |
| $asmlist.Add($rawlist.where({$_.ManifestModule.Name -eq "System.Core.dll"},'first').Location) | |
| if ($noConsole) { | |
| $n = [System.Reflection.AssemblyName]::new("System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") | |
| $null = [System.AppDomain]::CurrentDomain.Load($n) | |
| $n = [System.Reflection.AssemblyName]::new("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") | |
| $null = [System.AppDomain]::CurrentDomain.Load($n) | |
| $rawlist = [System.AppDomain]::CurrentDomain.GetAssemblies() # refresh list | |
| $asmlist.Add($rawlist.where({$_.ManifestModule.Name -eq "System.Windows.Forms.dll"},'first').Location) | |
| $asmlist.Add($rawlist.where({$_.ManifestModule.Name -eq "System.Drawing.dll"},'first').Location) | |
| } else { | |
| $conhostdll = $rawlist.where({$_.ManifestModule.Name -eq "Microsoft.PowerShell.ConsoleHost.dll"},'first') | |
| if ($conhostdll) {$asmlist.Add(@($conhostdll.Location)[0])} | |
| } | |
| $asmlist | |
| } # END Add-Assembly | |
| function Export-Manifest ([string]$outputFile, [string]$type) { | |
| # using: $DPIAware,$longPaths,$requireAdmin,$supportOS, $winFormsDPIAware | |
| if ($type -match 'win32|core') { | |
| $win32manifest = [System.Collections.Generic.List[object]]::new() | |
| $win32manifest.AddRange(('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>','<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">')) | |
| if ($DPIAware -or $longPaths) { | |
| $win32manifest.AddRange(('<application xmlns="urn:schemas-microsoft-com:asm.v3">',' <windowsSettings>')) | |
| if ($DPIAware) { | |
| $win32manifest.AddRange(('<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>','<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>')) | |
| } | |
| if ($longPaths) { | |
| $win32manifest.Add('<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>') | |
| } | |
| $win32manifest.AddRange(('</windowsSettings>','</application>')) | |
| } | |
| if ($requireAdmin) { | |
| $win32manifest.AddRange(('<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">','<security>','<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">','<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>','</requestedPrivileges>','</security>',' </trustInfo>')) | |
| } | |
| if ($supportOS) { | |
| $win32manifest.AddRange(('<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">',' <application>','<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>','<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>',' <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>','<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>','<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>','</application>','</compatibility>')) | |
| } | |
| $win32manifest.Add('</assembly>') | |
| $manifest = if ($type -eq 'core') {[System.IO.Path]::ChangeExtension($outputFile,"manifest")} else {"$outputFile.win32manifest"} | |
| ([xml]"$win32manifest").Save($manifest) | |
| #$win32manifest | Out-File -FilePath $manifest -Encoding UTF8 | |
| return | |
| } # win32 | |
| elseif ($type -ne 'exe3') {return} | |
| $configFileForEXE3 = [System.Collections.Generic.List[object]]::new() | |
| $x = if ($winFormsDPIAware) {'4.7'} else {'4.0'} | |
| $configFileForEXE3.AddRange(('<?xml version="1.0" encoding="utf-8" ?>',"<configuration><startup><supportedRuntime version=`"v4.0`" sku=`".NETFramework,Version=v$x`" /></startup>")) | |
| if ($longPaths) { | |
| $configFileForEXE3.Add('<runtime><AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false;Switch.System.IO.BlockLongPaths=false" /></runtime>') | |
| } | |
| if ($winFormsDPIAware) { | |
| $configFileForEXE3.Add('<System.Windows.Forms.ApplicationConfigurationSection><add key="DpiAwareness" value="PerMonitorV2" /></System.Windows.Forms.ApplicationConfigurationSection>') | |
| } | |
| $configFileForEXE3.Add('</configuration>') | |
| $configFileForEXE3 | Out-File -FilePath "$outputFile.config" -Encoding UTF8 | |
| } # END Export-Manifest | |
| function New-EmbedFileSection ([hashtable]$embedFiles, $compilerparam) { | |
| if ($null -eq $embedFiles -or $embedFiles.Count -eq 0) {return} | |
| Write-Host "Embedding $($embedFiles.Count) file(s)..." | |
| $newline = [Environment]::NewLine | |
| $sb = [System.Text.StringBuilder]::new() | |
| $null = $sb.Append("string tgtFile = string.Empty, tgtDir = string.Empty;$newline") | |
| $null = $embedFiles.GetEnumerator() | | |
| ForEach-Object -begin {$i = 0} -process { | |
| if (-not $_.Value -or -not $_.Name) {return} | |
| $content = try {Compress-Text ([System.IO.File]::ReadAllText($_.Value))} catch { | |
| Write-Warning "Error reading file '$($_.Value)'." | |
| return | |
| } | |
| $sb.Append(('tgtFile = Environment.ExpandEnvironmentVariables(@"{0}");{2}if (string.Compare(".\\", 0, tgtFile, 0, 2) == 0) {{ tgtFile = System.AppDomain.CurrentDomain.BaseDirectory + tgtFile.Substring(2); }}{2}try {{ tgtDir = System.IO.Path.GetDirectoryName(tgtFile);{2}if (tgtDir != string.Empty) {{ System.IO.Directory.CreateDirectory(tgtDir); }}{2}System.IO.File.WriteAllText(tgtFile, Expand_text(@"{1}"),Encoding.UTF8);{2}}}{2}catch {{ throw new System.IO.IOException("Error creating ''" + tgtFile + "''\r\n"); }}{2}' -f $_.Name, $content, $newline)) | |
| $i++ | |
| } | |
| if ($i) {$sb.ToString()} | |
| } # END New-EmbedFileSection | |
| function Get-SourceContent ([string]$inputFile, [switch]$resource) { | |
| Write-Host "Reading the source..." | |
| if ($noCompress) { | |
| $scriptstring = if ($inputFile) { | |
| [System.IO.File]::ReadAllText($inputFile) #, ([System.Text.Encoding]::utf8) | |
| } else {$scriptblock.ToString()} | |
| if (-not $scriptstring) { | |
| Write-Error "Could not import the source." | |
| exit | |
| } | |
| } else { | |
| Write-Host 'Minifying...' | |
| $param = @{NoTest = $true} | |
| if ($basic) {$param['basic'] = $true} | |
| if ($inputFile) {$param['Path'] = $inputFile} else {$param['Scriptblock'] = $scriptblock} | |
| $scriptstring = Compress-ScriptBlock @param | |
| if (-not $scriptstring) { | |
| Write-Error "Could not compress the source." | |
| exit | |
| } | |
| } | |
| if ($inputFile) { | |
| $hl = Get-Content -LiteralPath $inputFile -TotalCount 1 -ErrorAction 0 | |
| if ($hl -match '#Requires\s+-Version\s+[67]') { | |
| Write-Warning "Input script contains `"$($matches[0])`" directive that may at least prevent correct working of the output executable." | |
| } | |
| } | |
| Write-Host 'Compressing & encoding...' # the return of the king: nomore antivirus glitches | |
| if ($resource) { # as embedResource; experimental | |
| $tempinputFile = $env:TEMP,'ResourceFile.ps1' -join '\' | |
| Compress-Text $scriptstring | Out-File -FilePath $tempinputFile -Encoding utf8 | |
| $tempinputFile | |
| } else { | |
| Compress-Text $scriptstring | |
| } | |
| } # END Get-SourceContent | |
| function Compress-ScriptBlock { | |
| [CmdletBinding(DefaultParameterSetName = 'File')] | |
| [OutputType([string])] | |
| [Alias('cmsb')] | |
| param ( | |
| [Parameter(Position = 0, Mandatory, ParameterSetName = 'File')] | |
| [ValidateNotNullOrEmpty()] | |
| [alias('FilePath','FileName')][String] $Path, | |
| [Parameter(Position = 0, Mandatory, ParameterSetName = 'ScriptBlock')] | |
| [ValidateNotNullOrEmpty()] | |
| [ScriptBlock] $ScriptBlock, | |
| [Parameter(ParameterSetName = 'File')] | |
| [Switch] $NoTest, | |
| [Switch] $Basic # remove comments only | |
| ) | |
| if ($Path) { | |
| if (-not $NoTest) {if (-not (Test-Path -Path $Path)) {return}} | |
| $scriptBlockString = [IO.File]::ReadAllText((Resolve-Path $Path).Path) #, ([System.Text.Encoding]::utf8) | |
| $scriptBlock = [ScriptBlock]::Create($scriptBlockString) | |
| } else { | |
| # Convert the scriptblock to a string so that it can be referenced to extract script lines | |
| $scriptBlockString = $scriptBlock.ToString() | |
| } | |
| if ([String]::IsNullOrEmpty($scriptBlockString)) {return} | |
| # Tokenize the scriptblock and return all tokens except for comments/exclusions | |
| $psperrors = $null | |
| $tokens = @([System.Management.Automation.PSParser]::Tokenize($scriptBlock, [Ref]$psperrors)).where({$_.Type -ne 'Comment' -or $_.Content -match '^#Requires'}) | |
| if ($null -eq $tokens) {return} | |
| #if ($psperrors.count) {} | |
| # Parse options | |
| $currentColumn = 1 # script line cursor | |
| $newLine = $false # sequential newlines semaphore | |
| $prevToken = $null # token cursor to look back | |
| $tindex = 0 # token index to lookup ahead | |
| $rxopt = [System.Text.RegularExpressions.RegexOptions]'Compiled,IgnoreCase' | |
| $rxopertyp = [regex]::new('[,+=|/\$\-]',$rxopt) | |
| $rxnewl = [regex]::new('^NewLine|^LineContin',$rxopt) | |
| $rxgroup = [regex]::new('Group[SE]',$rxopt) | |
| $rxtype = [regex]::new('GroupStart|NewLine|LineContin|Statement',$rxopt) | |
| $rxhere = [regex]::new("^@['`"]",$rxopt) | |
| # The show begins here | |
| $strBuilder = foreach ($currentToken in $tokens) { | |
| if ($rxnewl.IsMatch($currentToken.Type)) { | |
| $currentColumn = 1 | |
| # Handle newlines. Sequential newlines are ignored in order to save space | |
| if (-not $newLine) { | |
| if ($Basic) {"`n"} #[Environment]::NewLine | |
| else { | |
| $t1 = $tindex+1 | |
| $next = $t1 -lt $tokens.count | |
| # TODO: 1) ignore newline on condition 2) insert StatementSeparator on condition | |
| if ($rxtype.IsMatch($prevToken.Type)) {} | |
| elseif ($prevToken.Type -eq 16) {} | |
| elseif ($next -and $tokens[$t1].Type -eq 13) {} #13=GroupEnd | |
| elseif ($next -and $tokens[$t1].Type -eq 14 -and $tokens[$t1].Content -eq 'function') {} | |
| elseif ($next -and $tokens[$t1].Type -eq 6 -and $prevToken.Type -eq 12 -and $prevToken.Content -eq '(') {} | |
| elseif ($prevToken.Type -eq 11 -and $rxopertyp.IsMatch($prevToken.Content)) {} # 11=Operator | |
| elseif ($prevToken.Content -eq ')' -and $next -and $tokens[$t1].Content -eq '{') {} | |
| elseif ($prevToken.Type -eq 7 -and $next -and $tokens[$t1].Type -eq 7) {';'} | |
| #?elseif ($prevToken.Type -eq 13 -and $currentToken.Type -ne 'keyword') {';'} | |
| elseif ($next -and $tokens[$t1].Type -eq 6 -and -not $rxgroup.IsMatch($prevToken.Type)) {';'} | |
| elseif ($prevToken.Type -eq 1) {';'} # 1=command | |
| elseif ($prevToken.Type -eq 2) {' '} # 2=CommandParameter | |
| else {"`n"} #[Environment]::NewLine | |
| } | |
| $newLine = $true | |
| } | |
| } else { | |
| $newLine = $false | |
| # Handle any indenting | |
| if ($currentColumn -lt $currentToken.StartColumn) { | |
| # Handle spaces in between tokens on the same line | |
| # TODO: ignore extra spaces on condition | |
| if ($currentColumn -ne 1) { | |
| if ($Basic) {' '} | |
| else { | |
| if ($currentToken.Type -eq 11 -and $rxopertyp.IsMatch($currentToken.Content)) {} #11='Operator' | |
| elseif ($prevToken.Type -eq 11 -and $rxopertyp.IsMatch($prevToken.Content)) {} | |
| elseif ($currentToken.Type -eq 6 -and $prevToken.Type -eq 12 -and $prevToken.Content -eq '(') {} | |
| elseif ($rxgroup.IsMatch($currentToken.Type) -or $prevToken.Type -eq 16) {} #16='StatementSeparator' | |
| elseif ($currentToken.Type -eq 14 -and $currentToken.Content -ne 'in') {} #14='keyword' | |
| else {' '} | |
| } | |
| } | |
| } | |
| $tokenBody = $scriptBlockString.Substring($currentToken.Start,$currentToken.Length) | |
| $herestring = $currentToken.Type -eq 5 -and $rxhere.IsMatch($tokenBody) | |
| # Handle multi-line strings 5='String' | |
| if (-not $herestring -and $currentToken.Type -eq 5 -and $currentToken.EndLine -gt $currentToken.StartLine) { | |
| $tokenBody.replace([Environment]::NewLine,'') | |
| } | |
| else { # Write out a regular token | |
| $tokenBody | |
| } | |
| # Update current position in the column | |
| $currentColumn = $currentToken.EndColumn | |
| } # token type | |
| $prevToken = $currentToken | |
| $tindex++ | |
| } # token iterator | |
| [string]::Concat($strBuilder) # -replace '(\r\n){2,}' .replace("`r`n`r`n","") | |
| } # END Compress-ScriptBlock | |
| function Compress-Text ([string[]] $InputObject) { | |
| $memoryStream = [System.IO.MemoryStream]::new() | |
| $compressionStream = [System.IO.Compression.DeflateStream]::new($memoryStream, [System.IO.Compression.CompressionMode]::Compress) | |
| $streamWriter = [System.IO.StreamWriter]::new($compressionStream) # default is UTF8 | |
| try { | |
| $streamWriter.Write($InputObject -join "`n") #[Environment]::newline | |
| $streamWriter.Close() | |
| [System.Convert]::ToBase64String($memoryStream.ToArray()) | |
| } catch { | |
| Write-Error $_ | |
| } | |
| $memoryStream.Dispose() | |
| $streamWriter.Dispose() | |
| } # END Compress-Text | |
| function Resolve-PSHost ($parameters, [switch]$shellmode) { | |
| if ($PSVersionTable.PSEdition -ne 'Core' -or $core) {return} | |
| # PS7 host detected, start compiler in Windows PowerShell | |
| if ($shellmode) { | |
| Write-Warning 'Service mode: invalid set of parameters. Do not use "-shellmode" parameter.' | |
| exit | |
| } | |
| Write-Warning "Incompatible platform detected. Reverting to Windows PowerShell." | |
| # convert parameter table to command line form | |
| $params = [System.Text.StringBuilder]::new() | |
| $null = $parameters.GetEnumerator().foreach{ | |
| $key,$val = $_.Key,$_.Value | |
| if ($val -is [System.Management.Automation.SwitchParameter]) { | |
| if ($val.IsPresent) {$params.Append(" -$key")} | |
| } | |
| elseif ($val -is [string]) { | |
| if ($val.contains(' ') -or [string]::IsNullOrEmpty($val)) {$val = "'$val'"} | |
| $params.Append(" -$key $val") | |
| } | |
| elseif ($val -is [hashtable]) { | |
| $ht = $val.GetEnumerator().foreach{"'{0}'='{1}';" -f $_.Key,$_.Value} | |
| $params.Append(" -$key @{$ht}") | |
| } | |
| elseif ($val -is [array]) { | |
| $ar = $val.foreach{ | |
| elseif ($_ -is [string] -and | |
| ($_.contains(' ') -or [string]::IsNullOrEmpty($_))) {"'$_'"} else {$_} | |
| } -join ',' | |
| $params.Append(" -$key $ar") | |
| } | |
| elseif ($val -is [scriptblock]) {$params.Append(" -$key {$val}")} | |
| else { | |
| $params.Append(" -$key $val") | |
| } | |
| } # foreach | |
| $cmd = (Get-Variable -Name MyInvocation -Scope 1).Value.MyCommand.Name # caller name | |
| # dependency: module filename | |
| & powershell.exe -NoLogo -Command "if (-not (Get-Command -Name $cmd -ErrorAction 0)) {. $PSScriptRoot\ps2exec.ps1}; &'$cmd' $($params.ToString()) -shellmode" | |
| exit | |
| } # END Resolve-PSHost | |
| function Invoke-DomCompiler { | |
| $topline = Get-Content -LiteralPath $inputFile -TotalCount 1 -ErrorAction 4 | |
| if ($topline -match '#Requires\s+-Version') { # temporarily stub | |
| $ver = $topline.trim().split().where{$_}[2] | |
| if (5 -lt $ver.split('.')[0]) { | |
| Write-Warning "Incompatible target PowerShell host version ($ver) requirement detected. Current compiler mode is for Windows PowerShell only. Your script will fail to start." | |
| } | |
| } | |
| # Generate compiler options | |
| Write-Host "Preparing compiler..." -ForegroundColor Yellow | |
| $compilerparam = [System.CodeDom.Compiler.CompilerParameters]::new((Add-Assembly -noConsole:$noConsole), $outputFile) | |
| $compilerparam.GenerateInMemory = $false | |
| $compilerparam.GenerateExecutable = $true | |
| # Compiler command line options | |
| # preference if a legacy param is used; backward compatibility | |
| if ($x64 -and -not $x86) {$platform = 'x64'} | |
| if ($x86 -and -not $x64) {$platform = 'x86'} | |
| $target = if ($noConsole -or $conHost) {'winexe'} else {'exe'} | |
| $manifestParam = if ($requireAdmin -or $DPIAware -or $supportOS -or $longPaths) { | |
| '"/win32manifest:{0}.win32manifest"' -f $outputFile | |
| Export-Manifest $outputFile -type 'win32' | |
| } | |
| if ($virtualize) { | |
| Write-Host 'Application virtualization is activated, forcing x86 platform.' | |
| $platform = 'x86' | |
| $manifestParam = '/nowin32manifest' | |
| } | |
| $iconFileParam = if ($iconFile) { | |
| '"/win32icon:{0}"' -f $iconFile | |
| } | |
| $optimizeParam = if (-not $prepareDebug) {'/optimize+'} | |
| $compilerparam.CompilerOptions = "/platform:$platform /target:$target $optimizeParam $iconFileParam $manifestParam".TrimEnd() | |
| # Debug settings | |
| $compilerparam.IncludeDebugInformation = $prepareDebug | |
| if ($prepareDebug) { | |
| $compilerparam.TempFiles.KeepFiles = $true | |
| } | |
| # Create C# compiler | |
| # generic dictionary type needed for CSharpCodeProvider | |
| $dict = [Activator]::CreateInstance([System.Collections.Generic.Dictionary`2[string,string]]) | |
| ###$dict = [System.Collections.Generic.Dictionary[string,string]]::new() | |
| $dict['CompilerVersion'] = 'v4.0' | |
| $csprov = [Microsoft.CSharp.CSharpCodeProvider]::new($dict) | |
| $maincs = Get-MainProgramSource | |
| Write-Host "Compiling..." -ForegroundColor Yellow | |
| $cpstart = [datetime]::now | |
| # NOTE: https://learn.microsoft.com/en-us/dotnet/api/system.codedom.compiler.codedomprovider.compileassemblyfromsource?view=net-11.0-pp#remarks | |
| $cresult = $csprov.CompileAssemblyFromSource($compilerparam, $maincs) | |
| $rt = Measure-Compiler $cpstart | |
| #####Write-Host "Finishing..." | |
| if ($cresult.Errors.Count -gt 0) { | |
| Write-Host "Compiling runtime : $rt" | |
| if (Test-Path -LiteralPath $outputFile) { | |
| try { | |
| Remove-Item -LiteralPath $outputFile -Force #-Verbose:$false | |
| } catch { | |
| Write-Error $_ | |
| } | |
| } | |
| Write-Error "Compilation failed. Use -verbose parameter to see details." -ErrorAction Continue | |
| # omit useless enumeration of errors if -verbose not specified | |
| if ($VerbosePreference -ne 0) { | |
| $cresult.Output | Write-Host | |
| $cresult.Errors | Write-Verbose | |
| } | |
| } | |
| else { | |
| if (Test-Path -LiteralPath $outputFile) { | |
| Write-Host "Output file '$outputFile' successfully created" -ForegroundColor Green | |
| Write-Host "Compiling runtime : $rt" | |
| Measure-Output $outputFile | |
| if ($configFile) { | |
| Export-Manifest $outputFile -type 'exe3' | |
| Write-Host "Config file for EXE created" | |
| } | |
| if ($run) { | |
| Write-Host "Starting '$outputFile'..." | |
| & $outputFile | |
| } | |
| } | |
| else { | |
| Write-Error "Output file '$outputFile' not created." -ErrorAction Continue | |
| } | |
| } | |
| $csprov.Dispose() # release a compiler object | |
| if ($prepareDebug) { | |
| $dstSrc = if ($raw) {$workingDirectory} | |
| else {[System.IO.Path]::GetDirectoryName($outputFile)} | |
| $cresult.TempFiles | Where-Object {$_ -match "\.cs$"} | | |
| Select-Object -First 1 | ForEach-Object { | |
| Write-Host "Source file for debug copied: '$dstSrc\main.cs'" | |
| Copy-Item -Path $_ -Destination "$dstSrc\main.cs" -Force | |
| } | |
| } | |
| Write-Host 'Removing temp files...' | |
| $cresult.TempFiles | Remove-Item -Force -ErrorAction SilentlyContinue #-Verbose:$false | |
| if ($requireAdmin -or $DPIAware -or $supportOS -or $longPaths) { | |
| if (Test-Path -LiteralPath "$outputFile.win32manifest") { | |
| Remove-Item -LiteralPath "$outputFile.win32manifest" -Force #-Verbose:$false | |
| } | |
| } | |
| } # END Invoke-DomCompiler | |
| #region PS.Core support | |
| function Invoke-DotnetCompiler { | |
| # Some C# hints taken from https://github.com/FabienTschanz/PS2EXE.Core | |
| # Project-based compilation used | |
| ##api.nuget.org -Port 443 | |
| $pp = $global:ProgressPreference # fix PS.Core flaw | |
| $ProgressPreference = $global:ProgressPreference = 'SilentlyContinue' | |
| if (-not (Test-NetConnection -ComputerName nuget.org -InformationLevel Quiet -ErrorAction 0)) { | |
| Write-Warning 'Check your internet connection or DNS/Firewall settings. PowerShell Core support requires the public NuGet repository available at compiling runtime.' | |
| $ProgressPreference = $global:ProgressPreference = $pp | |
| return | |
| } | |
| $ProgressPreference = $global:ProgressPreference = $pp | |
| if (-not $solo) { # if self-contained mode | |
| Write-Warning ".NET versions of the compiled program and the target system must match." | |
| } | |
| Write-Host "Preparing compiler..." -ForegroundColor Yellow | |
| $dotnethost = Resolve-DotnetHost -nocache:$nocache | |
| if ($null -eq $dotnethost) { | |
| Write-Warning "Could not obtain .NET information." | |
| return | |
| } | |
| $dotnetversion,$pssdk = Resolve-DotnetRequirement $dotnethost $dotnetversion | |
| if (-not $dotnetversion) {return} | |
| # Prepare and save the project to build | |
| $csProjName = [System.IO.Path]::GetFileNameWithoutExtension($inputFile) | |
| $outputDirectory = [System.IO.Path]::GetDirectoryName($outputFile) | |
| $releaseDir = Join-Path -Path $outputDirectory -ChildPath 'Release' | |
| $requiresWinForms = [System.IO.File]::ReadAllText($inputFile) -match 'Add-Type\s+-AssemblyName\s+(System\.Windows\.Forms|PresentationFramework|\.showdialog\(\))' | |
| $includenativelibs = $PSVersionTable.PSVersion.Major -lt 6 -or -not $dotnethost.local.psver -or [version]$dotnethost.local.psver -lt [version]'7.6' | |
| $targetOS = if (-not $targetOS) {'win'} | |
| elseif ($targetOS -match '^windows|^win|^w') {'win'} | |
| elseif ($targetOS -match '^linux|^lx?') {'linux'} | |
| elseif ($targetOS -match '^macos|^mac|^osx|^m') {'osx'} | |
| else { | |
| 'win' | |
| Write-Warning 'Incorrect target OS specified. Reset to Windows. Correct values are windows, linux, mac' | |
| } | |
| $platform = if ($arm) {'arm64'} | |
| elseif ($targetOS -match 'linux|osx') {'x64'} | |
| elseif ($platform -eq 'x64') {'x64'} else {'x86'} | |
| $manifestFileName = if ($requireAdmin -or $DPIAware -or $supportOS -or $longPaths) { | |
| #Split-Path "$outputFile.win32manifest" -Leaf | |
| #Export-Manifest $outputFile -type 'win32' | |
| Split-Path ([System.IO.Path]::ChangeExtension($outputFile,"manifest")) -Leaf | |
| Export-Manifest $outputFile -type 'core' | |
| } | |
| $outputType = if ($targetOS -eq 'win') { | |
| if ($noConsole -or $CredentialGUI -or $requiresWinForms) { | |
| 'WinExe' | |
| $dotnetOS = '-windows' | |
| } else {'Exe'} | |
| } | |
| $psdnet = [System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription -split ' ' | |
| [pscustomobject][ordered]@{ | |
| 'Type' = 'Local' | |
| '.NET/SDK' = $(& dotnet.exe --version) | |
| 'PowerShell' = $dotnethost.local.psver | |
| 'PS .NET' = if ($PSVersionTable.PSVersion.Major -lt 6) {$psdnet[2]} else {$psdnet[1]} | |
| 'OS Id' = [System.Runtime.InteropServices.RuntimeInformation]::OSDescription -replace 'microsoft ' | |
| 'Platform' = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.tostring().tolower() | |
| }, | |
| [pscustomobject][ordered]@{ | |
| 'Type' = 'Target' | |
| '.NET/SDK' = $dotnetversion | |
| 'PowerShell' = $dotnethost.local.psver | |
| 'PS .NET' = '' | |
| 'OS Id' = $targetOS | |
| 'Platform' = $platform | |
| } | Format-Table | |
| $l = [version]$(& dotnet.exe --version) | |
| $t = [version]$dotnetversion | |
| $status = if ($t.Major -eq $l.Major -and $t.Minor -eq $l.Minor) {'OK'} else {'Not match'} | |
| Write-Host ".NET SDK requirements: $status" | |
| if ($status -ne 'OK') {return} | |
| $appIcon = if ($iconFile) {[System.IO.Path]::GetFileName($iconFile)} | |
| if ($appIcon -and -not (Test-Path $outputDirectory\$appIcon)) { | |
| Copy-Item -LiteralPath $iconFile -Destination $outputDirectory | |
| } | |
| $useWindowsForms = $noConsole -or $CredentialGUI -or $requiresWinForms | |
| # EXPERIMENTAL | |
| $constants = '' # already set in CS template | |
| # +PublishSingleFile (default) includes only the application and third-party assemblies | |
| # +SelfContained includes .NET runtime, output size up to 6x | |
| # +Trimmed requires SelfContained | |
| # -AOT requires SelfContained; unreliabale/useless | |
| # -ReadyToRun heavily increases compile time (6-10+x), output size (2-3x), and start time (1.5+x) without any valueble effect; useless | |
| # [File-based apps: Self-contained + Native AOT + .NET10+] - a different way to compile | |
| $trimmed = $readyToRun = $aot = $false | |
| $selfContained = $solo | |
| if ($trimmed -and -not $selfContained) {$selfContained = $trimmed} | |
| ##if ($aot -and -not $selfContained) {} | |
| # Generate project files; !comes after all: parameters already must be set! | |
| $csProjPath = "$outputDirectory\$csProjName.csproj" | |
| $csProjSource = "$outputDirectory\$csProjName.cs" | |
| Set-CSProject $csProjPath $csProjSource | |
| if ($prepareDebug) { | |
| Write-Host "`nPublish resources`n-----------------" | |
| Write-Host ( | |
| "CS File : $csProjName.cs", | |
| "Project File : $csProjName.csproj", | |
| "Output Path : $outputDirectory`n" | |
| ) -Separator "`n" | |
| } | |
| Write-Host "`nPublish profile`n---------------" | |
| Write-Host ( | |
| "SingleFile : (default)", | |
| "SelfContained : $selfContained", | |
| "Trimmed : $trimmed", | |
| "Aot : (disabled)", | |
| "ReadyToRun : (disabled)" | |
| ) -Separator "`n" | |
| # enclose paths in double quotes? | |
| $dotnetArgs = 'publish', $csProjPath, '-c', 'Release', '-o', $releaseDir | |
| # TODO: better Verbose | |
| if ($VerbosePreference -eq 'Continue') { | |
| $dotnetArgs += '-v', 'd' | |
| } else { | |
| $dotnetArgs += '-v', 'q', '--property', 'WarningLevel=0', '/clp:ErrorsOnly' | |
| } | |
| Write-Host "`nCompiling..." -ForegroundColor Yellow | |
| $dnstart = [datetime]::now | |
| $dotnetOutput = & dotnet.exe @dotnetArgs 2>&1 | |
| $rt = Measure-Compiler $dnstart | |
| $compileresult = $LASTEXITCODE | |
| if ($compileresult -ne 0) { | |
| Write-Host "Compiling runtime : $rt" | |
| $errorLines = $dotnetOutput | Where-Object {$_ -is [System.Management.Automation.ErrorRecord] -or $_ -match 'error'} | |
| $errorDetail = if ($errorLines) {$errorLines} else {$dotnetOutput} | |
| throw "dotnet publish failed with exit code $LASTEXITCODE.`n$(($errorDetail | Out-String).Trim())" | |
| } | |
| # Publish "exe", remove temp files | |
| ##Write-Host "Successfully built project '$csProjName' in '$outputDirectory'." -ForegroundColor Green | |
| Write-Host "Output file '$outputFile' successfully created" -ForegroundColor Green | |
| if (-not $selfContained) { | |
| Write-Host "Target runtime environment will require .NET SDK version $dotnetversion. To avoid framework dependency use parameter -solo, but this will increase compilation time and output file size." -ForegroundColor Yellow | |
| } | |
| Write-Host "Compiling runtime : $rt" | |
| Measure-Output "$releaseDir\$csProjName.exe" | |
| # TODO: better debug info | |
| # what about linux and macos? fuck it all | |
| Copy-Item -Path $releaseDir\$csProjName.exe -Destination $outputDirectory -Force -ErrorAction 0 | |
| Write-Host 'Removing temp files...' | |
| if (-not $prepareDebug) { | |
| Remove-Item -Path $releaseDir -Recurse -Force -ErrorAction 0 | |
| Remove-Item -Path $outputDirectory\bin -Recurse -Force -ErrorAction 0 | |
| Remove-Item -Path $outputDirectory\obj -Recurse -Force -ErrorAction 0 | |
| Remove-Item -Path $csProjPath -Force -ErrorAction 0 | |
| Remove-Item -Path $csProjSource -Force -ErrorAction 0 | |
| } | |
| if ($appIcon -and "$outputDirectory\$appIcon" -ne $iconFile) { | |
| Remove-Item -Path $outputDirectory\$appIcon -Force -ErrorAction 0 | |
| } | |
| if ($manifestFileName -and -not $prepareDebug) { | |
| Remove-Item -Path $outputDirectory\$manifestFileName -Force -ErrorAction 0 | |
| } | |
| if ($run) { | |
| Write-Host "Starting '$outputFile'..." | |
| & $outputFile | |
| } | |
| } # END Invoke-DotnetCompiler | |
| function Resolve-DotnetHost ([switch]$nocache) { | |
| $dnlocal = try { | |
| (& dotnet.exe --list-sdks) | ForEach-Object {$_.split('.')[0,1] -join '.'} | Sort-Object {[version]$_} -Descending | |
| } catch {} | |
| if (-not $dnlocal) { | |
| throw "The .NET SDK is not installed. Please install it from https://dotnet.microsoft.com/en-us/download." | |
| #Install-Dotnet -force | |
| } | |
| # TODO: what if internet or github unaccessable? soft workaround: caching | |
| $dncache = "$env:TEMP\dnrelease.json" | |
| $expired = if (Test-Path $dncache) { | |
| $lwt = Get-ItemProperty -LiteralPath $dncache -Name LastWriteTime | |
| ([datetime]::now - $lwt.LastWriteTime).totaldays -gt 2 | |
| } else {$true} | |
| if ($expired -or $nocache) { | |
| $getrelease = {param($url) (Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 15 -ErrorAction 0 -Verbose:$false).links | Where-Object href -match '/releases/tag' | ForEach-Object {$_.href.split('/')[-1].Substring(1)}} | |
| $p = $ProgressPreference | |
| $ProgressPreference = 'SilentlyContinue' | |
| $dnrel = & $getrelease 'https://github.com/dotnet/core/tags' | Sort-Object {[version]$_.split('-')[0]} -Unique -Descending | |
| $psrel = & $getrelease 'https://github.com/PowerShell/PowerShell/tags' | Sort-Object -Unique -Descending | |
| $ProgressPreference = $p | |
| $psmap = [ordered]@{} | |
| $dnrel | Group-Object {($_.split('.')[0,1] -join '.')} | ForEach-Object { | |
| $psmap[$_.name] = if ($_.group -match 'prev') {@{preview = $true}} else {@{}} | |
| } | |
| $psrel | Group-Object {($_.split('.')[0,1] -join '.')} | | |
| ForEach-Object -begin {$i = 0} -process { | |
| $v = @($psmap.Keys)[$i] | |
| $psmap[$v]['branch'] = $_.name | |
| $psmap[$v]['psver'] = $_.group[0] | |
| $i++ | |
| } | |
| $latest = if ($PSVersionTable.PSVersion.Major -lt 6) { | |
| try {((pwsh.exe -Version) -split ' ')[1]} catch {} | |
| } else {$PSVersionTable.PSVersion.ToString()} | |
| $psmap['local'] = @{ | |
| dotnet = @($dnlocal) | |
| psver = $latest | |
| } | |
| ##Remove-Item -LiteralPath $dncache -Force -ErrorAction 0 | |
| $psmap | ConvertTo-Json -Compress | Out-File $dncache -Encoding utf8 -ErrorAction 0 | |
| [pscustomobject]$psmap | |
| } else { | |
| Get-Content $dncache -Raw | ConvertFrom-Json -ErrorAction 0 | |
| } | |
| } # END Resolve-DotnetHost | |
| function Resolve-DotnetRequirement ($dotnethost, $dotnetversion) { | |
| $supportFrame = $dotnethost.psobject.properties.name | Where-Object {$_ -ne 'local'} | Sort-Object {[version]$_} -Descending | |
| if ($supportFrame) { | |
| $supportFrame[0] = [int]$supportFrame[0] | |
| $supportFrame[-1] = [int]$supportFrame[-1] | |
| } else { | |
| $supportFrame = 8,11 # fallback | |
| } | |
| $matchlocal = $dotnethost.local.dotnet | Where-Object {([version]$_).major -ge $supportFrame[-1]} | |
| [string[]]$listlocal = $dotnethost.local.dotnet | Sort-Object {[version]$_} -Descending | |
| if ($dotnethost.local.dotnet -and -not $matchlocal) { | |
| Write-Warning ".NET version $($supportFrame[-1]) or higher is required. Current version is $($listlocal -join ', '). Install latest from https://dotnet.microsoft.com/en-us/download. .NET Core lifecycle: https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core#lifecycle" | |
| return | |
| } | |
| # TODO: auto-determine minimum dotnet version | |
| $mindn,$minps = '10.0','7.6.*' # magic numbers, soft defaults, should be revisited in 2028 | |
| if (-not $dotnetversion) { | |
| $dotnetversion = if ($dotnethost.local.dotnet) {$listlocal[0]} else {$mindn} | |
| } elseif ($dotnetversion -eq 'latest') { | |
| $dotnetversion = if ($dotnethost.count) {@($dotnethost.psobject.properties.name)[0]} else {$mindn} | |
| } | |
| if ($dotnetversion -notmatch '\.') {$dotnetversion = "$dotnetversion.0"} | |
| $dotnetversion = $dotnetversion -replace '[dotnet]' | |
| $dotnetversion = $dotnetversion.split('.')[0,1] -join '.' | |
| if ($supportFrame[-1] -ge $dotnetversion -or $supportFrame[0] -le $dotnetversion) { | |
| $dotnetversion = if ($dotnethost.local.dotnet) {$listlocal[0]} else {$mindn} | |
| } | |
| $pssdk = if ($dotnethost.local.psver) {$dotnethost.local.psver} else {$minps} | |
| if ($pssdk -notmatch '\*') { | |
| $pssdk = '{0}.{1}.*' -f $pssdk.split('.')[0,1] | |
| } | |
| $dotnetversion,$pssdk | |
| } # END Resolve-DotnetRequirement | |
| function Install-Dotnet ([switch]$force) { | |
| # https://github.com/actions/setup-dotnet | |
| if (-not $force -and | |
| $null -ne (Get-Command "dotnet.exe" -ErrorAction SilentlyContinue) -and | |
| $(& dotnet.exe --version) -and $LASTEXITCODE -eq 0) { | |
| Write-Host ".NET Core SDK already installed"; $null | |
| } | |
| else { | |
| $DotNetInstallFile = "$env:TEMP\dotnet-install.ps1" | |
| $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" | |
| $p = [Net.ServicePointManager]::SecurityProtocol | |
| [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 | |
| try { | |
| [System.Net.WebClient]::new().DownloadFile($DotNetInstallUrl, $DotNetInstallFile) | |
| } catch {} | |
| [Net.ServicePointManager]::SecurityProtocol = $p | |
| if (Test-Path $DotNetInstallFile) { | |
| $dnpath = "$env:ProgramFiles\dotnet" | |
| & powershell.exe -Nologo -ExecutionPolicy bypass -File $DotNetInstallFile -InstallDir $dnpath | |
| if ($LASTEXITCODE) {$LASTEXITCODE} else { | |
| if ($env:PATH -notmatch "$([regex]::Escape($dnpath))") { | |
| [System.Environment]::SetEnvironmentVariable('PATH',"$env:PATH;$dnpath", 'Machine') | |
| Write-Host ".NET Core SDK version: $(& dotnet.exe --version)" | |
| }; $true | |
| } | |
| } else { | |
| Write-Warning "Error downloading installation script. Try again later or download and install .NET SDK manually from https://dotnet.microsoft.com/en-us/download"; $false | |
| } | |
| } | |
| } # END Install-Dotnet | |
| function Set-CSProject ([string]$csProjPath, [string]$csProjSource) { | |
| # https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#targetframeworks | |
| # https://learn.microsoft.com/en-us/dotnet/standard/frameworks | |
| # https://learn.microsoft.com/en-us/dotnet/core/deploying/single-file/overview?tabs=cli#native-libraries | |
| if ($constants) {$constants = "<DefineConstants>$constants</DefineConstants>"} | |
| if ($appIcon) {$appIcon = "<ApplicationIcon>$appIcon</ApplicationIcon>"} | |
| if ($manifestFileName) {$manifestFileName = "<ApplicationManifest>$manifestFileName</ApplicationManifest>"} | |
| #$includenativelibs,$includeallcontent = if ($includenativelibs) { | |
| # "<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>", | |
| # "<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>" | |
| #} else {'',''} | |
| $csproj = [xml]@" | |
| <Project Sdk="Microsoft.NET.Sdk"> | |
| <PropertyGroup> | |
| <OutputType>$outputType</OutputType> | |
| <TargetFramework>net${dotnetversion}$dotnetOS</TargetFramework> | |
| <!--does not work <TargetFrameworks>net10.0;net9.0;net8.0</TargetFrameworks>--> | |
| <RuntimeIdentifier>$targetOS-$platform</RuntimeIdentifier> | |
| <LangVersion>latest</LangVersion> | |
| <Nullable>enable</Nullable> | |
| <PublishSingleFile>True</PublishSingleFile> | |
| <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract> | |
| <IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract> | |
| <SelfContained>$selfContained</SelfContained> | |
| <UseWindowsForms>$useWindowsForms</UseWindowsForms> | |
| <UseWPF>$useWindowsForms</UseWPF> | |
| <PublishTrimmed>$trimmed</PublishTrimmed> | |
| <!--<TrimMode></TrimMode>--> | |
| <PublishReadyToRun>$readyToRun</PublishReadyToRun> | |
| <PublishAot>$aot</PublishAot> | |
| <InvariantGlobalization>False</InvariantGlobalization> | |
| <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> | |
| <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> | |
| <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> | |
| <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute> | |
| <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute> | |
| <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute> | |
| <StartupObject>PSRunner.MainApp</StartupObject> | |
| <SatelliteResourceLanguages>en-US;en</SatelliteResourceLanguages> | |
| $constants | |
| $appIcon | |
| $manifestFileName | |
| </PropertyGroup> | |
| <ItemGroup> | |
| <PackageReference Include="Microsoft.PowerShell.SDK" Version="$pssdk"/> | |
| </ItemGroup> | |
| <!-- Preserve PowerShell reflection targets from trimming | |
| <ItemGroup Condition="'$trimmed' == 'True'"> | |
| <TrimmerRootAssembly Include="System.Management.Automation"/> | |
| <TrimmerRootAssembly Include="Microsoft.PowerShell.Commands.Diagnostics"/> | |
| <TrimmerRootAssembly Include="Microsoft.PowerShell.Commands.Management"/> | |
| <TrimmerRootAssembly Include="Microsoft.PowerShell.Commands.Utility"/> | |
| <TrimmerRootAssembly Include="Microsoft.PowerShell.ConsoleHost"/> | |
| <TrimmerRootAssembly Include="Microsoft.PowerShell.Security"/> | |
| <TrimmerRootAssembly Include="Microsoft.WSMan.Management"/> | |
| </ItemGroup> --> | |
| <!-- If AOT compilation enabled and .NETSDK > 8, include System.Formats.Nrbf | |
| <ItemGroup Condition="'$aot' == 'True' And `$([MSBuild]::GetTargetFrameworkIdentifier('`$(TargetFramework)')) == '.NETCoreApp' And `$([MSBuild]::GetTargetFrameworkVersion('`$(TargetFramework)')) >= '8.0'"> | |
| <PackageReference Include="System.Formats.Nrbf" Version="10.*"/> | |
| </ItemGroup> --> | |
| </Project> | |
| "@ | |
| # Idempotency: write changes only, spend time/memory/CPU to save disk | |
| Write-Host 'Checking for input changes...' | |
| $cssource = Get-MainProgramSource | |
| $hashPath = "$env:TEMP\ps2exe.hash" | |
| $teststring = "$cssource|$useWindowsForms|$appIcon|$outputType|$dotnetversion|$dotnetOS|$targetOS|$platform|$includenativelibs|$selfContained|$trimmed|$readyToRun|$aot|$constants|$manifestFileName" | |
| $newhash = (Get-FileHash -InputStream ([IO.MemoryStream]::new([Text.Encoding]::UTF8.GetBytes($teststring))) -Algorithm SHA256).Hash | |
| ####$newhash = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($teststring))) #.Replace('-', '') | |
| $dirty = if (-not $nocache -and (Test-Path $hashPath) -and (Test-Path $outputFile) -and (Test-Path $csProjPath)) { | |
| $hash = Get-Content $hashPath -Raw | |
| $newhash -ne $hash.trim() | |
| } else {$true} | |
| if ($dirty) { | |
| $newhash | Out-File -FilePath $hashPath -Encoding UTF8 -Force | |
| $csproj.save($csProjPath) | |
| $cssource | Out-File -FilePath $csProjSource -Encoding utf8 | |
| } else { | |
| Write-Warning "No changes found, nothing to do. Use -nocache parameter to omit checking for changes." | |
| exit | |
| } | |
| # Something went wrong | |
| $csProjName = [System.IO.Path]::GetFileNameWithoutExtension($csProjPath) | |
| if (-not (Test-Path $csProjSource,$csProjPath -ErrorAction 0)) { | |
| Write-Warning "Missing CS project file(s). Check it out: '$csProjName.csproj', '$csProjName.cs'. Resolve all issues and try again." | |
| exit | |
| } | |
| # multiple CS files result in conflicts & inexplicable errors | |
| if ((Get-ChildItem "$([System.IO.Path]::GetDirectoryName($csProjPath))\*.cs").count -gt 1) { | |
| Write-Warning "There must be just one CS file: '$csProjName.cs'. Remove non relevant files and try again." | |
| exit | |
| } | |
| } # END Set-CSProject | |
| #endregion PS.Core support | |
| function Measure-Compiler ($start) { | |
| $diff = [datetime]::now - $start | |
| $msfractional = $diff.TotalMilliseconds - [Math]::Truncate($diff.TotalMilliseconds) | |
| $ms = $msfractional + $diff.Milliseconds | |
| if ($diff.Hours) {'{0}h:{1}m:{2}s:{3:N2}ms' -f $diff.Hours,$diff.Minutes,$diff.Seconds,$ms} | |
| elseif ($diff.Minutes) {'{0}m:{1}s:{2:N2}ms' -f $diff.Minutes,$diff.Seconds,$ms} | |
| elseif ($diff.Seconds) {'{0}s:{1:N2}ms' -f $diff.Seconds,$ms} | |
| else {'{0:N2}ms' -f $diff.TotalMilliseconds} | |
| } # END Measure-Compiler | |
| function Measure-Output ($filepath) { | |
| $filesize = (Get-ItemProperty -LiteralPath $filepath -Name Length).Length | |
| $capacity,$unit = if ($filesize/1MB -le 1) {1KB,'KB'} else {1MB,'MB'} | |
| Write-Host "Output file size : $('{0} {1}' -f ([math]::round($filesize/$capacity,1)),$unit)" | |
| } # END Measure-Output | |
| function Get-DefaultIcon { | |
| # NOTE: this is a work around because in Windows Powershell [System.Drawing.Icon] class has a limited functionality | |
| $code = ' | |
| using System; | |
| using System.Drawing; | |
| using System.Runtime.InteropServices; | |
| using System.IO; | |
| public class PngIconConverter | |
| { | |
| public static bool Convert(System.IO.Stream input_stream, System.IO.Stream output_stream, int size, bool keep_aspect_ratio = false) | |
| { | |
| System.Drawing.Bitmap input_bit = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromStream(input_stream); | |
| if (input_bit != null) | |
| { | |
| int width, height; | |
| if (keep_aspect_ratio) | |
| { | |
| width = size; | |
| height = input_bit.Height / input_bit.Width * size; | |
| } | |
| else | |
| { | |
| width = height = size; | |
| } | |
| System.Drawing.Bitmap new_bit = new System.Drawing.Bitmap(input_bit, new System.Drawing.Size(width, height)); | |
| if (new_bit != null) | |
| { | |
| System.IO.MemoryStream mem_data = new System.IO.MemoryStream(); | |
| new_bit.Save(mem_data, System.Drawing.Imaging.ImageFormat.Png); | |
| System.IO.BinaryWriter icon_writer = new System.IO.BinaryWriter(output_stream); | |
| if (output_stream != null && icon_writer != null) | |
| { | |
| icon_writer.Write((byte)0); | |
| icon_writer.Write((byte)0); | |
| icon_writer.Write((short)1); | |
| icon_writer.Write((short)1); | |
| icon_writer.Write((byte)width); | |
| icon_writer.Write((byte)height); | |
| icon_writer.Write((byte)0); | |
| icon_writer.Write((byte)0); | |
| icon_writer.Write((short)0); | |
| icon_writer.Write((short)32); | |
| icon_writer.Write((int)mem_data.Length); | |
| icon_writer.Write((int)(6 + 16)); | |
| icon_writer.Write(mem_data.ToArray()); | |
| icon_writer.Flush(); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| return false; | |
| } | |
| public static bool Convert(string input_image, string output_icon, int size, bool keep_aspect_ratio = false) | |
| { | |
| System.IO.FileStream input_stream = new System.IO.FileStream(input_image, System.IO.FileMode.Open); | |
| System.IO.FileStream output_stream = new System.IO.FileStream(output_icon, System.IO.FileMode.OpenOrCreate); | |
| bool result = Convert(input_stream, output_stream, size, keep_aspect_ratio); | |
| input_stream.Close(); | |
| output_stream.Close(); | |
| return result; | |
| } | |
| }' | |
| try {Add-Type -TypeDefinition $code -ReferencedAssemblies System.Drawing, System.IO -ErrorAction SilentlyContinue} catch {} | |
| $iconsource = 'C:\windows\system32\WindowsPowerShell\v1.0\PowerShell.exe' | |
| $tempiconFile = "$env:TEMP\ps2exedefault.png" | |
| $iconFile = "$env:TEMP\ps2exedefault.ico" | |
| [System.Drawing.Icon]::ExtractAssociatedIcon($iconsource).ToBitMap().Save($tempiconFile) | |
| if ([PngIconConverter]::Convert($tempiconFile,$iconFile,32,$true)) {$iconFile} | |
| Remove-Item $tempiconFile -Force -ErrorAction SilentlyContinue | |
| } # END Get-DefaultIcon | |
| function Get-MainProgramSource { | |
| $sourceScript = Get-SourceContent $inputFile | |
| $embedFileSection = New-EmbedFileSection $embedFiles $compilerparam | |
| $culture = if ($lcid -and $lcid -as [int]) { | |
| @" | |
| System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo($lcid); | |
| System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo($lcid); | |
| "@ | |
| } | |
| # template {{preprocessor}} | |
| $preprocessor = [System.Collections.Generic.List[object]]::new() | |
| # all uppercase? .toupper() | |
| 'noConsole noOutput noError conHost unicode credentialGUI winFormsDpiAware noVisualStyles exitOnCancel title'.split() | ForEach-Object { | |
| if (Get-Variable -Name $_ -ValueOnly -ErrorAction 0) {$preprocessor.Add("#define $_")} | |
| } | |
| if ($core) {$preprocessor.Add("#define psCore")} | |
| if (-not $mta) {$preprocessor.Add("#define STA")} | |
| if (-not $version) {$version = '0.0.0.0'} | |
| <#$metadata = [System.Collections.Generic.List[string]]::new() | |
| if ($title) {$metadata.Add(('[assembly:AssemblyTitle("{0}")]' -f $title))} | |
| if ($copyright) {$metadata.Add(('[assembly:AssemblyCopyright("{0}")]' -f $copyright))} | |
| if ($trademark) {$metadata.Add(('[assembly:AssemblyTrademark("{0}")]' -f $trademark))} | |
| if ($version) { | |
| $metadata.Add(('[assembly:AssemblyVersion("{0}")]' -f $version)) | |
| $metadata.Add(('[assembly:AssemblyFileVersion("{0}")]' -f $version)) | |
| } | |
| if ($description) {$metadata.Add(('[assembly:AssemblyDescription("{0}")]' -f $description))} | |
| if ($company) {$metadata.Add(('[assembly:AssemblyCompany("{0}")]' -f $company))} | |
| $metadata = $metadata -join "`n"#> | |
| # C# source; minimal compression: strip empty lines, leading whitespace, and oneliner comments | |
| # multiline comments: $main -replace '(?m)/\*(.|[\r\n])*?\*/' | |
| @" | |
| // Simple PowerShell host created by Ingo Karstein (http://blog.karstein-consulting.com) | |
| // Reworked and GUI support by Markus Scholtes | |
| // REFACTOR: using preprocessor directives instead of inline PS code; translations from german | |
| // ENHANCEMENT: added source encoding, minifying, and compression | |
| // Support for PS.Core taken from https://github.com/FabienTschanz/PS2EXE.Core | |
| $($preprocessor -join "`n") | |
| using System; | |
| using System.IO; | |
| using System.IO.Compression; | |
| using System.Collections.Generic; | |
| using System.Text; | |
| //using System.Text.RegularExpressions; | |
| using System.Management.Automation; | |
| using System.Management.Automation.Runspaces; | |
| using System.Management.Automation.Host; | |
| using System.Globalization; | |
| using System.Security; | |
| using System.Reflection; | |
| using System.Runtime.InteropServices; | |
| #if noConsole | |
| using System.Windows.Forms; | |
| using System.Drawing; | |
| #endif | |
| #if winFormsDpiAware | |
| using System.Runtime.Versioning; | |
| [assembly:TargetFrameworkAttribute(".NETFramework,Version=v4.7,Profile=Client",FrameworkDisplayName=".NET Framework 4.7")] | |
| #endif | |
| [assembly:AssemblyTitle("$title")] | |
| [assembly:AssemblyProduct("$product")] | |
| [assembly:AssemblyCopyright("$copyright")] | |
| [assembly:AssemblyTrademark("$trademark")] | |
| [assembly:AssemblyVersion("$version")] | |
| [assembly:AssemblyFileVersion("$version")] | |
| // not displayed in details tab of properties dialog, but embedded to file | |
| [assembly:AssemblyDescription("$description")] | |
| [assembly:AssemblyCompany("$company")] | |
| namespace PSRunner | |
| { | |
| #if noConsole || credentialGUI | |
| internal class Credential_Form | |
| { | |
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |
| private struct CREDUI_INFO | |
| { | |
| public int cbSize; | |
| public IntPtr hwndParent; | |
| public string pszMessageText; | |
| public string pszCaptionText; | |
| public IntPtr hbmBanner; | |
| } | |
| [Flags] | |
| enum CREDUI_FLAGS | |
| { | |
| INCORRECT_PASSWORD = 0x1, | |
| DO_NOT_PERSIST = 0x2, | |
| REQUEST_ADMINISTRATOR = 0x4, | |
| EXCLUDE_CERTIFICATES = 0x8, | |
| REQUIRE_CERTIFICATE = 0x10, | |
| SHOW_SAVE_CHECK_BOX = 0x40, | |
| ALWAYS_SHOW_UI = 0x80, | |
| REQUIRE_SMARTCARD = 0x100, | |
| PASSWORD_ONLY_OK = 0x200, | |
| VALIDATE_USERNAME = 0x400, | |
| COMPLETE_USERNAME = 0x800, | |
| PERSIST = 0x1000, | |
| SERVER_CREDENTIAL = 0x4000, | |
| EXPECT_CONFIRMATION = 0x20000, | |
| GENERIC_CREDENTIALS = 0x40000, | |
| USERNAME_TARGET_CREDENTIALS = 0x80000, | |
| KEEP_USERNAME = 0x100000, | |
| } | |
| public enum CredUI_ReturnCodes | |
| { | |
| NO_ERROR = 0, | |
| ERROR_CANCELLED = 1223, | |
| ERROR_NO_SUCH_LOGON_SESSION = 1312, | |
| ERROR_NOT_FOUND = 1168, | |
| ERROR_INVALID_ACCOUNT_NAME = 1315, | |
| ERROR_INSUFFICIENT_BUFFER = 122, | |
| ERROR_INVALID_PARAMETER = 87, | |
| ERROR_INVALID_FLAGS = 1004, | |
| } | |
| [DllImport("credui", CharSet = CharSet.Unicode)] | |
| private static extern CredUI_ReturnCodes CredUIPromptForCredentials(ref CREDUI_INFO credinfo, | |
| string targetName, | |
| IntPtr reserved1, | |
| int iError, | |
| StringBuilder userName, | |
| int maxUserName, | |
| StringBuilder password, | |
| int maxPassword, | |
| [MarshalAs(UnmanagedType.Bool)] ref bool pfSave, | |
| CREDUI_FLAGS flags); | |
| public class User_Pwd | |
| { | |
| public string User = string.Empty; | |
| public string Password = string.Empty; | |
| public string Domain = string.Empty; | |
| } | |
| internal static User_Pwd PromptForPassword(string caption, string message, string target, string user, PSCredentialTypes credTypes, PSCredentialUIOptions options) | |
| { | |
| // Initializing flags and variables | |
| StringBuilder userPassword = new StringBuilder("", 128), userID = new StringBuilder(user, 128); | |
| CREDUI_INFO credUI = new CREDUI_INFO(); | |
| if (!string.IsNullOrEmpty(message)) credUI.pszMessageText = message; | |
| if (!string.IsNullOrEmpty(caption)) credUI.pszCaptionText = caption; | |
| credUI.cbSize = Marshal.SizeOf(credUI); | |
| bool save = false; | |
| CREDUI_FLAGS flags = CREDUI_FLAGS.DO_NOT_PERSIST; | |
| if ((credTypes & PSCredentialTypes.Generic) == PSCredentialTypes.Generic) | |
| { | |
| flags |= CREDUI_FLAGS.GENERIC_CREDENTIALS; | |
| if ((options & PSCredentialUIOptions.AlwaysPrompt) == PSCredentialUIOptions.AlwaysPrompt) | |
| { | |
| flags |= CREDUI_FLAGS.ALWAYS_SHOW_UI; | |
| } | |
| } | |
| // prompt the user for their password, graphical prompt | |
| CredUI_ReturnCodes returnCode = CredUIPromptForCredentials(ref credUI, target, IntPtr.Zero, 0, userID, 128, userPassword, 128, ref save, flags); | |
| if (returnCode == CredUI_ReturnCodes.NO_ERROR) | |
| { | |
| User_Pwd ret = new User_Pwd(); | |
| ret.User = userID.ToString(); | |
| ret.Password = userPassword.ToString(); | |
| ret.Domain = ""; | |
| return ret; | |
| } | |
| return null; | |
| } | |
| } | |
| #endif | |
| internal class MainModuleRawUI : PSHostRawUserInterface | |
| { | |
| #if noConsole | |
| // Storage console colors in GUI output is read and set, but not currently used (for future use) | |
| private ConsoleColor GUIBackgroundColor = ConsoleColor.White; | |
| private ConsoleColor GUIForegroundColor = ConsoleColor.Black; | |
| #if !title | |
| private string GUITitle = System.AppDomain.CurrentDomain.FriendlyName; | |
| #else | |
| private string GUITitle = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyTitleAttribute>().Title; | |
| #endif | |
| #else | |
| const int STD_OUTPUT_HANDLE = -11; | |
| //CHAR_INFO struct, which was a union in the old days | |
| // so we want to use LayoutKind.Explicit to mimic it as closely as we can | |
| [StructLayout(LayoutKind.Explicit)] | |
| public struct CHAR_INFO | |
| { | |
| [FieldOffset(0)] | |
| internal char UnicodeChar; | |
| [FieldOffset(0)] | |
| internal char AsciiChar; | |
| [FieldOffset(2)] //2 bytes seems to work properly | |
| internal UInt16 Attributes; | |
| } | |
| //COORD struct | |
| [StructLayout(LayoutKind.Sequential)] | |
| public struct COORD | |
| { | |
| public short X; | |
| public short Y; | |
| } | |
| //SMALL_RECT struct | |
| [StructLayout(LayoutKind.Sequential)] | |
| public struct SMALL_RECT | |
| { | |
| public short Left; | |
| public short Top; | |
| public short Right; | |
| public short Bottom; | |
| } | |
| // Reads character and color attribute data from a rectangular block of character cells in a console screen buffer, and the function writes the data to a rectangular block at a specified location in the destination buffer. | |
| [DllImport("kernel32.dll", EntryPoint = "ReadConsoleOutputW", CharSet = CharSet.Unicode, SetLastError = true)] | |
| internal static extern bool ReadConsoleOutput( | |
| IntPtr hConsoleOutput, | |
| // This pointer is treated as the origin of a two-dimensional array of CHAR_INFO structures whose size is specified by the dwBufferSize parameter. | |
| [MarshalAs(UnmanagedType.LPArray), Out] CHAR_INFO[,] lpBuffer, | |
| COORD dwBufferSize, | |
| COORD dwBufferCoord, | |
| ref SMALL_RECT lpReadRegion); | |
| // Writes character and color attribute data to a specified rectangular block of character cells in a console screen buffer. | |
| // The data to be written is taken from a correspondingly sized rectangular block at a specified location in the source buffer | |
| [DllImport("kernel32.dll", EntryPoint = "WriteConsoleOutputW", CharSet = CharSet.Unicode, SetLastError = true)] | |
| internal static extern bool WriteConsoleOutput( | |
| IntPtr hConsoleOutput, | |
| // This pointer is treated as the origin of a two-dimensional array of CHAR_INFO structures whose size is specified by the dwBufferSize parameter. | |
| [MarshalAs(UnmanagedType.LPArray), In] CHAR_INFO[,] lpBuffer, | |
| COORD dwBufferSize, | |
| COORD dwBufferCoord, | |
| ref SMALL_RECT lpWriteRegion); | |
| // Moves a block of data in a screen buffer. The effects of the move can be limited by specifying a clipping rectangle, so the contents of the console screen buffer outside the clipping rectangle are unchanged. | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| static extern bool ScrollConsoleScreenBuffer( | |
| IntPtr hConsoleOutput, | |
| [In] ref SMALL_RECT lpScrollRectangle, | |
| [In] ref SMALL_RECT lpClipRectangle, | |
| COORD dwDestinationOrigin, | |
| [In] ref CHAR_INFO lpFill); | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| static extern IntPtr GetStdHandle(int nStdHandle); | |
| #endif | |
| public override ConsoleColor BackgroundColor | |
| { | |
| get | |
| { | |
| #if noConsole | |
| return GUIBackgroundColor; | |
| #else | |
| return Console.BackgroundColor; | |
| #endif | |
| } | |
| set | |
| { | |
| #if noConsole | |
| GUIBackgroundColor = value; | |
| #else | |
| Console.BackgroundColor = value; | |
| #endif | |
| } | |
| } | |
| public override System.Management.Automation.Host.Size BufferSize | |
| { | |
| get | |
| { | |
| #if !noConsole | |
| if (Console.IsOutputRedirected) | |
| // return default value for redirection. If no valid value is returned WriteLine will not be called | |
| return new System.Management.Automation.Host.Size(120, 50); | |
| else | |
| return new System.Management.Automation.Host.Size(Console.BufferWidth, Console.BufferHeight); | |
| #else | |
| // return default value for Winforms. If no valid value is returned WriteLine will not be called | |
| return new System.Management.Automation.Host.Size(120, 50); | |
| #endif | |
| } | |
| set | |
| { | |
| #if !noConsole | |
| Console.BufferWidth = value.Width; | |
| Console.BufferHeight = value.Height; | |
| #endif | |
| } | |
| } | |
| public override Coordinates CursorPosition | |
| { | |
| get | |
| { | |
| #if !noConsole | |
| return new Coordinates(Console.CursorLeft, Console.CursorTop); | |
| #else | |
| // Return dummy value for Winforms. | |
| return new Coordinates(0, 0); | |
| #endif | |
| } | |
| set | |
| { | |
| #if !noConsole | |
| Console.CursorTop = value.Y; | |
| Console.CursorLeft = value.X; | |
| #endif | |
| } | |
| } | |
| public override int CursorSize | |
| { | |
| get | |
| { | |
| #if !noConsole | |
| return Console.CursorSize; | |
| #else | |
| // Return dummy value for Winforms. | |
| return 25; | |
| #endif | |
| } | |
| set | |
| { | |
| #if !noConsole | |
| Console.CursorSize = value; | |
| #endif | |
| } | |
| } | |
| #if noConsole | |
| private Form Invisible_Form = null; | |
| #endif | |
| public override void FlushInputBuffer() | |
| { | |
| #if !noConsole | |
| if (!Console.IsInputRedirected) | |
| { while (Console.KeyAvailable) Console.ReadKey(true); } | |
| #else | |
| if (Invisible_Form != null) | |
| { | |
| Invisible_Form.Close(); | |
| Invisible_Form = null; | |
| } | |
| else | |
| { | |
| Invisible_Form = new Form(); | |
| Invisible_Form.Opacity = 0; | |
| Invisible_Form.ShowInTaskbar = false; | |
| Invisible_Form.Visible = true; | |
| } | |
| #endif | |
| } | |
| public override ConsoleColor ForegroundColor | |
| { | |
| get | |
| { | |
| #if noConsole | |
| return GUIForegroundColor; | |
| #else | |
| return Console.ForegroundColor; | |
| #endif | |
| } | |
| set | |
| { | |
| #if noConsole | |
| GUIForegroundColor = value; | |
| #else | |
| Console.ForegroundColor = value; | |
| #endif | |
| } | |
| } | |
| public override BufferCell[,] GetBufferContents(System.Management.Automation.Host.Rectangle rectangle) | |
| { | |
| #if !noConsole | |
| IntPtr hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); | |
| CHAR_INFO[,] buffer = new CHAR_INFO[rectangle.Bottom - rectangle.Top + 1, rectangle.Right - rectangle.Left + 1]; | |
| COORD buffer_size = new COORD() {X = (short)(rectangle.Right - rectangle.Left + 1), Y = (short)(rectangle.Bottom - rectangle.Top + 1)}; | |
| COORD buffer_index = new COORD() {X = 0, Y = 0}; | |
| SMALL_RECT screen_rect = new SMALL_RECT() {Left = (short)rectangle.Left, Top = (short)rectangle.Top, Right = (short)rectangle.Right, Bottom = (short)rectangle.Bottom}; | |
| ReadConsoleOutput(hStdOut, buffer, buffer_size, buffer_index, ref screen_rect); | |
| System.Management.Automation.Host.BufferCell[,] ScreenBuffer = new System.Management.Automation.Host.BufferCell[rectangle.Bottom - rectangle.Top + 1, rectangle.Right - rectangle.Left + 1]; | |
| for (int y = 0; y <= rectangle.Bottom - rectangle.Top; y++) | |
| for (int x = 0; x <= rectangle.Right - rectangle.Left; x++) | |
| { | |
| ScreenBuffer[y,x] = new System.Management.Automation.Host.BufferCell(buffer[y,x].AsciiChar, (System.ConsoleColor)(buffer[y,x].Attributes & 0xF), (System.ConsoleColor)((buffer[y,x].Attributes & 0xF0) / 0x10), System.Management.Automation.Host.BufferCellType.Complete); | |
| } | |
| return ScreenBuffer; | |
| #else | |
| System.Management.Automation.Host.BufferCell[,] ScreenBuffer = new System.Management.Automation.Host.BufferCell[rectangle.Bottom - rectangle.Top + 1, rectangle.Right - rectangle.Left + 1]; | |
| for (int y = 0; y <= rectangle.Bottom - rectangle.Top; y++) | |
| for (int x = 0; x <= rectangle.Right - rectangle.Left; x++) | |
| { | |
| ScreenBuffer[y,x] = new System.Management.Automation.Host.BufferCell(' ', GUIForegroundColor, GUIBackgroundColor, System.Management.Automation.Host.BufferCellType.Complete); | |
| } | |
| return ScreenBuffer; | |
| #endif | |
| } | |
| public override bool KeyAvailable | |
| { | |
| get | |
| { | |
| #if !noConsole | |
| return Console.KeyAvailable; | |
| #else | |
| return true; | |
| #endif | |
| } | |
| } | |
| public override System.Management.Automation.Host.Size MaxPhysicalWindowSize | |
| { | |
| get | |
| { | |
| #if !noConsole | |
| return new System.Management.Automation.Host.Size(Console.LargestWindowWidth, Console.LargestWindowHeight); | |
| #else | |
| // Dummy value for Winforms | |
| return new System.Management.Automation.Host.Size(240, 84); | |
| #endif | |
| } | |
| } | |
| public override System.Management.Automation.Host.Size MaxWindowSize | |
| { | |
| get | |
| { | |
| #if !noConsole | |
| return new System.Management.Automation.Host.Size(Console.BufferWidth, Console.BufferWidth); | |
| #else | |
| // Dummy value for Winforms | |
| return new System.Management.Automation.Host.Size(120, 84); | |
| #endif | |
| } | |
| } | |
| public override KeyInfo ReadKey(ReadKeyOptions options) | |
| { | |
| #if !noConsole | |
| ConsoleKeyInfo cki = Console.ReadKey((options & ReadKeyOptions.NoEcho)!=0); | |
| ControlKeyStates cks = 0; | |
| if ((cki.Modifiers & ConsoleModifiers.Alt) != 0) | |
| cks |= ControlKeyStates.LeftAltPressed | ControlKeyStates.RightAltPressed; | |
| if ((cki.Modifiers & ConsoleModifiers.Control) != 0) | |
| cks |= ControlKeyStates.LeftCtrlPressed | ControlKeyStates.RightCtrlPressed; | |
| if ((cki.Modifiers & ConsoleModifiers.Shift) != 0) | |
| cks |= ControlKeyStates.ShiftPressed; | |
| if (Console.CapsLock) | |
| cks |= ControlKeyStates.CapsLockOn; | |
| if (Console.NumberLock) | |
| cks |= ControlKeyStates.NumLockOn; | |
| return new KeyInfo((int)cki.Key, cki.KeyChar, cks, (options & ReadKeyOptions.IncludeKeyDown)!=0); | |
| #else | |
| if ((options & ReadKeyOptions.IncludeKeyDown)!=0) | |
| return ReadKey_Box.Show(WindowTitle, "", true); | |
| else | |
| return ReadKey_Box.Show(WindowTitle, "", false); | |
| #endif | |
| } | |
| public override void ScrollBufferContents(System.Management.Automation.Host.Rectangle source, Coordinates destination, System.Management.Automation.Host.Rectangle clip, BufferCell fill) | |
| { // no destination block clipping implemented | |
| #if !noConsole | |
| // clip area out of source range? | |
| if ((source.Left > clip.Right) || (source.Right < clip.Left) || (source.Top > clip.Bottom) || (source.Bottom < clip.Top)) | |
| { // clipping out of range -> nothing to do | |
| return; | |
| } | |
| IntPtr hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); | |
| SMALL_RECT lpScrollRectangle = new SMALL_RECT() {Left = (short)source.Left, Top = (short)source.Top, Right = (short)(source.Right), Bottom = (short)(source.Bottom)}; | |
| SMALL_RECT lpClipRectangle; | |
| if (clip != null) | |
| { lpClipRectangle = new SMALL_RECT() {Left = (short)clip.Left, Top = (short)clip.Top, Right = (short)(clip.Right), Bottom = (short)(clip.Bottom)}; } | |
| else | |
| { lpClipRectangle = new SMALL_RECT() {Left = (short)0, Top = (short)0, Right = (short)(Console.WindowWidth - 1), Bottom = (short)(Console.WindowHeight - 1)}; } | |
| COORD dwDestinationOrigin = new COORD() {X = (short)(destination.X), Y = (short)(destination.Y)}; | |
| CHAR_INFO lpFill = new CHAR_INFO() { AsciiChar = fill.Character, Attributes = (ushort)((int)(fill.ForegroundColor) + (int)(fill.BackgroundColor)*16) }; | |
| ScrollConsoleScreenBuffer(hStdOut, ref lpScrollRectangle, ref lpClipRectangle, dwDestinationOrigin, ref lpFill); | |
| #endif | |
| } | |
| public override void SetBufferContents(System.Management.Automation.Host.Rectangle rectangle, BufferCell fill) | |
| { | |
| #if !noConsole | |
| // using a trick: move the buffer out of the screen, the source area gets filled with the char fill.Character | |
| if (rectangle.Left >= 0) | |
| Console.MoveBufferArea(rectangle.Left, rectangle.Top, rectangle.Right-rectangle.Left+1, rectangle.Bottom-rectangle.Top+1, BufferSize.Width, BufferSize.Height, fill.Character, fill.ForegroundColor, fill.BackgroundColor); | |
| else | |
| { // Clear-Host: move all content off the screen | |
| Console.MoveBufferArea(0, 0, BufferSize.Width, BufferSize.Height, BufferSize.Width, BufferSize.Height, fill.Character, fill.ForegroundColor, fill.BackgroundColor); | |
| } | |
| #endif | |
| } | |
| public override void SetBufferContents(Coordinates origin, BufferCell[,] contents) | |
| { | |
| #if !noConsole | |
| IntPtr hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); | |
| CHAR_INFO[,] buffer = new CHAR_INFO[contents.GetLength(0), contents.GetLength(1)]; | |
| COORD buffer_size = new COORD() {X = (short)(contents.GetLength(1)), Y = (short)(contents.GetLength(0))}; | |
| COORD buffer_index = new COORD() {X = 0, Y = 0}; | |
| SMALL_RECT screen_rect = new SMALL_RECT() {Left = (short)origin.X, Top = (short)origin.Y, Right = (short)(origin.X + contents.GetLength(1) - 1), Bottom = (short)(origin.Y + contents.GetLength(0) - 1)}; | |
| for (int y = 0; y < contents.GetLength(0); y++) | |
| for (int x = 0; x < contents.GetLength(1); x++) | |
| { | |
| buffer[y,x] = new CHAR_INFO() { AsciiChar = contents[y,x].Character, Attributes = (ushort)((int)(contents[y,x].ForegroundColor) + (int)(contents[y,x].BackgroundColor)*16) }; | |
| } | |
| WriteConsoleOutput(hStdOut, buffer, buffer_size, buffer_index, ref screen_rect); | |
| #endif | |
| } | |
| public override Coordinates WindowPosition | |
| { | |
| get | |
| { | |
| Coordinates s = new Coordinates(); | |
| #if !noConsole | |
| s.X = Console.WindowLeft; | |
| s.Y = Console.WindowTop; | |
| #else | |
| // Dummy value for Winforms | |
| s.X = 0; | |
| s.Y = 0; | |
| #endif | |
| return s; | |
| } | |
| set | |
| { | |
| #if !noConsole | |
| Console.WindowLeft = value.X; | |
| Console.WindowTop = value.Y; | |
| #endif | |
| } | |
| } | |
| public override System.Management.Automation.Host.Size WindowSize | |
| { | |
| get | |
| { | |
| System.Management.Automation.Host.Size s = new System.Management.Automation.Host.Size(); | |
| #if !noConsole | |
| s.Height = Console.WindowHeight; | |
| s.Width = Console.WindowWidth; | |
| #else | |
| // Dummy value for Winforms | |
| s.Height = 50; | |
| s.Width = 120; | |
| #endif | |
| return s; | |
| } | |
| set | |
| { | |
| #if !noConsole | |
| Console.WindowWidth = value.Width; | |
| Console.WindowHeight = value.Height; | |
| #endif | |
| } | |
| } | |
| public override string WindowTitle | |
| { | |
| get | |
| { | |
| #if !noConsole | |
| return Console.Title; | |
| #else | |
| return GUITitle; | |
| #endif | |
| } | |
| set | |
| { | |
| #if !noConsole | |
| Console.Title = value; | |
| #else | |
| GUITitle = value; | |
| #endif | |
| } | |
| } | |
| } | |
| #if noConsole | |
| public class Input_Box | |
| { | |
| [DllImport("user32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] | |
| private static extern IntPtr MB_GetString(uint strId); | |
| public static DialogResult Show(string strTitle, string strPrompt, ref string strVal, bool blSecure) | |
| { | |
| // Generate controls | |
| Form form = new Form(); | |
| form.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); | |
| form.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | |
| Label label = new Label(); | |
| TextBox textBox = new TextBox(); | |
| Button buttonOk = new Button(); | |
| Button buttonCancel = new Button(); | |
| // Sizes and positions are defined according to the label | |
| // This control has to be finished first | |
| if (string.IsNullOrEmpty(strPrompt)) | |
| { | |
| if (blSecure) | |
| label.Text = "Secure input: "; | |
| else | |
| label.Text = "Input: "; | |
| } | |
| else | |
| label.Text = strPrompt; | |
| label.Location = new Point(9, 19); | |
| label.MaximumSize = new System.Drawing.Size(System.Windows.Forms.Screen.FromControl(form).Bounds.Width*5/8 - 18, 0); | |
| label.AutoSize = true; | |
| // Size of the label is defined not before Add() | |
| form.Controls.Add(label); | |
| // Generate textbox | |
| if (blSecure) textBox.UseSystemPasswordChar = true; | |
| textBox.Text = strVal; | |
| textBox.SetBounds(12, label.Bottom, label.Right - 12, 20); | |
| // Generate buttons | |
| // get localized "OK"-string | |
| string sTextOK = Marshal.PtrToStringUni(MB_GetString(0)); | |
| if (string.IsNullOrEmpty(sTextOK)) | |
| buttonOk.Text = "OK"; | |
| else | |
| buttonOk.Text = sTextOK; | |
| // get localized "Cancel"-string | |
| string sTextCancel = Marshal.PtrToStringUni(MB_GetString(1)); | |
| if (string.IsNullOrEmpty(sTextCancel)) | |
| buttonCancel.Text = "Cancel"; | |
| else | |
| buttonCancel.Text = sTextCancel; | |
| buttonOk.DialogResult = DialogResult.OK; | |
| buttonCancel.DialogResult = DialogResult.Cancel; | |
| buttonOk.SetBounds(System.Math.Max(12, label.Right - 158), label.Bottom + 36, 75, 23); | |
| buttonCancel.SetBounds(System.Math.Max(93, label.Right - 77), label.Bottom + 36, 75, 23); | |
| // Configure form | |
| form.Text = strTitle; | |
| form.ClientSize = new System.Drawing.Size(System.Math.Max(178, label.Right + 10), label.Bottom + 71); | |
| form.Controls.AddRange(new Control[] { textBox, buttonOk, buttonCancel }); | |
| form.FormBorderStyle = FormBorderStyle.FixedDialog; | |
| form.StartPosition = FormStartPosition.CenterScreen; | |
| try { | |
| form.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location); | |
| } | |
| catch { } | |
| form.MinimizeBox = false; | |
| form.MaximizeBox = false; | |
| form.AcceptButton = buttonOk; | |
| form.CancelButton = buttonCancel; | |
| // Show form and compute results | |
| DialogResult dialogResult = form.ShowDialog(); | |
| strVal = textBox.Text; | |
| return dialogResult; | |
| } | |
| public static DialogResult Show(string strTitle, string strPrompt, ref string strVal) | |
| { | |
| return Show(strTitle, strPrompt, ref strVal, false); | |
| } | |
| } | |
| public class Choice_Box | |
| { | |
| public static int Show(System.Collections.ObjectModel.Collection<ChoiceDescription> arrChoice, int intDefault, string strTitle, string strPrompt) | |
| { | |
| // cancel if array is empty | |
| if (arrChoice == null) return -1; | |
| if (arrChoice.Count < 1) return -1; | |
| // Generate controls | |
| Form form = new Form(); | |
| form.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); | |
| form.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | |
| RadioButton[] aradioButton = new RadioButton[arrChoice.Count]; | |
| ToolTip toolTip = new ToolTip(); | |
| Button buttonOk = new Button(); | |
| // Sizes and positions are defined according to the label | |
| // This control has to be finished first when a prompt is available | |
| int iPosY = 19, iMaxX = 0; | |
| if (!string.IsNullOrEmpty(strPrompt)) | |
| { | |
| Label label = new Label(); | |
| label.Text = strPrompt; | |
| label.Location = new Point(9, 19); | |
| label.MaximumSize = new System.Drawing.Size(System.Windows.Forms.Screen.FromControl(form).Bounds.Width*5/8 - 18, 0); | |
| label.AutoSize = true; | |
| // The size of the label is only determined using the Add() method | |
| form.Controls.Add(label); | |
| iPosY = label.Bottom; | |
| iMaxX = label.Right; | |
| } | |
| // The other sizes and positions are based on the radio buttons | |
| // Now let's finish these controls | |
| int Counter = 0; | |
| int tempWidth = System.Windows.Forms.Screen.FromControl(form).Bounds.Width*5/8 - 18; | |
| foreach (ChoiceDescription sAuswahl in arrChoice) | |
| { | |
| aradioButton[Counter] = new RadioButton(); | |
| aradioButton[Counter].Text = sAuswahl.Label; | |
| if (Counter == intDefault) aradioButton[Counter].Checked = true; | |
| aradioButton[Counter].Location = new Point(9, iPosY); | |
| aradioButton[Counter].AutoSize = true; | |
| // The size of the label is only determined using the Add() method | |
| form.Controls.Add(aradioButton[Counter]); | |
| if (aradioButton[Counter].Width > tempWidth) | |
| { // radio field to wide for screen -> make two lines | |
| int tempHeight = aradioButton[Counter].Height; | |
| aradioButton[Counter].Height = tempHeight*(1 + (aradioButton[Counter].Width-1)/tempWidth); | |
| aradioButton[Counter].Width = tempWidth; | |
| aradioButton[Counter].AutoSize = false; | |
| } | |
| iPosY = aradioButton[Counter].Bottom; | |
| if (aradioButton[Counter].Right > iMaxX) { iMaxX = aradioButton[Counter].Right; } | |
| if (!string.IsNullOrEmpty(sAuswahl.HelpMessage)) | |
| toolTip.SetToolTip(aradioButton[Counter], sAuswahl.HelpMessage); | |
| Counter++; | |
| } | |
| // Show tooltip even when parent window is inactive | |
| toolTip.ShowAlways = true; | |
| // Create button | |
| buttonOk.Text = "OK"; | |
| buttonOk.DialogResult = DialogResult.OK; | |
| buttonOk.SetBounds(System.Math.Max(12, iMaxX - 77), iPosY + 36, 75, 23); | |
| // configure form | |
| if (string.IsNullOrEmpty(strTitle)) | |
| form.Text = System.AppDomain.CurrentDomain.FriendlyName; | |
| else | |
| form.Text = strTitle; | |
| form.ClientSize = new System.Drawing.Size(System.Math.Max(178, iMaxX + 10), iPosY + 71); | |
| form.Controls.Add(buttonOk); | |
| form.FormBorderStyle = FormBorderStyle.FixedDialog; | |
| form.StartPosition = FormStartPosition.CenterScreen; | |
| try { | |
| form.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location); | |
| } | |
| catch { } | |
| form.MinimizeBox = false; | |
| form.MaximizeBox = false; | |
| form.AcceptButton = buttonOk; | |
| // show and compute form | |
| if (form.ShowDialog() == DialogResult.OK) | |
| { int iRueck = -1; | |
| for (Counter = 0; Counter < arrChoice.Count; Counter++) | |
| { | |
| if (aradioButton[Counter].Checked == true) { iRueck = Counter; } | |
| } | |
| return iRueck; | |
| } | |
| else | |
| return -1; | |
| } | |
| } | |
| public class ReadKey_Box | |
| { | |
| [DllImport("user32.dll")] | |
| public static extern int ToUnicode(uint wVirtKey, uint wScanCode, byte[] lpKeyState, | |
| [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags); | |
| static string GetCharFromKeys(Keys keys, bool blShift, bool blAltGr) | |
| { | |
| System.Text.StringBuilder buffer = new System.Text.StringBuilder(64); | |
| byte[] keyboardState = new byte[256]; | |
| if (blShift) | |
| { keyboardState[(int) Keys.ShiftKey] = 0xff; } | |
| if (blAltGr) | |
| { keyboardState[(int) Keys.ControlKey] = 0xff; | |
| keyboardState[(int) Keys.Menu] = 0xff; | |
| } | |
| if (ToUnicode((uint) keys, 0, keyboardState, buffer, 64, 0) >= 1) | |
| return buffer.ToString(); | |
| else | |
| return "\0"; | |
| } | |
| class Keyboard_Form : Form | |
| { | |
| public Keyboard_Form() | |
| { | |
| this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); | |
| this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | |
| this.KeyDown += new KeyEventHandler(Keyboard_Form_KeyDown); | |
| this.KeyUp += new KeyEventHandler(Keyboard_Form_KeyUp); | |
| } | |
| // check for KeyDown or KeyUp? | |
| public bool checkKeyDown = true; | |
| // key code for pressed key | |
| public KeyInfo keyinfo; | |
| void Keyboard_Form_KeyDown(object sender, KeyEventArgs e) | |
| { | |
| if (checkKeyDown) | |
| { // store key info | |
| keyinfo.VirtualKeyCode = e.KeyValue; | |
| keyinfo.Character = GetCharFromKeys(e.KeyCode, e.Shift, e.Alt & e.Control)[0]; | |
| keyinfo.KeyDown = false; | |
| keyinfo.ControlKeyState = 0; | |
| if (e.Alt) { keyinfo.ControlKeyState = ControlKeyStates.LeftAltPressed | ControlKeyStates.RightAltPressed; } | |
| if (e.Control) | |
| { keyinfo.ControlKeyState |= ControlKeyStates.LeftCtrlPressed | ControlKeyStates.RightCtrlPressed; | |
| if (!e.Alt) { if (e.KeyValue > 64 && e.KeyValue < 96) keyinfo.Character = (char)(e.KeyValue - 64); } | |
| } | |
| if (e.Shift) { keyinfo.ControlKeyState |= ControlKeyStates.ShiftPressed; } | |
| if ((e.Modifiers & System.Windows.Forms.Keys.CapsLock) > 0) { keyinfo.ControlKeyState |= ControlKeyStates.CapsLockOn; } | |
| if ((e.Modifiers & System.Windows.Forms.Keys.NumLock) > 0) { keyinfo.ControlKeyState |= ControlKeyStates.NumLockOn; } | |
| // and close the form | |
| this.Close(); | |
| } | |
| } | |
| void Keyboard_Form_KeyUp(object sender, KeyEventArgs e) | |
| { | |
| if (!checkKeyDown) | |
| { // store key info | |
| keyinfo.VirtualKeyCode = e.KeyValue; | |
| keyinfo.Character = GetCharFromKeys(e.KeyCode, e.Shift, e.Alt & e.Control)[0]; | |
| keyinfo.KeyDown = true; | |
| keyinfo.ControlKeyState = 0; | |
| if (e.Alt) { keyinfo.ControlKeyState = ControlKeyStates.LeftAltPressed | ControlKeyStates.RightAltPressed; } | |
| if (e.Control) | |
| { keyinfo.ControlKeyState |= ControlKeyStates.LeftCtrlPressed | ControlKeyStates.RightCtrlPressed; | |
| if (!e.Alt) | |
| { if (e.KeyValue > 64 && e.KeyValue < 96) keyinfo.Character = (char)(e.KeyValue - 64); } | |
| } | |
| if (e.Shift) { keyinfo.ControlKeyState |= ControlKeyStates.ShiftPressed; } | |
| if ((e.Modifiers & System.Windows.Forms.Keys.CapsLock) > 0) { keyinfo.ControlKeyState |= ControlKeyStates.CapsLockOn; } | |
| if ((e.Modifiers & System.Windows.Forms.Keys.NumLock) > 0) { keyinfo.ControlKeyState |= ControlKeyStates.NumLockOn; } | |
| // and close the form | |
| this.Close(); | |
| } | |
| } | |
| } | |
| public static KeyInfo Show(string strTitle, string strPrompt, bool blIncludeKeyDown) | |
| { | |
| // Create controls | |
| Keyboard_Form form = new Keyboard_Form(); | |
| Label label = new Label(); | |
| // The label determines the sizes and positions | |
| // So finish this control first | |
| if (string.IsNullOrEmpty(strPrompt)) | |
| label.Text = "Press a key"; | |
| else | |
| label.Text = strPrompt; | |
| label.Location = new Point(9, 19); | |
| label.MaximumSize = new System.Drawing.Size(System.Windows.Forms.Screen.FromControl(form).Bounds.Width*5/8 - 18, 0); | |
| label.AutoSize = true; | |
| // The size of the label is only determined using the Add() method | |
| form.Controls.Add(label); | |
| // configure form | |
| form.Text = strTitle; | |
| form.ClientSize = new System.Drawing.Size(System.Math.Max(178, label.Right + 10), label.Bottom + 55); | |
| form.FormBorderStyle = FormBorderStyle.FixedDialog; | |
| form.StartPosition = FormStartPosition.CenterScreen; | |
| try { | |
| form.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location); | |
| } | |
| catch { } | |
| form.MinimizeBox = false; | |
| form.MaximizeBox = false; | |
| // show and compute form | |
| form.checkKeyDown = blIncludeKeyDown; | |
| form.ShowDialog(); | |
| return form.keyinfo; | |
| } | |
| } | |
| public class Progress_Form : Form | |
| { | |
| private ConsoleColor ProgressBarColor = ConsoleColor.DarkCyan; | |
| private string WindowTitle = ""; | |
| #if !noVisualStyles | |
| private System.Timers.Timer timer = new System.Timers.Timer(); | |
| private int barNumber = -1; | |
| private int barValue = -1; | |
| private bool inTick = false; | |
| #endif | |
| struct Progress_Data | |
| { | |
| internal Label lbActivity; | |
| internal Label lbStatus; | |
| internal ProgressBar objProgressBar; | |
| internal Label lbRemainingTime; | |
| internal Label lbOperation; | |
| internal int ActivityId; | |
| internal int ParentActivityId; | |
| internal int Depth; | |
| }; | |
| private List<Progress_Data> progressDataList = new List<Progress_Data>(); | |
| private Color DrawingColor(ConsoleColor color) | |
| { // convert ConsoleColor to System.Drawing.Color | |
| switch (color) | |
| { | |
| case ConsoleColor.Black: return Color.Black; | |
| case ConsoleColor.Blue: return Color.Blue; | |
| case ConsoleColor.Cyan: return Color.Cyan; | |
| case ConsoleColor.DarkBlue: return ColorTranslator.FromHtml("#000080"); | |
| case ConsoleColor.DarkGray: return ColorTranslator.FromHtml("#808080"); | |
| case ConsoleColor.DarkGreen: return ColorTranslator.FromHtml("#008000"); | |
| case ConsoleColor.DarkCyan: return ColorTranslator.FromHtml("#008080"); | |
| case ConsoleColor.DarkMagenta: return ColorTranslator.FromHtml("#800080"); | |
| case ConsoleColor.DarkRed: return ColorTranslator.FromHtml("#800000"); | |
| case ConsoleColor.DarkYellow: return ColorTranslator.FromHtml("#808000"); | |
| case ConsoleColor.Gray: return ColorTranslator.FromHtml("#C0C0C0"); | |
| case ConsoleColor.Green: return ColorTranslator.FromHtml("#00FF00"); | |
| case ConsoleColor.Magenta: return Color.Magenta; | |
| case ConsoleColor.Red: return Color.Red; | |
| case ConsoleColor.White: return Color.White; | |
| default: return Color.Yellow; | |
| } | |
| } | |
| public Progress_Form() | |
| { | |
| InitializeComponent(); | |
| } | |
| public Progress_Form(ConsoleColor BarColor) | |
| { | |
| ProgressBarColor = BarColor; | |
| InitializeComponent(); | |
| } | |
| public Progress_Form(string Title, ConsoleColor BarColor) | |
| { | |
| WindowTitle = Title; | |
| ProgressBarColor = BarColor; | |
| InitializeComponent(); | |
| } | |
| private void InitializeComponent() | |
| { | |
| this.SuspendLayout(); | |
| this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); | |
| this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | |
| this.AutoScroll = true; | |
| this.Text = WindowTitle; | |
| this.Height = 147; | |
| this.Width = 800; | |
| this.BackColor = Color.White; | |
| this.FormBorderStyle = FormBorderStyle.FixedSingle; | |
| this.MinimizeBox = false; | |
| this.MaximizeBox = false; | |
| this.ControlBox = false; | |
| this.StartPosition = FormStartPosition.CenterScreen; | |
| this.ResumeLayout(); | |
| #if !noVisualStyles | |
| timer.Elapsed += new System.Timers.ElapsedEventHandler(TimeTick); | |
| timer.Interval = 50; // milliseconds | |
| timer.AutoReset = true; | |
| timer.Start(); | |
| #endif | |
| } | |
| #if !noVisualStyles | |
| private void TimeTick(object source, System.Timers.ElapsedEventArgs e) | |
| { // worker function that is called by timer event | |
| if (inTick) return; | |
| inTick = true; | |
| if (barNumber >= 0) | |
| { | |
| if (barValue >= 0) | |
| { | |
| progressDataList[barNumber].objProgressBar.Value = barValue; | |
| barValue = -1; | |
| } | |
| progressDataList[barNumber].objProgressBar.Refresh(); | |
| } | |
| inTick = false; | |
| } | |
| #endif | |
| private void AddBar(ref Progress_Data pd, int position) | |
| { | |
| // Create Label | |
| pd.lbActivity = new Label(); | |
| pd.lbActivity.Left = 5; | |
| pd.lbActivity.Top = 104*position + 10; | |
| pd.lbActivity.Width = 800 - 20; | |
| pd.lbActivity.Height = 16; | |
| pd.lbActivity.Font = new Font(pd.lbActivity.Font, FontStyle.Bold); | |
| pd.lbActivity.Text = ""; | |
| // Add Label to Form | |
| this.Controls.Add(pd.lbActivity); | |
| // Create Label | |
| pd.lbStatus = new Label(); | |
| pd.lbStatus.Left = 25; | |
| pd.lbStatus.Top = 104*position + 26; | |
| pd.lbStatus.Width = 800 - 40; | |
| pd.lbStatus.Height = 16; | |
| pd.lbStatus.Text = ""; | |
| // Add Label to Form | |
| this.Controls.Add(pd.lbStatus); | |
| // Create ProgressBar | |
| pd.objProgressBar = new ProgressBar(); | |
| pd.objProgressBar.Value = 0; | |
| #if !noVisualStyles // was not ! | |
| pd.objProgressBar.Style = ProgressBarStyle.Continuous; | |
| #else | |
| pd.objProgressBar.Style = ProgressBarStyle.Blocks; | |
| #endif | |
| pd.objProgressBar.ForeColor = DrawingColor(ProgressBarColor); | |
| if (pd.Depth < 15) | |
| { | |
| pd.objProgressBar.Size = new System.Drawing.Size(800 - 60 - 30*pd.Depth, 20); | |
| pd.objProgressBar.Left = 25 + 30*pd.Depth; | |
| } | |
| else | |
| { | |
| pd.objProgressBar.Size = new System.Drawing.Size(800 - 60 - 450, 20); | |
| pd.objProgressBar.Left = 25 + 450; | |
| } | |
| pd.objProgressBar.Top = 104*position + 47; | |
| // Add ProgressBar to Form | |
| this.Controls.Add(pd.objProgressBar); | |
| // Create Label | |
| pd.lbRemainingTime = new Label(); | |
| pd.lbRemainingTime.Left = 5; | |
| pd.lbRemainingTime.Top = 104*position + 72; | |
| pd.lbRemainingTime.Width = 800 - 20; | |
| pd.lbRemainingTime.Height = 16; | |
| pd.lbRemainingTime.Text = ""; | |
| // Add Label to Form | |
| this.Controls.Add(pd.lbRemainingTime); | |
| // Create Label | |
| pd.lbOperation = new Label(); | |
| pd.lbOperation.Left = 25; | |
| pd.lbOperation.Top = 104*position + 88; | |
| pd.lbOperation.Width = 800 - 40; | |
| pd.lbOperation.Height = 16; | |
| pd.lbOperation.Text = ""; | |
| // Add Label to Form | |
| this.Controls.Add(pd.lbOperation); | |
| } | |
| public int GetCount() | |
| { | |
| return progressDataList.Count; | |
| } | |
| public void Update(ProgressRecord objRecord) | |
| { | |
| if (objRecord == null) return; | |
| int currentProgress = -1; | |
| for (int i = 0; i < progressDataList.Count; i++) | |
| { | |
| if (progressDataList[i].ActivityId == objRecord.ActivityId) | |
| { currentProgress = i; break; } | |
| } | |
| if (objRecord.RecordType == ProgressRecordType.Completed) | |
| { | |
| if (currentProgress >= 0) | |
| { | |
| #if !noVisualStyles | |
| if (barNumber == currentProgress) barNumber = -1; | |
| #endif | |
| this.Controls.Remove(progressDataList[currentProgress].lbActivity); | |
| this.Controls.Remove(progressDataList[currentProgress].lbStatus); | |
| this.Controls.Remove(progressDataList[currentProgress].objProgressBar); | |
| this.Controls.Remove(progressDataList[currentProgress].lbRemainingTime); | |
| this.Controls.Remove(progressDataList[currentProgress].lbOperation); | |
| progressDataList[currentProgress].lbActivity.Dispose(); | |
| progressDataList[currentProgress].lbStatus.Dispose(); | |
| progressDataList[currentProgress].objProgressBar.Dispose(); | |
| progressDataList[currentProgress].lbRemainingTime.Dispose(); | |
| progressDataList[currentProgress].lbOperation.Dispose(); | |
| progressDataList.RemoveAt(currentProgress); | |
| } | |
| if (progressDataList.Count == 0) | |
| { | |
| #if !noVisualStyles | |
| timer.Stop(); | |
| timer.Dispose(); | |
| #endif | |
| this.Close(); | |
| return; | |
| } | |
| if (currentProgress < 0) return; | |
| for (int i = currentProgress; i < progressDataList.Count; i++) | |
| { | |
| progressDataList[i].lbActivity.Top = 104*i + 10; | |
| progressDataList[i].lbStatus.Top = 104*i + 26; | |
| progressDataList[i].objProgressBar.Top = 104*i + 47; | |
| progressDataList[i].lbRemainingTime.Top = 104*i + 72; | |
| progressDataList[i].lbOperation.Top = 104*i + 88; | |
| } | |
| if (104*progressDataList.Count + 43 <= System.Windows.Forms.Screen.FromControl(this).Bounds.Height) | |
| { | |
| this.Height = 104*progressDataList.Count + 43; | |
| this.Location = new Point((System.Windows.Forms.Screen.FromControl(this).Bounds.Width - this.Width)/2, (System.Windows.Forms.Screen.FromControl(this).Bounds.Height - this.Height)/2); | |
| } | |
| else | |
| { | |
| this.Height = System.Windows.Forms.Screen.FromControl(this).Bounds.Height; | |
| this.Location = new Point((System.Windows.Forms.Screen.FromControl(this).Bounds.Width - this.Width)/2, 0); | |
| } | |
| return; | |
| } | |
| if (currentProgress < 0) | |
| { | |
| Progress_Data pd = new Progress_Data(); | |
| pd.ActivityId = objRecord.ActivityId; | |
| pd.ParentActivityId = objRecord.ParentActivityId; | |
| pd.Depth = 0; | |
| int nextid = -1; | |
| int parentid = -1; | |
| if (pd.ParentActivityId >= 0) | |
| { | |
| for (int i = 0; i < progressDataList.Count; i++) | |
| { | |
| if (progressDataList[i].ActivityId == pd.ParentActivityId) | |
| { parentid = i; break; } | |
| } | |
| } | |
| if (parentid >= 0) | |
| { | |
| pd.Depth = progressDataList[parentid].Depth + 1; | |
| for (int i = parentid + 1; i < progressDataList.Count; i++) | |
| { | |
| if ((progressDataList[i].Depth < pd.Depth) || ((progressDataList[i].Depth == pd.Depth) && (progressDataList[i].ParentActivityId != pd.ParentActivityId))) | |
| { nextid = i; | |
| break; | |
| } | |
| } | |
| } | |
| if (nextid == -1) | |
| { | |
| AddBar(ref pd, progressDataList.Count); | |
| currentProgress = progressDataList.Count; | |
| progressDataList.Add(pd); | |
| } | |
| else | |
| { | |
| AddBar(ref pd, nextid); | |
| currentProgress = nextid; | |
| progressDataList.Insert(nextid, pd); | |
| for (int i = currentProgress+1; i < progressDataList.Count; i++) | |
| { | |
| progressDataList[i].lbActivity.Top = 104*i + 10; | |
| progressDataList[i].lbStatus.Top = 104*i + 26; | |
| progressDataList[i].objProgressBar.Top = 104*i + 47; | |
| progressDataList[i].lbRemainingTime.Top = 104*i + 72; | |
| progressDataList[i].lbOperation.Top = 104*i + 88; | |
| } | |
| } | |
| if (104*progressDataList.Count + 43 <= System.Windows.Forms.Screen.FromControl(this).Bounds.Height) | |
| { | |
| this.Height = 104*progressDataList.Count + 43; | |
| this.Location = new Point((System.Windows.Forms.Screen.FromControl(this).Bounds.Width - this.Width)/2, (System.Windows.Forms.Screen.FromControl(this).Bounds.Height - this.Height)/2); | |
| } | |
| else | |
| { | |
| this.Height = System.Windows.Forms.Screen.FromControl(this).Bounds.Height; | |
| this.Location = new Point((System.Windows.Forms.Screen.FromControl(this).Bounds.Width - this.Width)/2, 0); | |
| } | |
| } | |
| if (!string.IsNullOrEmpty(objRecord.Activity)) | |
| progressDataList[currentProgress].lbActivity.Text = objRecord.Activity; | |
| else | |
| progressDataList[currentProgress].lbActivity.Text = ""; | |
| if (!string.IsNullOrEmpty(objRecord.StatusDescription)) | |
| progressDataList[currentProgress].lbStatus.Text = objRecord.StatusDescription; | |
| else | |
| progressDataList[currentProgress].lbStatus.Text = ""; | |
| if ((objRecord.PercentComplete >= 0) && (objRecord.PercentComplete <= 100)) | |
| { | |
| #if !noVisualStyles | |
| if (objRecord.PercentComplete < 100) | |
| progressDataList[currentProgress].objProgressBar.Value = objRecord.PercentComplete + 1; | |
| else | |
| progressDataList[currentProgress].objProgressBar.Value = 99; | |
| progressDataList[currentProgress].objProgressBar.Visible = true; | |
| barNumber = currentProgress; | |
| barValue = objRecord.PercentComplete; | |
| #else | |
| progressDataList[currentProgress].objProgressBar.Value = objRecord.PercentComplete; | |
| progressDataList[currentProgress].objProgressBar.Visible = true; | |
| #endif | |
| } | |
| else | |
| { if (objRecord.PercentComplete > 100) | |
| { | |
| progressDataList[currentProgress].objProgressBar.Value = 0; | |
| progressDataList[currentProgress].objProgressBar.Visible = true; | |
| #if !noVisualStyles | |
| barNumber = currentProgress; | |
| barValue = 0; | |
| #endif | |
| } | |
| else | |
| { | |
| progressDataList[currentProgress].objProgressBar.Visible = false; | |
| #if !noVisualStyles | |
| if (barNumber == currentProgress) barNumber = -1; | |
| #endif | |
| } | |
| } | |
| if (objRecord.SecondsRemaining >= 0) | |
| { | |
| System.TimeSpan objTimeSpan = new System.TimeSpan(0, 0, objRecord.SecondsRemaining); | |
| progressDataList[currentProgress].lbRemainingTime.Text = "Remaining time: " + string.Format("{0:00}:{1:00}:{2:00}", (int)objTimeSpan.TotalHours, objTimeSpan.Minutes, objTimeSpan.Seconds); | |
| } | |
| else | |
| progressDataList[currentProgress].lbRemainingTime.Text = ""; | |
| if (!string.IsNullOrEmpty(objRecord.CurrentOperation)) | |
| progressDataList[currentProgress].lbOperation.Text = objRecord.CurrentOperation; | |
| else | |
| progressDataList[currentProgress].lbOperation.Text = ""; | |
| Application.DoEvents(); | |
| } | |
| } | |
| #endif | |
| public class MainModuleUI : PSHostUserInterface | |
| { | |
| private MainModuleRawUI rawUI = null; | |
| public ConsoleColor ErrorForegroundColor = ConsoleColor.Red; | |
| public ConsoleColor ErrorBackgroundColor = ConsoleColor.Black; | |
| public ConsoleColor WarningForegroundColor = ConsoleColor.Yellow; | |
| public ConsoleColor WarningBackgroundColor = ConsoleColor.Black; | |
| public ConsoleColor DebugForegroundColor = ConsoleColor.Yellow; | |
| public ConsoleColor DebugBackgroundColor = ConsoleColor.Black; | |
| public ConsoleColor VerboseForegroundColor = ConsoleColor.Yellow; | |
| public ConsoleColor VerboseBackgroundColor = ConsoleColor.Black; | |
| #if !noConsole | |
| public ConsoleColor ProgressForegroundColor = ConsoleColor.Yellow; | |
| #else | |
| public ConsoleColor ProgressForegroundColor = ConsoleColor.DarkCyan; | |
| #endif | |
| public ConsoleColor ProgressBackgroundColor = ConsoleColor.DarkCyan; | |
| public MainModuleUI() : base() | |
| { | |
| rawUI = new MainModuleRawUI(); | |
| #if !noConsole | |
| rawUI.ForegroundColor = Console.ForegroundColor; | |
| rawUI.BackgroundColor = Console.BackgroundColor; | |
| #endif | |
| } | |
| public override Dictionary<string, PSObject> Prompt(string caption, string message, System.Collections.ObjectModel.Collection<FieldDescription> descriptions) | |
| { | |
| #if !noConsole | |
| if (!string.IsNullOrEmpty(caption)) WriteLine(caption); | |
| if (!string.IsNullOrEmpty(message)) WriteLine(message); | |
| #else | |
| if ((!string.IsNullOrEmpty(caption)) || (!string.IsNullOrEmpty(message))) | |
| { string sTitel = System.AppDomain.CurrentDomain.FriendlyName, sMeldung = ""; | |
| if (!string.IsNullOrEmpty(caption)) sTitel = caption; | |
| if (!string.IsNullOrEmpty(message)) sMeldung = message; | |
| MessageBox.Show(sMeldung, sTitel); | |
| } | |
| // Reset label text for Input_Box | |
| ib_message = ""; | |
| #endif | |
| Dictionary<string, PSObject> ret = new Dictionary<string, PSObject>(); | |
| foreach (FieldDescription cd in descriptions) | |
| { | |
| Type t = null; | |
| if (string.IsNullOrEmpty(cd.ParameterAssemblyFullName)) | |
| t = typeof(string); | |
| else | |
| t = Type.GetType(cd.ParameterAssemblyFullName); | |
| if (t.IsArray) | |
| { | |
| Type elementType = t.GetElementType(); | |
| Type genericListType = Type.GetType("System.Collections.Generic.List"+((char)0x60).ToString()+"1"); | |
| genericListType = genericListType.MakeGenericType(new Type[] { elementType }); | |
| ConstructorInfo constructor = genericListType.GetConstructor(BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null); | |
| object resultList = constructor.Invoke(null); | |
| int index = 0; | |
| string data = ""; | |
| do | |
| { | |
| try | |
| { | |
| #if !noConsole | |
| if (!string.IsNullOrEmpty(cd.Name)) Write(string.Format("{0}[{1}]: ", cd.Name, index)); | |
| #else | |
| if (!string.IsNullOrEmpty(cd.Name)) ib_message = string.Format("{0}[{1}]: ", cd.Name, index); | |
| #endif | |
| data = ReadLine(); | |
| if (string.IsNullOrEmpty(data)) break; | |
| object o = System.Convert.ChangeType(data, elementType); | |
| genericListType.InvokeMember("Add", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, resultList, new object[] { o }); | |
| } | |
| catch (Exception e) | |
| { | |
| throw e; | |
| } | |
| index++; | |
| } while (true); | |
| System.Array retArray = (System.Array )genericListType.InvokeMember("ToArray", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, resultList, null); | |
| ret.Add(cd.Name, new PSObject(retArray)); | |
| } | |
| else | |
| { | |
| object o = null; | |
| string l = null; | |
| try | |
| { | |
| if (t != typeof(System.Security.SecureString)) | |
| { | |
| if (t != typeof(System.Management.Automation.PSCredential)) | |
| { | |
| #if !noConsole | |
| if (!string.IsNullOrEmpty(cd.Name)) Write(cd.Name); | |
| if (!string.IsNullOrEmpty(cd.HelpMessage)) Write(" (Type !? for help.)"); | |
| if ((!string.IsNullOrEmpty(cd.Name)) || (!string.IsNullOrEmpty(cd.HelpMessage))) Write(": "); | |
| #else | |
| if (!string.IsNullOrEmpty(cd.Name)) ib_message = string.Format("{0}: ", cd.Name); | |
| if (!string.IsNullOrEmpty(cd.HelpMessage)) ib_message += "\n(Type !? for help.)"; | |
| #endif | |
| do { | |
| l = ReadLine(); | |
| if (l == "!?") | |
| WriteLine(cd.HelpMessage); | |
| else | |
| { | |
| if (string.IsNullOrEmpty(l)) o = cd.DefaultValue; | |
| if (o == null) | |
| { | |
| try { | |
| o = System.Convert.ChangeType(l, t); | |
| } | |
| catch { | |
| Write("Wrong format, please repeat input: "); | |
| l = "!?"; | |
| } | |
| } | |
| } | |
| } while (l == "!?"); | |
| } | |
| else | |
| { | |
| PSCredential pscred = PromptForCredential("", "", "", ""); | |
| o = pscred; | |
| } | |
| } | |
| else | |
| { | |
| #if !noConsole | |
| if (!string.IsNullOrEmpty(cd.Name)) Write(string.Format("{0}: ", cd.Name)); | |
| #else | |
| if (!string.IsNullOrEmpty(cd.Name)) ib_message = string.Format("{0}: ", cd.Name); | |
| #endif | |
| SecureString pwd = null; | |
| pwd = ReadLineAsSecureString(); | |
| o = pwd; | |
| } | |
| ret.Add(cd.Name, new PSObject(o)); | |
| } | |
| catch (Exception e) | |
| { | |
| throw e; | |
| } | |
| } | |
| } | |
| #if noConsole | |
| // Reset label text for input box | |
| ib_message = ""; | |
| #endif | |
| return ret; | |
| } | |
| public override int PromptForChoice(string caption, string message, System.Collections.ObjectModel.Collection<ChoiceDescription> choices, int defaultChoice) | |
| { | |
| #if noConsole | |
| int iReturn = Choice_Box.Show(choices, defaultChoice, caption, message); | |
| if (iReturn == -1) { iReturn = defaultChoice; } | |
| return iReturn; | |
| #else | |
| if (!string.IsNullOrEmpty(caption)) WriteLine(caption); | |
| WriteLine(message); | |
| do { | |
| int idx = 0; | |
| SortedList<string, int> res = new SortedList<string, int>(); | |
| string defkey = ""; | |
| foreach (ChoiceDescription cd in choices) | |
| { | |
| string lkey = cd.Label.Substring(0, 1), ltext = cd.Label; | |
| int pos = cd.Label.IndexOf('&'); | |
| if (pos > -1) | |
| { | |
| lkey = cd.Label.Substring(pos + 1, 1).ToUpper(); | |
| if (pos > 0) | |
| ltext = cd.Label.Substring(0, pos) + cd.Label.Substring(pos + 1); | |
| else | |
| ltext = cd.Label.Substring(1); | |
| } | |
| res.Add(lkey.ToLower(), idx); | |
| if (idx > 0) Write(" "); | |
| if (idx == defaultChoice) | |
| { | |
| Write(VerboseForegroundColor, rawUI.BackgroundColor, string.Format("[{0}] {1}", lkey, ltext)); | |
| defkey = lkey; | |
| } | |
| else | |
| Write(rawUI.ForegroundColor, rawUI.BackgroundColor, string.Format("[{0}] {1}", lkey, ltext)); | |
| idx++; | |
| } | |
| Write(rawUI.ForegroundColor, rawUI.BackgroundColor, string.Format(" [?] Help (default is \"{0}\"): ", defkey)); | |
| string inpkey = ""; | |
| try | |
| { | |
| inpkey = Console.ReadLine().ToLower(); | |
| if (res.ContainsKey(inpkey)) return res[inpkey]; | |
| if (string.IsNullOrEmpty(inpkey)) return defaultChoice; | |
| } | |
| catch { } | |
| if (inpkey == "?") | |
| { | |
| foreach (ChoiceDescription cd in choices) | |
| { | |
| string lkey = cd.Label.Substring(0, 1); | |
| int pos = cd.Label.IndexOf('&'); | |
| if (pos > -1) lkey = cd.Label.Substring(pos + 1, 1).ToUpper(); | |
| if (!string.IsNullOrEmpty(cd.HelpMessage)) | |
| WriteLine(rawUI.ForegroundColor, rawUI.BackgroundColor, string.Format("{0} - {1}", lkey, cd.HelpMessage)); | |
| else | |
| WriteLine(rawUI.ForegroundColor, rawUI.BackgroundColor, string.Format("{0} -", lkey)); | |
| } | |
| } | |
| } while (true); | |
| #endif | |
| } | |
| public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName, PSCredentialTypes allowedCredentialTypes, PSCredentialUIOptions options) | |
| { | |
| #if !noConsole && !credentialGUI | |
| if (!string.IsNullOrEmpty(caption)) WriteLine(caption); | |
| WriteLine(message); | |
| string un; | |
| if ((string.IsNullOrEmpty(userName)) || ((options & PSCredentialUIOptions.ReadOnlyUserName) == 0)) | |
| { | |
| Write("User name: "); | |
| un = ReadLine(); | |
| } | |
| else | |
| { | |
| Write("User name: "); | |
| if (!string.IsNullOrEmpty(targetName)) Write(targetName + "\\"); | |
| WriteLine(userName); | |
| un = userName; | |
| } | |
| SecureString pwd = null; | |
| Write("Password: "); | |
| pwd = ReadLineAsSecureString(); | |
| if (string.IsNullOrEmpty(un)) un = "<NOUSER>"; | |
| if (!string.IsNullOrEmpty(targetName)) | |
| { | |
| if (un.IndexOf('\\') < 0) un = targetName + "\\" + un; | |
| } | |
| PSCredential c2 = new PSCredential(un, pwd); | |
| return c2; | |
| #else | |
| Credential_Form.User_Pwd cred = Credential_Form.PromptForPassword(caption, message, targetName, userName, allowedCredentialTypes, options); | |
| if (cred != null) | |
| { | |
| System.Security.SecureString x = new System.Security.SecureString(); | |
| foreach (char c in cred.Password.ToCharArray()) | |
| x.AppendChar(c); | |
| return new PSCredential(cred.User, x); | |
| } | |
| return null; | |
| #endif | |
| } | |
| public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName) | |
| { | |
| #if !noConsole && !credentialGUI | |
| if (!string.IsNullOrEmpty(caption)) WriteLine(caption); | |
| WriteLine(message); | |
| string un; | |
| if (string.IsNullOrEmpty(userName)) | |
| { | |
| Write("User name: "); | |
| un = ReadLine(); | |
| } | |
| else | |
| { | |
| Write("User name: "); | |
| if (!string.IsNullOrEmpty(targetName)) Write(targetName + "\\"); | |
| WriteLine(userName); | |
| un = userName; | |
| } | |
| SecureString pwd = null; | |
| Write("Password: "); | |
| pwd = ReadLineAsSecureString(); | |
| if (string.IsNullOrEmpty(un)) un = "<NOUSER>"; | |
| if (!string.IsNullOrEmpty(targetName)) | |
| { | |
| if (un.IndexOf('\\') < 0) un = targetName + "\\" + un; | |
| } | |
| PSCredential c2 = new PSCredential(un, pwd); | |
| return c2; | |
| #else | |
| Credential_Form.User_Pwd cred = Credential_Form.PromptForPassword(caption, message, targetName, userName, PSCredentialTypes.Default, PSCredentialUIOptions.Default); | |
| if (cred != null) | |
| { | |
| System.Security.SecureString x = new System.Security.SecureString(); | |
| foreach (char c in cred.Password.ToCharArray()) | |
| x.AppendChar(c); | |
| return new PSCredential(cred.User, x); | |
| } | |
| return null; | |
| #endif | |
| } | |
| public override PSHostRawUserInterface RawUI | |
| { | |
| get { return rawUI; } | |
| } | |
| #if noConsole | |
| private string ib_message; | |
| #endif | |
| public override string ReadLine() | |
| { | |
| #if noConsole | |
| string sWert = ""; | |
| if (Input_Box.Show(rawUI.WindowTitle, ib_message, ref sWert) == DialogResult.OK) | |
| return sWert; | |
| else | |
| #if exitOnCancel | |
| Environment.Exit(1); | |
| return ""; | |
| #else | |
| return ""; | |
| #endif | |
| #else | |
| return Console.ReadLine(); | |
| #endif | |
| } | |
| private System.Security.SecureString getPassword() | |
| { | |
| System.Security.SecureString pwd = new System.Security.SecureString(); | |
| while (true) | |
| { | |
| ConsoleKeyInfo i = Console.ReadKey(true); | |
| if (i.Key == ConsoleKey.Enter) | |
| { | |
| Console.WriteLine(); | |
| break; | |
| } | |
| else if (i.Key == ConsoleKey.Backspace) | |
| { | |
| if (pwd.Length > 0) | |
| { | |
| pwd.RemoveAt(pwd.Length - 1); | |
| Console.Write("\b \b"); | |
| } | |
| } | |
| else if (i.KeyChar != '\u0000') | |
| { | |
| pwd.AppendChar(i.KeyChar); | |
| Console.Write("*"); | |
| } | |
| } | |
| return pwd; | |
| } | |
| public override System.Security.SecureString ReadLineAsSecureString() | |
| { | |
| System.Security.SecureString secstr = new System.Security.SecureString(); | |
| #if noConsole | |
| string sWert = ""; | |
| if (Input_Box.Show(rawUI.WindowTitle, ib_message, ref sWert, true) == DialogResult.OK) | |
| { | |
| foreach (char ch in sWert) | |
| secstr.AppendChar(ch); | |
| } | |
| #if exitOnCancel | |
| else | |
| Environment.Exit(1); | |
| #endif | |
| #else | |
| secstr = getPassword(); | |
| #endif | |
| return secstr; | |
| } | |
| // called by Write-Host | |
| public override void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value) | |
| { | |
| #if !noOutput | |
| #if noConsole | |
| if ((!string.IsNullOrEmpty(value)) && (value != "\n")) | |
| MessageBox.Show(value, rawUI.WindowTitle); | |
| #else | |
| ConsoleColor fgc = Console.ForegroundColor, bgc = Console.BackgroundColor; | |
| Console.ForegroundColor = foregroundColor; | |
| Console.BackgroundColor = backgroundColor; | |
| Console.Write(value); | |
| Console.ForegroundColor = fgc; | |
| Console.BackgroundColor = bgc; | |
| #endif | |
| #endif | |
| } | |
| public override void Write(string value) | |
| { | |
| #if !noOutput | |
| #if noConsole | |
| if ((!string.IsNullOrEmpty(value)) && (value != "\n")) | |
| MessageBox.Show(value, rawUI.WindowTitle); | |
| #else | |
| Console.Write(value); | |
| #endif | |
| #endif | |
| } | |
| // called by Write-Debug | |
| public override void WriteDebugLine(string message) | |
| { | |
| #if !noError | |
| #if noConsole | |
| MessageBox.Show(message, rawUI.WindowTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); | |
| #else | |
| WriteLineInternal(DebugForegroundColor, DebugBackgroundColor, string.Format("DEBUG: {0}", message)); | |
| #endif | |
| #endif | |
| } | |
| // called by Write-Error | |
| public override void WriteErrorLine(string value) | |
| { | |
| #if !noError | |
| #if noConsole | |
| MessageBox.Show(value, rawUI.WindowTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); | |
| #else | |
| if (Console.IsErrorRedirected) | |
| Console.Error.WriteLine(string.Format("ERROR: {0}", value)); | |
| else | |
| WriteLineInternal(ErrorForegroundColor, ErrorBackgroundColor, string.Format("ERROR: {0}", value)); | |
| #endif | |
| #endif | |
| } | |
| public override void WriteLine() | |
| { | |
| #if !noOutput | |
| #if noConsole | |
| MessageBox.Show("", rawUI.WindowTitle); | |
| #else | |
| Console.WriteLine(); | |
| #endif | |
| #endif | |
| } | |
| public override void WriteLine(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value) | |
| { | |
| #if !noOutput | |
| #if noConsole | |
| if ((!string.IsNullOrEmpty(value)) && (value != "\n")) | |
| MessageBox.Show(value, rawUI.WindowTitle); | |
| #else | |
| ConsoleColor fgc = Console.ForegroundColor, bgc = Console.BackgroundColor; | |
| Console.ForegroundColor = foregroundColor; | |
| Console.BackgroundColor = backgroundColor; | |
| Console.WriteLine(value); | |
| Console.ForegroundColor = fgc; | |
| Console.BackgroundColor = bgc; | |
| #endif | |
| #endif | |
| } | |
| #if !noError && !noConsole | |
| private void WriteLineInternal(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value) | |
| { | |
| ConsoleColor fgc = Console.ForegroundColor, bgc = Console.BackgroundColor; | |
| Console.ForegroundColor = foregroundColor; | |
| Console.BackgroundColor = backgroundColor; | |
| Console.WriteLine(value); | |
| Console.ForegroundColor = fgc; | |
| Console.BackgroundColor = bgc; | |
| } | |
| #endif | |
| // called by Write-Output | |
| public override void WriteLine(string value) | |
| { | |
| #if !noOutput | |
| #if noConsole | |
| if ((!string.IsNullOrEmpty(value)) && (value != "\n")) | |
| MessageBox.Show(value, rawUI.WindowTitle); | |
| #else | |
| Console.WriteLine(value); | |
| #endif | |
| #endif | |
| } | |
| #if noConsole | |
| public Progress_Form pf = null; | |
| #endif | |
| public override void WriteProgress(long sourceId, ProgressRecord record) | |
| { | |
| #if noConsole | |
| if (pf == null) | |
| { | |
| if (record.RecordType == ProgressRecordType.Completed) return; | |
| pf = new Progress_Form(rawUI.WindowTitle, ProgressForegroundColor); | |
| pf.Show(); | |
| } | |
| pf.Update(record); | |
| if (record.RecordType == ProgressRecordType.Completed) | |
| { | |
| if (pf.GetCount() == 0) pf = null; | |
| } | |
| #endif | |
| } | |
| // called by Write-Verbose | |
| public override void WriteVerboseLine(string message) | |
| { | |
| #if !noOutput | |
| #if noConsole | |
| MessageBox.Show(message, rawUI.WindowTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); | |
| #else | |
| WriteLine(VerboseForegroundColor, VerboseBackgroundColor, string.Format("VERBOSE: {0}", message)); | |
| #endif | |
| #endif | |
| } | |
| // called by Write-Warning | |
| public override void WriteWarningLine(string message) | |
| { | |
| #if !noError | |
| #if noConsole | |
| MessageBox.Show(message, rawUI.WindowTitle, MessageBoxButtons.OK, MessageBoxIcon.Warning); | |
| #else | |
| WriteLineInternal(WarningForegroundColor, WarningBackgroundColor, string.Format("WARNING: {0}", message)); | |
| #endif | |
| #endif | |
| } | |
| } | |
| internal class MainModule : PSHost | |
| { | |
| private MainAppInterface parent; | |
| private MainModuleUI ui = null; | |
| private CultureInfo originalCultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture; | |
| private CultureInfo originalUICultureInfo = System.Threading.Thread.CurrentThread.CurrentUICulture; | |
| private Guid myId = Guid.NewGuid(); | |
| public MainModule(MainAppInterface app, MainModuleUI ui) | |
| { | |
| this.parent = app; | |
| this.ui = ui; | |
| } | |
| public class ConsoleColorProxy | |
| { | |
| private MainModuleUI _ui; | |
| public ConsoleColorProxy(MainModuleUI ui) | |
| { | |
| if (ui == null) throw new ArgumentNullException("ui"); | |
| _ui = ui; | |
| } | |
| public ConsoleColor ErrorForegroundColor | |
| { | |
| get { return _ui.ErrorForegroundColor; } | |
| set { _ui.ErrorForegroundColor = value; } | |
| } | |
| public ConsoleColor ErrorBackgroundColor | |
| { | |
| get { return _ui.ErrorBackgroundColor; } | |
| set { _ui.ErrorBackgroundColor = value; } | |
| } | |
| public ConsoleColor WarningForegroundColor | |
| { | |
| get { return _ui.WarningForegroundColor; } | |
| set { _ui.WarningForegroundColor = value; } | |
| } | |
| public ConsoleColor WarningBackgroundColor | |
| { | |
| get { return _ui.WarningBackgroundColor; } | |
| set { _ui.WarningBackgroundColor = value; } | |
| } | |
| public ConsoleColor DebugForegroundColor | |
| { | |
| get { return _ui.DebugForegroundColor; } | |
| set { _ui.DebugForegroundColor = value; } | |
| } | |
| public ConsoleColor DebugBackgroundColor | |
| { | |
| get { return _ui.DebugBackgroundColor; } | |
| set { _ui.DebugBackgroundColor = value; } | |
| } | |
| public ConsoleColor VerboseForegroundColor | |
| { | |
| get { return _ui.VerboseForegroundColor; } | |
| set { _ui.VerboseForegroundColor = value; } | |
| } | |
| public ConsoleColor VerboseBackgroundColor | |
| { | |
| get { return _ui.VerboseBackgroundColor; } | |
| set { _ui.VerboseBackgroundColor = value; } | |
| } | |
| public ConsoleColor ProgressForegroundColor | |
| { | |
| get { return _ui.ProgressForegroundColor; } | |
| set { _ui.ProgressForegroundColor = value; } | |
| } | |
| public ConsoleColor ProgressBackgroundColor | |
| { | |
| get { return _ui.ProgressBackgroundColor; } | |
| set { _ui.ProgressBackgroundColor = value; } | |
| } | |
| } | |
| public override PSObject PrivateData | |
| { | |
| get | |
| { | |
| if (ui == null) return null; | |
| return _consoleColorProxy ?? (_consoleColorProxy = PSObject.AsPSObject(new ConsoleColorProxy(ui))); | |
| } | |
| } | |
| private PSObject _consoleColorProxy; | |
| public override System.Globalization.CultureInfo CurrentCulture | |
| { | |
| get { return this.originalCultureInfo; } | |
| } | |
| public override System.Globalization.CultureInfo CurrentUICulture | |
| { | |
| get { return this.originalUICultureInfo; } | |
| } | |
| public override Guid InstanceId | |
| { | |
| get { return this.myId; } | |
| } | |
| public override string Name | |
| { | |
| get { return "PSRunspace-Host"; } | |
| } | |
| public override PSHostUserInterface UI | |
| { | |
| get { return ui; } | |
| } | |
| public override Version Version | |
| { | |
| get { return new Version(0, 5, 0, 33); } | |
| } | |
| public override void EnterNestedPrompt() | |
| { } | |
| public override void ExitNestedPrompt() | |
| { } | |
| public override void NotifyBeginApplication() | |
| { | |
| return; | |
| } | |
| public override void NotifyEndApplication() | |
| { | |
| return; | |
| } | |
| public override void SetShouldExit(int exitCode) | |
| { | |
| this.parent.ShouldExit = true; | |
| this.parent.ExitCode = exitCode; | |
| } | |
| } | |
| internal interface MainAppInterface | |
| { | |
| bool ShouldExit { get; set; } | |
| int ExitCode { get; set; } | |
| } | |
| internal class MainApp : MainAppInterface | |
| { | |
| private bool shouldExit; | |
| private int exitCode; | |
| public bool ShouldExit | |
| { | |
| get { return this.shouldExit; } | |
| set { this.shouldExit = value; } | |
| } | |
| public int ExitCode | |
| { | |
| get { return this.exitCode; } | |
| set { this.exitCode = value; } | |
| } | |
| #if conHost | |
| [DllImport("kernel32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)] | |
| private static extern bool AllocConsole(); | |
| #endif | |
| #if STA | |
| [STAThread] | |
| #else | |
| [MTAThread] | |
| #endif | |
| private static int Main(string[] args) | |
| { | |
| #if conHost | |
| // before this command no console should be attached or allocation fails | |
| if (!AllocConsole()) { Console.Error.WriteLine("Creation of console failed!"); } | |
| // connect STDIN | |
| Console.SetIn(new System.IO.StreamReader(Console.OpenStandardInput())); | |
| // connect STDOUT | |
| System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(Console.OpenStandardOutput()); | |
| streamWriter.AutoFlush = true; | |
| Console.SetOut(streamWriter); | |
| // connect STDERR | |
| System.IO.StreamWriter errorWriter = new System.IO.StreamWriter(Console.OpenStandardOutput()); | |
| errorWriter.AutoFlush = true; | |
| Console.SetError(errorWriter); | |
| #endif | |
| #if !noConsole && unicode | |
| Console.OutputEncoding = new System.Text.UnicodeEncoding(); | |
| #endif | |
| $culture | |
| #if !noVisualStyles && noConsole | |
| Application.EnableVisualStyles(); | |
| #endif | |
| MainApp me = new MainApp(); | |
| bool paramWait = false; | |
| string extractFN = string.Empty; | |
| MainModuleUI ui = new MainModuleUI(); | |
| MainModule host = new MainModule(me, ui); | |
| System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false); | |
| AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); | |
| try | |
| { | |
| using (Runspace myRunSpace = RunspaceFactory.CreateRunspace(host)) | |
| { | |
| #if STA | |
| myRunSpace.ApartmentState = System.Threading.ApartmentState.STA; | |
| #else | |
| myRunSpace.ApartmentState = System.Threading.ApartmentState.MTA; | |
| #endif | |
| myRunSpace.Open(); | |
| // automatic variables | |
| myRunSpace.SessionStateProxy.SetVariable("ScriptRoot", AppContext.BaseDirectory); | |
| myRunSpace.SessionStateProxy.SetVariable("ScriptName", System.Reflection.Assembly.GetExecutingAssembly().Location); | |
| myRunSpace.SessionStateProxy.SetVariable("rtmode", "exe"); | |
| using (PowerShell posh = PowerShell.Create()) | |
| { | |
| #if !noConsole | |
| Console.CancelKeyPress += new ConsoleCancelEventHandler(delegate(object sender, ConsoleCancelEventArgs e) | |
| { | |
| try | |
| { | |
| posh.BeginStop(new AsyncCallback(delegate(IAsyncResult r) | |
| { | |
| mre.Set(); | |
| e.Cancel = true; | |
| }), null); | |
| } | |
| catch { }; | |
| }); | |
| #endif | |
| posh.Runspace = myRunSpace; | |
| posh.Streams.Error.DataAdded += new EventHandler<DataAddedEventArgs>(delegate(object sender, DataAddedEventArgs e) | |
| { | |
| ui.WriteErrorLine(((PSDataCollection<ErrorRecord>)sender)[e.Index].Exception.Message); | |
| }); | |
| PSDataCollection<string> colInput = new PSDataCollection<string>(); | |
| if (Console.IsInputRedirected) | |
| { // read standard input | |
| string sItem = ""; | |
| while ((sItem = Console.ReadLine()) != null) | |
| { // add to powershell pipeline | |
| colInput.Add(sItem); | |
| } | |
| } | |
| colInput.Complete(); | |
| PSDataCollection<PSObject> colOutput = new PSDataCollection<PSObject>(); | |
| colOutput.DataAdded += new EventHandler<DataAddedEventArgs>(delegate(object sender, DataAddedEventArgs e) | |
| { | |
| ui.WriteLine(((PSDataCollection<PSObject>)sender)[e.Index].ToString()); | |
| }); | |
| int separator = 0; | |
| int idx = 0; | |
| bool bHelp = false; | |
| string sHelp = ""; | |
| foreach (string s in args) | |
| { | |
| if (string.Compare(s, "-wait", true) == 0) paramWait = true; | |
| else if (s.StartsWith("-extract", StringComparison.InvariantCultureIgnoreCase)) | |
| { | |
| string[] s1 = s.Split(new string[] { ":" }, 2, StringSplitOptions.RemoveEmptyEntries); | |
| if (s1.Length != 2) | |
| { | |
| #if noConsole | |
| MessageBox.Show("If you specify the -extract option you need to add a file for extraction in this way\r\n -extract:\"<filename>\"", System.AppDomain.CurrentDomain.FriendlyName, MessageBoxButtons.OK, MessageBoxIcon.Error); | |
| #else | |
| Console.WriteLine("If you specify the -extract option you need to add a file for extraction in this way\r\n -extract:\"<filename>\""); | |
| #endif | |
| return 1; | |
| } | |
| extractFN = s1[1].Trim(new char[] { '\"','\'' }); | |
| } | |
| else if (string.Compare(s, "-end", true) == 0) | |
| { | |
| separator = idx + 1; | |
| break; | |
| } | |
| else if (string.Compare(s, "-?", true) == 0) bHelp = true; | |
| else if (bHelp) | |
| { | |
| if ((string.Compare(s, "-detailed", true) == 0) || (string.Compare(s, "-examples", true) == 0) || (string.Compare(s, "-full", true) == 0)) sHelp = s; | |
| } | |
| else if (string.Compare(s, "-debug", true) == 0) | |
| { | |
| System.Diagnostics.Debugger.Launch(); | |
| break; | |
| } | |
| idx++; | |
| } | |
| Assembly executingAssembly = Assembly.GetExecutingAssembly(); | |
| $embedFileSection | |
| string script = Expand_text(@"$sourceScript"); | |
| if (!string.IsNullOrEmpty(extractFN)) | |
| { | |
| System.IO.File.WriteAllText(extractFN, script); | |
| return 0; | |
| } | |
| #if psCore | |
| string modulePath = "`$env:PSModulePath = \"" + GetPowershellModulePath() + "\""; | |
| posh.AddScript(modulePath); | |
| #endif | |
| if (bHelp) | |
| { // help selected | |
| posh.AddScript("function " + System.AppDomain.CurrentDomain.FriendlyName + "{" + script + "}; Get-Help " + System.AppDomain.CurrentDomain.FriendlyName + " " + sHelp + " | Out-String"); | |
| } else { // execution selected | |
| posh.AddScript(script); | |
| } | |
| if (!bHelp) | |
| { // only if no help selected | |
| // parse parameters | |
| string argbuffer = null; | |
| // regex for named parameters | |
| System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"^-([^: ]+)[ :]?([^:]*)$"); | |
| for (int i = separator; i < args.Length; i++) | |
| { | |
| System.Text.RegularExpressions.Match match = regex.Match(args[i]); | |
| double dummy; | |
| if ((match.Success && match.Groups.Count == 3) && (!Double.TryParse(args[i], out dummy))) | |
| { // parameter in powershell style, means named parameter found | |
| if (argbuffer != null) // already a named parameter in buffer, then flush it | |
| posh.AddParameter(argbuffer); | |
| if (match.Groups[2].Value.Trim() == "") | |
| { // store named parameter in buffer | |
| argbuffer = match.Groups[1].Value; | |
| } | |
| else | |
| // caution: when called in powershell $TRUE gets converted, when called in cmd.exe not | |
| if ((match.Groups[2].Value == "$TRUE") || (match.Groups[2].Value.ToUpper() == "\x24TRUE")) | |
| { // switch found | |
| posh.AddParameter(match.Groups[1].Value, true); | |
| argbuffer = null; | |
| } | |
| else | |
| // caution: when called in powershell $FALSE gets converted, when called in cmd.exe not | |
| if ((match.Groups[2].Value == "$FALSE") || (match.Groups[2].Value.ToUpper() == "\x24"+"FALSE")) | |
| { // switch found | |
| posh.AddParameter(match.Groups[1].Value, false); | |
| argbuffer = null; | |
| } | |
| else | |
| { // named parameter with value found | |
| posh.AddParameter(match.Groups[1].Value, match.Groups[2].Value); | |
| argbuffer = null; | |
| } | |
| } | |
| else | |
| { // unnamed parameter found | |
| if (argbuffer != null) | |
| { // already a named parameter in buffer, so this is the value | |
| posh.AddParameter(argbuffer, args[i]); | |
| argbuffer = null; | |
| } | |
| else | |
| { // position parameter found | |
| posh.AddArgument(args[i]); | |
| } | |
| } | |
| } | |
| if (argbuffer != null) posh.AddParameter(argbuffer); // flush parameter buffer... | |
| // convert output to strings | |
| posh.AddCommand("Out-String"); | |
| // with a single string per line | |
| posh.AddParameter("Stream"); | |
| } | |
| posh.BeginInvoke<string, PSObject>(colInput, colOutput, null, new AsyncCallback(delegate(IAsyncResult ar) | |
| { | |
| if (ar.IsCompleted) mre.Set(); | |
| }), null); | |
| while (!me.ShouldExit && !mre.WaitOne(100)) { }; | |
| posh.Stop(); | |
| if (posh.InvocationStateInfo.State == PSInvocationState.Failed) | |
| ui.WriteErrorLine(posh.InvocationStateInfo.Reason.Message); | |
| } | |
| myRunSpace.Close(); | |
| } | |
| } | |
| catch (Exception ex) | |
| { | |
| #if !noError | |
| #if noConsole | |
| MessageBox.Show("An exception occured: " + ex.Message, System.AppDomain.CurrentDomain.FriendlyName, MessageBoxButtons.OK, MessageBoxIcon.Error); | |
| #else | |
| Console.Write("An exception occured: "); | |
| Console.WriteLine(ex.Message); | |
| #endif | |
| #endif | |
| } | |
| if (paramWait) | |
| { | |
| #if !noError | |
| #if noConsole | |
| MessageBox.Show("Click OK to exit...", System.AppDomain.CurrentDomain.FriendlyName); | |
| #else | |
| Console.WriteLine("Hit any key to exit..."); | |
| Console.ReadKey(); | |
| #endif | |
| #endif | |
| } | |
| return me.ExitCode; | |
| } // Main | |
| #if psCore | |
| // two functions below taken from https://github.com/FabienTschanz/PS2EXE.Core | |
| private static string GetPowershellModulePath() | |
| { | |
| var environment = new StringBuilder(); | |
| // Get the default windows modules path | |
| var winModules = Path.Combine(Environment.ExpandEnvironmentVariables("%WINDIR%"), | |
| "system32", "WindowsPowerShell", "v1.0", "Modules"); | |
| // Add default Windows modules | |
| var env = Environment.GetEnvironmentVariable("PSModulePath"); | |
| if (!string.IsNullOrEmpty(env)) environment.Append(env); | |
| if (!environment.ToString().Contains(winModules, StringComparison.OrdinalIgnoreCase)) | |
| environment.Append(';').Append(winModules); | |
| // Get PowerShell SDK modules path | |
| var bundlePath = GetBundleExtractLocation(); | |
| var extractLocation = Path.Combine(bundlePath, "runtimes", "win", "lib", "net$dotnetversion", "Modules"); | |
| if (System.IO.Directory.Exists(extractLocation)) | |
| environment.Append(';').Append(extractLocation); | |
| return environment.ToString(); | |
| } | |
| private static string? GetBundleExtractLocation() | |
| { | |
| // Get extract bundle extract location | |
| // Docs: https://learn.microsoft.com/en-us/dotnet/core/deploying/single-file/overview?tabs=cli#native-libraries | |
| string? assemblyLocation = null; | |
| if (string.IsNullOrEmpty(assemblyLocation)) | |
| assemblyLocation = Assembly.GetEntryAssembly()?.Location; | |
| if (string.IsNullOrEmpty(assemblyLocation)) | |
| assemblyLocation = Assembly.GetExecutingAssembly().Location; | |
| if (string.IsNullOrEmpty(assemblyLocation)) | |
| assemblyLocation = AppContext.BaseDirectory; | |
| // Get the base folder of the assembly location | |
| var baseFolder = Path.GetDirectoryName(assemblyLocation); | |
| if (!string.IsNullOrEmpty(baseFolder) && !Directory.Exists(baseFolder)) return null; | |
| return baseFolder; | |
| } | |
| #endif | |
| static string Expand_text(string InputString) | |
| { | |
| if (string.IsNullOrEmpty(InputString)) return null; | |
| string result; | |
| var compressedStream = new MemoryStream(Convert.FromBase64String(InputString)); | |
| using (var decompressorStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) | |
| { | |
| using (var decompressedStream = new MemoryStream()) | |
| { | |
| decompressorStream.CopyTo(decompressedStream); | |
| result = Encoding.UTF8.GetString(decompressedStream.ToArray()); | |
| } | |
| } | |
| return result; | |
| } // Expand_text | |
| static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) | |
| { | |
| throw new Exception("Unhandled exception in " + System.AppDomain.CurrentDomain.FriendlyName); | |
| } | |
| } | |
| } | |
| "@.split("`n").where({$_}).foreach({ | |
| $l = $_.trim() | |
| if ($l -notmatch '^$|^(//|#reg|#endreg)') {$l} | |
| <#if ($l) { TEST!!! | |
| if ([regex]::IsMatch($l,'//')) { #, [System.Text.RegularExpressions.RegexOptions]::Compiled) | |
| $l = [regex]::replace($l,'//.*$','').TrimStart() | |
| if ($l) {$l} | |
| } else {$l} | |
| }#> | |
| }) -join "`n" | |
| } # END Get-MainProgramSource | |
| function Show-ModuleHelp { | |
| $colw = 18 | |
| $col1 = [string]::format("`$1`n{0}", ' '*($colw + 3)) | |
| ##$rx1 = [regex]::new("(^.{1,$([console]::WindowWidth)} (?!$))", [System.Text.RegularExpressions.RegexOptions]::Compiled) | |
| $rx1 = [regex]::new("(^.{$([console]::WindowWidth)}(?!$))", [System.Text.RegularExpressions.RegexOptions]::Compiled) | |
| $fmt1 = "{0,-$colw} : {1}" | |
| $fmt11 = "{0,-$colw} {1}" | |
| $fmtpad2 = '{0}' | |
| $fmtpad6 = ' {0}' | |
| $util = 'invoke-ps2exe' | |
| $titleExm = 'DarkGray' | |
| function write-format { | |
| Write-Host $rx1.Replace(($fmt1 -f $args),$col1) | |
| } # end | |
| function write-format1 { | |
| Write-Host $rx1.Replace(($fmt11 -f $args),$col1) | |
| } # end | |
| Write-Host "USAGE:" -ForegroundColor DarkYellow | |
| Write-Host "Invoke-ps2exe [-InputFile] <fullname> [-OutputFile <filepath>]" | |
| Write-Host " [-ScriptBlock] <script> [-OutputFile <filepath>]" | |
| Write-Host " [-EmbedFiles <hashtable>] [-IconFile {<fullname> | system}]" | |
| Write-Host " [-Title <string>] [-Description <string>] [-Version <string>]" | |
| Write-Host " [-Company <string>] [-Product <string>] [-Copyright <string>] [-Trademark <string>]" | |
| Write-Host " [-Core] [-TargetOS {w[indows] | l[inux] | m[acos]}] [-Arm] [-DotnetVersion <string>] [-Solo] [-NoCache]" | |
| Write-Host " [-Platform {x64|x86|any}] [-MTA] [-Lcid <lang_id>] [-LongPaths]" | |
| Write-Host " [-NoOutput] [-NoError] [-NoConsole] [-ConHost] [-Unicode]" | |
| Write-Host " [-DPIAware] [-WinFormsDPIAware] [-NoVisualStyles] [-ExitOnCancel]" | |
| Write-Host " [-CredentialGUI] [-RequireAdmin] [-SupportOS] [-Virtualize]" | |
| Write-Host " [-PrepareDebug] [-ConfigFile] [-NoCompress] [-Basic] [-Run]" | |
| #Write-Host " [-Profile {gui | console | winforms}]" | |
| Write-Host | |
| Write-Host 'KEY FEATURES:' -ForegroundColor DarkYellow | |
| Write-Host '- No external dependencies' | |
| Write-Host '- Deployment strategy for output file: single executable' | |
| Write-Host '- Source compressor & encoder' | |
| Write-Host '- Automatic variables in user PS script: $ScriptRoot, $ScriptName, $RtMode' | |
| Write-Host '- Experimental support for PowerShell Core (see Notes)' | |
| Write-Host '- Full backward compatibility with original PS2EXE PS script' | |
| Write-Host '- There is a GUI counterpart of the ps2exec — Ps2exe.NET' | |
| Write-Host | |
| Write-Host 'PARAMETERS:' -ForegroundColor DarkYellow | |
| write-format "-InputFile" "PowerShell script filename that you want to convert to executable (file has to be UTF8 or UTF16 encoded)" | |
| write-format "-ScriptBlock" "PowerShell script that you want to convert to executable" | |
| write-format "-OutputFile" "Destination executable file name or folder, defaults to inputFile with extension '.exe'" | |
| write-format "-IconFile" "Icon file name for the compiled executable or 'system' to set default, otherwise the icon from powershell.exe will be used" | |
| write-format "-EmbedFiles" "Files to embed given as a hashtable, will be extracted to the key of the hashtable, source file names must be unique" | |
| write-format1 "" "(e.g. -embedFiles @{'Targetfilepath'='Sourcefilepath'})" | |
| write-format "-Title" "Title information (displayed in details tab of Windows Explorer's properties dialog)" | |
| write-format "-Description" "Description information (not displayed, but embedded in executable)" | |
| write-format "-Company" "Company information (not displayed, but embedded in executable)" | |
| write-format "-Product" "Product information (displayed in details tab of Windows Explorer's properties dialog)" | |
| write-format "-Copyright" "Copyright information (displayed in details tab of Windows Explorer's properties dialog)" | |
| write-format "-Trademark" "Trademark information (displayed in details tab of Windows Explorer's properties dialog)" | |
| write-format "-Version" "Version information (displayed in details tab of Windows Explorer's properties dialog)" | |
| write-format "-Lcid" "Locale ID decimal notation for the compiled executable. Current user culture applies if not specified" | |
| write-format "-Core" "EXPERIMENTAL support for PowerShell Core scripts" | |
| write-format "-TargetOS" "Option for -Core. Valid values are Windows, Linux, Macos. Default is Windows" | |
| write-format "-Arm" "Option for -Core. Enable compiler to create executable for ARM platforms" | |
| write-format "-DotnetVersion" "Option for -Core. Target .NET version" | |
| write-format "-Solo" "Option for -Core. Enable 'SelfContained' mode" | |
| write-format "-NoCache" "Option for -Core. Omit checking for changes in compiler parameters" | |
| write-format "-MTA" "Enable Multi Thread Apartment mode. Default is Single Thread Apartment (STA). This parameter replaces separate 'STA', but that can be used for backward compatibility" | |
| write-format "-Platform" "Compile for 32-bit or 64-bit runtime, or AnyCPU. This parameter replaces 'x86' and 'x64' separate ones, but those can be used for backward compatibility" | |
| write-format "-NoConsole" "The resulting executable will be a GUI app without a console window" | |
| write-format "-ConHost" "Force start with conhost as console instead of Windows Terminal (disables redirections)" | |
| write-format "-NoOutput" "The resulting executable will generate no standard output (includes verbose and information channel)" | |
| write-format "-NoError" "The resulting executable will generate no error output (includes warning and debug channel)" | |
| write-format "-Unicode" "Encode output as Unicode in console mode" | |
| write-format "-CredentialGUI" "Use GUI for prompting credentials in console mode" | |
| write-format "-PrepareDebug" "Create helpful information for debugging" | |
| write-format "-ConfigFile" "Write a config file (<outputfile>.exe.config)" | |
| write-format "-NoVisualStyles" "Disable visual styles for a generated windows GUI application (only with -noConsole)" | |
| write-format "-ExitOnCancel" 'Exit program when Cancel or "✖" is selected in a Read-Host input box (only with -NoConsole)' | |
| write-format "-DPIAware" "If display scaling is enabled, GUI controls will be scaled if possible" | |
| write-format "-WinFormsDPIAware" "If display scaling is enabled, WinForms use DPI scaling (requires Windows 10 and .Net 4.7 or up)" | |
| write-format "-RequireAdmin" "If UAC is enabled, compiled executable run only in elevated context (UAC dialog appears if required)" | |
| write-format "-SupportOS" "Use features of the latest Windows versions (run [Environment]::OSVersion to see the difference)" | |
| write-format "-LongPaths" "Enable long paths (beyond 260 characters) if enabled on OS (works only with Windows 10 and up)" | |
| write-format "-Virtualize" "Application virtualization is activated (forcing x86 runtime)" | |
| write-format "-NoCompress" "Disable a PS compression of the source. Text compression is always on" | |
| write-format "-Basic" "Use a light compression mode. Omits comments and indented formatting in the output string. Ignored if -NoCompress specified" | |
| write-format "-Run" "Invoke output executable immediately after creation" | |
| #write-format "-Profile" "Selection shortcut. Group selection of parameters. Any other parameter takes precedence over this parameter" | |
| Write-Host | |
| Write-Host 'NOTES:' -ForegroundColor DarkYellow | |
| Write-Host 'Executable uses Windows PowerShell host to run the embeded script' | |
| Write-Host 'Executable takes 2-4 sec to create Windows PowerShell host before starting the embeded script' | |
| Write-Host 'In compiled executable some of the PowerShell related automatic variables are no longer available. Especially the $PSScriptRoot variable is empty. It replaced with $ScriptRoot' | |
| Write-Host 'PowerShell Core profile: PublishSingleFile, net8+, x64' | |
| Write-Host 'PowerShell Core support requires the public NuGet repository available at compiling runtime' | |
| Write-Host | |
| Write-Host 'EXAMPLES:' -ForegroundColor DarkYellow | |
| Write-Host ($fmtpad2 -f "- Compile GUI application") -ForegroundColor $titleExm | |
| Write-Host ($fmtpad6 -f "$util -input C:\myscripts\myguiapp.ps1 -icon C:\myscripts\appicon.ico -noconsole -noerror -nooutput") | |
| Write-Host | |
| $defparams = $script:MyInvocation.MyCommand.ScriptBlock.Ast.FindAll({ | |
| $args[0] -is [System.Management.Automation.Language.ParameterAst] | |
| }, $false).where{$_.DefaultValue} | |
| if ($defparams) { | |
| Write-Host 'DEFAULT VALUES:' -ForegroundColor DarkYellow | |
| $defparams.DefaultValue | ForEach-Object { | |
| write-format ('-{0}' -f $_.Parent.Name.VariablePath.UserPath) $_.Value | |
| } | |
| Write-Host | |
| } | |
| Write-Warning "Input file or scriptblock not specified." | |
| } # END Show-ModuleHelp |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment