src/programFrames/default.cs

// Simple PowerShell host created by Ingo Karstein (http://blog.karstein-consulting.com)
// Reworked and GUI support by Markus Scholtes
 
using System;
using System.Collections.Generic;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
#if !Pwsh20
    using System.Management.Automation.Language;
#endif
using System.Globalization;
using System.Management.Automation.Host;
using System.Security;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
#if noConsole
    using System.Windows.Forms;
    using System.Drawing;
#endif
using System.Runtime.Versioning;
 
// not displayed in details tab of properties dialog, but embedded to file
#if Resources
    [assembly: AssemblyDescription("$description")]
    [assembly: AssemblyCompany("$company")]
    [assembly: AssemblyTitle("$title")]
    [assembly: AssemblyProduct("$product")]
    [assembly: AssemblyCopyright("$copyright")]
    [assembly: AssemblyTrademark("$trademark")]
#endif
#if version
    [assembly: AssemblyVersion("$version")]
    [assembly: AssemblyFileVersion("$version")]
#endif
#if winFormsDPIAware
    [assembly: TargetFrameworkAttribute("$TargetFramework,Profile=Client")]
#endif
 
namespace PSRunnerNS {
    #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) {
            // Flags und Variablen initialisieren
            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;
                }
            }
 
            // den Benutzer nach Kennwort fragen, grafischer 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 PSRunnerRawUI: PSHostRawUserInterface {
        #if noConsole
            // Speicher für Konsolenfarben bei GUI-Output werden gelesen und gesetzt, aber im Moment nicht genutzt (for future use)
            private ConsoleColor _GUIBackgroundColor = ConsoleColor.White;
            private ConsoleColor _GUIForegroundColor = ConsoleColor.Black;
 
            #if noConsole
            private string _windowTitleData;
            public PSRunnerRawUI() {
                // load assembly:AssemblyTitle
                AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute) Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(AssemblyTitleAttribute));
                if (titleAttribute != null)
                    _windowTitleData = titleAttribute.Title;
                else
                    _windowTitleData = System.AppDomain.CurrentDomain.FriendlyName;
            }
            #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 {
            #if !noConsole
                get { return Console.BackgroundColor; }
                set { Console.BackgroundColor = value; }
            #else
                get { return _GUIBackgroundColor; }
                set { _GUIBackgroundColor = value; }
            #endif
        }
 
        public override System.Management.Automation.Host.Size BufferSize {
            get {
                #if !noConsole
                    if (!Console_Info.IsOutputRedirected())
                        return new System.Management.Automation.Host.Size(Console.BufferWidth, Console.BufferHeight);
                #endif
                // return default value. If no valid value is returned WriteLine will not be called
                return new System.Management.Automation.Host.Size(120, 50);
            }
            set {
                #if !noConsole
                    Console.BufferWidth = value.Width;
                    Console.BufferHeight = value.Height;
                #endif
            }
        }
 
        public override Coordinates CursorPosition {
            get {
                return new Coordinates(
                #if !noConsole
                    Console.CursorLeft, Console.CursorTop
                #else
                    // Dummywert für Winforms zurückgeben.
                    0, 0
                #endif
                );
            }
            set {
                #if !noConsole
                    Console.CursorTop = value.Y;
                    Console.CursorLeft = value.X;
                #endif
            }
        }
 
        public override int CursorSize {
            get {
                return
                    #if !noConsole
                        Console.CursorSize
                    #else
                        // Dummywert für Winforms zurückgeben.
                        25
                    #endif
                ;
            }
            set {
                #if !noConsole
                    Console.CursorSize = value;
                #endif
            }
        }
 
        #if noConsole
            private Form Invisible_Form;
        #endif
 
        public override void FlushInputBuffer() {
            #if !noConsole
            if (!Console_Info.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 {
            #if !noConsole
                get { return Console.ForegroundColor; }
                set { Console.ForegroundColor = value; }
            #else
                get { return _GUIForegroundColor; }
                set { _GUIForegroundColor = 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 {
                return
                    #if !noConsole
                        Console.KeyAvailable
                    #else
                        true
                    #endif
                ;
            }
        }
 
        public override System.Management.Automation.Host.Size MaxPhysicalWindowSize {
            get {
                return new System.Management.Automation.Host.Size(
                    #if !noConsole
                        Console.LargestWindowWidth, Console.LargestWindowHeight
                    #else
                        // Dummy-Wert für Winforms
                        240, 84
                    #endif
                );
            }
        }
 
        public override System.Management.Automation.Host.Size MaxWindowSize {
            get {
                return new System.Management.Automation.Host.Size(
                    #if !noConsole
                        Console.BufferWidth, Console.BufferWidth
                    #else
                        // Dummy-Wert für Winforms
                        120, 84
                    #endif
                );
            }
        }
 
        public override KeyInfo ReadKey(ReadKeyOptions options) {
            #if !noConsole
            ConsoleKeyInfo info = Console.ReadKey((options & ReadKeyOptions.NoEcho) != 0);
 
            ControlKeyStates state = 0;
            if ((info.Modifiers & ConsoleModifiers.Alt) != 0)
                state |= ControlKeyStates.LeftAltPressed | ControlKeyStates.RightAltPressed;
            if ((info.Modifiers & ConsoleModifiers.Control) != 0)
                state |= ControlKeyStates.LeftCtrlPressed | ControlKeyStates.RightCtrlPressed;
            if ((info.Modifiers & ConsoleModifiers.Shift) != 0)
                state |= ControlKeyStates.ShiftPressed;
            if (Console.CapsLock)
                state |= ControlKeyStates.CapsLockOn;
            if (Console.NumberLock)
                state |= ControlKeyStates.NumLockOn;
 
            return new KeyInfo((int) info.Key, info.KeyChar, state, (options & ReadKeyOptions.IncludeKeyDown) != 0);
            #else
            if ((options & ReadKeyOptions.IncludeKeyDown) != 0)
                return ReadKey_Box.Show("", "", true);
            else
                return ReadKey_Box.Show("", "", 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 {
                return new Coordinates(
                    #if !noConsole
                        Console.WindowLeft, Console.WindowTop
                    #else
                        // Dummy-Wert für Winforms
                        0, 0
                    #endif
                );
            }
            set {
                #if !noConsole
                Console.WindowLeft = value.X;
                Console.WindowTop = value.Y;
                #endif
            }
        }
 
        public override System.Management.Automation.Host.Size WindowSize {
            get {
                return new System.Management.Automation.Host.Size(
                #if !noConsole
                    Console.WindowWidth, Console.WindowHeight
                #else
                    // Dummy-Wert für Winforms
                    120, 50
                #endif
                );
            }
            set {
                #if !noConsole
                Console.WindowWidth = value.Width;
                Console.WindowHeight = value.Height;
                #endif
            }
        }
 
        public override string WindowTitle {
            get {
                return
                #if !noConsole
                Console.Title
                #else
                _windowTitleData
                #endif
                ;
            }
            set {
                #if !noConsole
                Console.Title
                #else
                _windowTitleData
                #endif
                = value;
            }
        }
    }
 
    #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)
                    strPrompt = "Secure input:";
                else
                    strPrompt = "Input:";
            }
            label.Text = strPrompt.PadRight(16);
            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
            if (string.IsNullOrEmpty(strTitle))
                form.Text = System.AppDomain.CurrentDomain.FriendlyName;
            else
                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;
                // erst durch Add() wird die Größe des Labels ermittelt
                form.Controls.Add(label);
                iPosY = label.Bottom;
                iMaxX = label.Right;
            }
 
            // An den Radiobuttons orientieren sich die weiteren Größen und Positionen
            // Diese Controls also jetzt fertigstellen
            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 = Regex.Replace(sAuswahl.Label, ".\b", "");
                if (Counter == intDefault)
                    aradioButton[Counter].Checked = true;
                aradioButton[Counter].Location = new Point(9, iPosY);
                aradioButton[Counter].AutoSize = true;
                // erst durch Add() wird die Größe des Labels ermittelt
                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++;
            }
 
            // Tooltip auch anzeigen, wenn Parent-Fenster inaktiv ist
            toolTip.ShowAlways = true;
 
            // Button erzeugen
            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)
                return -1;
            int iRueck = -1;
            for (Counter = 0; Counter < arrChoice.Count; Counter++) {
                if (aradioButton[Counter].Checked == true) {
                    iRueck = Counter;
                }
            }
            return iRueck;
        }
    }
 
    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 kevent) {
                if (checkKeyDown) { // store key info
                    keyinfo.VirtualKeyCode = kevent.KeyValue;
                    keyinfo.Character = GetCharFromKeys(kevent.KeyCode, kevent.Shift, kevent.Alt & kevent.Control)[0];
                    keyinfo.KeyDown = false;
                    keyinfo.ControlKeyState = 0;
                    if (kevent.Alt) {
                        keyinfo.ControlKeyState = ControlKeyStates.LeftAltPressed | ControlKeyStates.RightAltPressed;
                    }
                    if (kevent.Control) {
                        keyinfo.ControlKeyState |= ControlKeyStates.LeftCtrlPressed | ControlKeyStates.RightCtrlPressed;
                        if (!kevent.Alt)
                            if (kevent.KeyValue > 64 && kevent.KeyValue < 96)
                                keyinfo.Character = (char)(kevent.KeyValue - 64);
                    }
                    if (kevent.Shift) {
                        keyinfo.ControlKeyState |= ControlKeyStates.ShiftPressed;
                    }
                    if ((kevent.Modifiers & System.Windows.Forms.Keys.CapsLock) > 0) {
                        keyinfo.ControlKeyState |= ControlKeyStates.CapsLockOn;
                    }
                    if ((kevent.Modifiers & System.Windows.Forms.Keys.NumLock) > 0) {
                        keyinfo.ControlKeyState |= ControlKeyStates.NumLockOn;
                    }
                    // and close the form
                    this.Close();
                }
            }
 
            void Keyboard_Form_KeyUp(object sender, KeyEventArgs kevent) {
                if (!checkKeyDown) { // store key info
                    keyinfo.VirtualKeyCode = kevent.KeyValue;
                    keyinfo.Character = GetCharFromKeys(kevent.KeyCode, kevent.Shift, kevent.Alt & kevent.Control)[0];
                    keyinfo.KeyDown = true;
                    keyinfo.ControlKeyState = 0;
                    if (kevent.Alt) {
                        keyinfo.ControlKeyState = ControlKeyStates.LeftAltPressed | ControlKeyStates.RightAltPressed;
                    }
                    if (kevent.Control) {
                        keyinfo.ControlKeyState |= ControlKeyStates.LeftCtrlPressed | ControlKeyStates.RightCtrlPressed;
                        if (!kevent.Alt)
                            if (kevent.KeyValue > 64 && kevent.KeyValue < 96)
                                keyinfo.Character = (char)(kevent.KeyValue - 64);
                    }
                    if (kevent.Shift) {
                        keyinfo.ControlKeyState |= ControlKeyStates.ShiftPressed;
                    }
                    if ((kevent.Modifiers & System.Windows.Forms.Keys.CapsLock) > 0) {
                        keyinfo.ControlKeyState |= ControlKeyStates.CapsLockOn;
                    }
                    if ((kevent.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) {
            // Controls erzeugen
            Keyboard_Form form = new Keyboard_Form();
            Label label = new Label();
 
            // Am Label orientieren sich die Größen und Positionen
            // Dieses Control also zuerst fertigstellen
            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;
            // erst durch Add() wird die Größe des Labels ermittelt
            form.Controls.Add(label);
 
            // 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, 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;
 
        #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 Color.DarkBlue;
            case ConsoleColor.DarkGray:
                return Color.DarkGray;
            case ConsoleColor.DarkGreen:
                return Color.DarkGreen;
            case ConsoleColor.DarkCyan:
                return Color.DarkCyan;
            case ConsoleColor.DarkMagenta:
                return Color.DarkMagenta;
            case ConsoleColor.DarkRed:
                return Color.DarkRed;
            case ConsoleColor.DarkYellow:
                return ColorTranslator.FromOle(35723);//#8B8B00
            case ConsoleColor.Gray:
                return Color.Gray;
            case ConsoleColor.Green:
                return Color.Green;
            case ConsoleColor.Magenta:
                return Color.Magenta;
            case ConsoleColor.Red:
                return Color.Red;
            case ConsoleColor.White:
                return Color.White;
            default:
                return Color.Yellow;
            }
        }
 
        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 = System.AppDomain.CurrentDomain.FriendlyName;
            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 eventargs) { // 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;
            pd.objProgressBar.Style =
                #if noVisualStyles
                    ProgressBarStyle.Continuous
                #else
                    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 Progress_Form() {
            InitializeComponent();
        }
 
        public Progress_Form(ConsoleColor BarColor) {
            ProgressBarColor = BarColor;
            InitializeComponent();
        }
 
        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 = string.Format("Remaining time: {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
 
    // define IsInputRedirected(), IsOutputRedirected() and IsErrorRedirected() here since they were introduced first with .Net 4.5
    public class Console_Info {
        private enum FileType: uint {
            FILE_TYPE_UNKNOWN = 0x0000,
            FILE_TYPE_DISK = 0x0001,
            FILE_TYPE_CHAR = 0x0002,
            FILE_TYPE_PIPE = 0x0003,
            FILE_TYPE_REMOTE = 0x8000
        }
 
        private enum STDHandle: uint {
            STD_INPUT_HANDLE = unchecked((uint) - 10),
            STD_OUTPUT_HANDLE = unchecked((uint) - 11),
            STD_ERROR_HANDLE = unchecked((uint) - 12)
        }
        private enum ConsoleMode: uint {
            ENABLE_ECHO_INPUT = 0x0004,
            ENABLE_INSERT_MODE = 0x0020,
            ENABLE_LINE_INPUT = 0x0002,
            ENABLE_MOUSE_INPUT = 0x0010,
            ENABLE_PROCESSED_INPUT = 0x0001,
            ENABLE_QUICK_EDIT_MODE = 0x0040,
            ENABLE_WINDOW_INPUT = 0x0008,
            ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200,
 
            ENABLE_PROCESSED_OUTPUT = 0x0001,
            ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002,
            ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004,
            DISABLE_NEWLINE_AUTO_RETURN = 0x0008,
            ENABLE_LVB_GRID_WORLDWIDE = 0x0010
        }
 
        [DllImport("Kernel32.dll")]
        static private extern UIntPtr GetStdHandle(STDHandle stdHandle);
 
        [DllImport("Kernel32.dll")]
        static private extern FileType GetFileType(UIntPtr hFile);
        [DllImport("Kernel32.dll")]
        static private extern bool GetConsoleMode(UIntPtr hConsoleHandle, out ConsoleMode lpConsoleMode);
        [DllImport("Kernel32.dll")]
        static private extern bool SetConsoleMode(UIntPtr hConsoleHandle, ConsoleMode dwMode);
 
        static public bool IsInputRedirected() {
            UIntPtr hInput = GetStdHandle(STDHandle.STD_INPUT_HANDLE);
            FileType fileType = GetFileType(hInput);
            if ((fileType == FileType.FILE_TYPE_CHAR) || (fileType == FileType.FILE_TYPE_UNKNOWN))
                return false;
            return true;
        }
 
        static public bool IsOutputRedirected() {
            UIntPtr hOutput = GetStdHandle(STDHandle.STD_OUTPUT_HANDLE);
            FileType fileType = GetFileType(hOutput);
            if ((fileType == FileType.FILE_TYPE_CHAR) || (fileType == FileType.FILE_TYPE_UNKNOWN))
                return false;
            return true;
        }
 
        static public bool IsErrorRedirected() {
            UIntPtr hError = GetStdHandle(STDHandle.STD_ERROR_HANDLE);
            FileType fileType = GetFileType(hError);
            if ((fileType == FileType.FILE_TYPE_CHAR) || (fileType == FileType.FILE_TYPE_UNKNOWN))
                return false;
            return true;
        }
        static public bool IsVirtualTerminalSupported() {
            UIntPtr hOutput = GetStdHandle(STDHandle.STD_OUTPUT_HANDLE);
            ConsoleMode consoleMode;
            if(!GetConsoleMode(hOutput, out consoleMode))
                return false;
            return (consoleMode & ConsoleMode.ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0;
        }
    }
 
    internal class PSRunnerUI: PSHostUserInterface {
        public PSRunnerRawUI rawUI;
 
        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;
 
        public ConsoleColor ProgressForegroundColor =
        #if !noConsole
            ConsoleColor.Yellow
        #else
            ConsoleColor.DarkCyan
        #endif
        ;
        public ConsoleColor ProgressBackgroundColor = ConsoleColor.DarkCyan;
 
        public PSRunnerUI() {
            rawUI = new PSRunnerRawUI();
            #if !noConsole
                rawUI.ForegroundColor = Console.ForegroundColor;
                rawUI.BackgroundColor = Console.BackgroundColor;
            #endif
        }
 
        #if !Pwsh20
            public override bool SupportsVirtualTerminal { get { return Console_Info.IsVirtualTerminalSupported(); } }
        #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 = rawUI.WindowTitle, sMeldung = "";
 
                    if (!string.IsNullOrEmpty(caption)) sTitel = caption;
                    if (!string.IsNullOrEmpty(message)) sMeldung = message;
                    MessageBox.Show(sMeldung, sTitel);
                }
 
                // Titel und Labeltext für Input_Box zurücksetzen
                _ib_caption = "";
                _ib_message = "";
            #endif
            Dictionary<string, PSObject> ret = new Dictionary<string, PSObject> ();
            foreach(FieldDescription cd in descriptions) {
                Type type;
                if (string.IsNullOrEmpty(cd.ParameterAssemblyFullName))
                    type = typeof(string);
                else
                    type = Type.GetType(cd.ParameterAssemblyFullName);
 
                if (type.IsArray) {
                    Type elementType = type.GetElementType();
                    Type genericListType = Type.GetType("System.Collections.Generic.List\x60\x31");
                    genericListType = genericListType.MakeGenericType(new [] {
                        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 {
                        if (!string.IsNullOrEmpty(cd.Name))
                            #if !noConsole
                                Write(string.Format("{0}[{1}]: ", cd.Name, index));
                            #else
                                _ib_message = string.Format("{0}[{1}]: ", cd.Name, index);
                            #endif
                        data = ReadLine();
                        if (string.IsNullOrEmpty(data))
                            break;
                        object obj = System.Convert.ChangeType(data, elementType);
                        genericListType.InvokeMember("Add", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, resultList, new [] {
                            obj
                        });
                        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 obj=null;
                    string line;
                    if (type != typeof(System.Security.SecureString)) {
                        if (type != 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 {
                                line = ReadLine();
                                if (line == "!?")
                                    WriteLine(cd.HelpMessage);
                                else {
                                    if (string.IsNullOrEmpty(line)) obj = cd.DefaultValue;
                                    if (obj == null) {
                                        try {
                                            obj = System.Convert.ChangeType(line, type);
                                        } catch {
                                            Write("Wrong format, please repeat input: ");
                                            line = "!?";
                                        }
                                    }
                                }
                            } while (line == "!?");
                        } else
                            obj = PromptForCredential("", "", "", "");
                    } else {
                        if (!string.IsNullOrEmpty(cd.Name))
                            #if !noConsole
                                Write(string.Format("{0}: ", cd.Name));
                            #else
                                _ib_message = string.Format("{0}: ", cd.Name);
                            #endif
 
                        obj = ReadLineAsSecureString();
                    }
 
                    ret.Add(cd.Name, new PSObject(obj));
                }
            }
            #if noConsole
            // Titel und Labeltext für Input_Box zurücksetzen
            _ib_caption = "";
            _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(" ");
                    ConsoleColor fg = rawUI.ForegroundColor, bg = rawUI.BackgroundColor;
                    if (idx == defaultChoice) {
                        fg = VerboseForegroundColor;
                        defkey = lkey;
                    }
                    Write(fg, bg, 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 {/* ignore some read errors */}
                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 UserName;
            Write("User name: ");
            if ((string.IsNullOrEmpty(userName)) || ((options & PSCredentialUIOptions.ReadOnlyUserName) == 0))
                UserName = ReadLine();
            else {
                if (!string.IsNullOrEmpty(targetName)) Write(targetName + "\\");
                WriteLine(userName);
                UserName = userName;
            }
            Write("Password: ");
            SecureString password = ReadLineAsSecureString();
 
            if (string.IsNullOrEmpty(UserName)) UserName = "<NOUSER>";
            if (!string.IsNullOrEmpty(targetName))
                if (UserName.IndexOf('\\') < 0)
                    UserName = targetName + "\\" + UserName;
 
            return new PSCredential(UserName, password);
            #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;
                Write("User name: ");
                if (string.IsNullOrEmpty(userName))
                    un = ReadLine();
                else {
                    if (!string.IsNullOrEmpty(targetName)) Write(targetName + "\\");
                    WriteLine(userName);
                    un = userName;
                }
                SecureString pwd;
                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_caption;
        private string _ib_message;
        #endif
 
        public override string ReadLine() {
            #if !noConsole
                return Console.ReadLine();
            #else
                string sWert = "";
                if (Input_Box.Show(_ib_caption, _ib_message, ref sWert) == DialogResult.OK)
                    return sWert;
                #if exitOnCancel
                    Environment.Exit(1);
                #endif
                return "";
            #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;
            #if !noConsole
                secstr = getPassword();
            #else
                secstr = new System.Security.SecureString();
                string sWert = "";
 
                if (Input_Box.Show(_ib_caption, _ib_message, ref sWert, true) == DialogResult.OK) {
                    foreach(char ch in sWert)
                    secstr.AppendChar(ch);
                }
                #if exitOnCancel
                else
                    Environment.Exit(1);
                #endif
            #endif
            return secstr;
        }
 
        // called by Write-Host
        public override void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value) {
            #if !noOutput
            #if !noConsole
            ConsoleColor fgc = Console.ForegroundColor, bgc = Console.BackgroundColor;
            Console.ForegroundColor = foregroundColor;
            Console.BackgroundColor = backgroundColor;
            Console.Write(value);
            Console.ForegroundColor = fgc;
            Console.BackgroundColor = bgc;
            #else
            if ((!string.IsNullOrEmpty(value)) && (value != "\n"))
                MessageBox.Show(value, rawUI.WindowTitle);
            #endif
            #endif
        }
 
        public override void Write(string value) {
            #if !noOutput
            #if !noConsole
            Console.Write(value);
            #else
            if ((!string.IsNullOrEmpty(value)) && (value != "\n"))
                MessageBox.Show(value, rawUI.WindowTitle);
            #endif
            #endif
        }
 
        // called by Write-Debug
        public override void WriteDebugLine(string message) {
            #if !noError
            #if !noConsole
            WriteLineInternal(DebugForegroundColor, DebugBackgroundColor, string.Format("DEBUG: {0}", message));
            #else
            MessageBox.Show(message, rawUI.WindowTitle, MessageBoxButtons.OK, MessageBoxIcon.Information);
            #endif
            #endif
        }
 
        // called by Write-Error
        public override void WriteErrorLine(string value) {
            #if !noError
            #if !noConsole
            if (Console_Info.IsErrorRedirected())
                Console.Error.WriteLine(string.Format("ERROR: {0}", value));
            else
                WriteLineInternal(ErrorForegroundColor, ErrorBackgroundColor, string.Format("ERROR: {0}", value));
            #else
            MessageBox.Show(value, rawUI.WindowTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
            #endif
            #endif
        }
 
        public override void WriteLine() {
            #if !noOutput
            #if !noConsole
            Console.WriteLine();
            #else
            MessageBox.Show("", rawUI.WindowTitle);
            #endif
            #endif
        }
 
        public override void WriteLine(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value) {
            #if !noOutput
            #if !noConsole
            ConsoleColor fgc = Console.ForegroundColor, bgc = Console.BackgroundColor;
            Console.ForegroundColor = foregroundColor;
            Console.BackgroundColor = backgroundColor;
            Console.WriteLine(value);
            Console.ForegroundColor = fgc;
            Console.BackgroundColor = bgc;
            #else
            if ((!string.IsNullOrEmpty(value)) && (value != "\n"))
                MessageBox.Show(value, rawUI.WindowTitle);
            #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
            Console.WriteLine(value);
            #else
            if ((!string.IsNullOrEmpty(value)) && (value != "\n"))
                MessageBox.Show(value, rawUI.WindowTitle);
            #endif
            #endif
        }
 
        #if noConsole
        public Progress_Form pf;
        #endif
        public override void WriteProgress(long sourceId, ProgressRecord record) {
            #if noConsole
            if (pf == null) {
                if (record.RecordType == ProgressRecordType.Completed) return;
                pf = new Progress_Form(ProgressForegroundColor);
                pf.Show();
            }
            pf.Update(record);
            if (record.RecordType == ProgressRecordType.Completed) {
                if (pf.GetCount() == 0) pf = null;
            }
            #else
            if (!Console_Info.IsOutputRedirected())// Do not write progress bar when the stdout is redirected.
            {
                // OSC sequence to turn on progress indicator
                // https://github.com/microsoft/terminal/issues/6700
                if(Console_Info.IsVirtualTerminalSupported()){
                    if (record.RecordType == ProgressRecordType.Completed)//End progress indicator
                        Console.Write("\x1b]9;4;0\x1b\\");
                    else {
                        int percentComplete = record.PercentComplete;
                        // Write-Progress allows for negative percent complete, but not greater than 100
                        // but OSC sequence is limited from 0 to 100.
                        if (percentComplete < 0)
                            percentComplete = 0;
                        Console.Write(string.Format("\x1b]9;4;1;{0}\x1b\\", percentComplete));
                    }
                }
            }
            #endif
        }
 
        // called by Write-Verbose
        public override void WriteVerboseLine(string message) {
            #if !noOutput
            #if !noConsole
            WriteLine(VerboseForegroundColor, VerboseBackgroundColor, string.Format("VERBOSE: {0}", message));
            #else
            MessageBox.Show(message, rawUI.WindowTitle, MessageBoxButtons.OK, MessageBoxIcon.Information);
            #endif
            #endif
        }
 
        // called by Write-Warning
        public override void WriteWarningLine(string message) {
            #if !noError
                #if !noConsole
                    WriteLineInternal(WarningForegroundColor, WarningBackgroundColor, string.Format("WARNING: {0}", message));
                #else
                    MessageBox.Show(message, rawUI.WindowTitle, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                #endif
            #endif
        }
    }
 
    internal class PSRunnerHost: PSHost {
        private readonly PSRunnerInterface parent;
        private readonly PSRunnerUI _ui;
 
        private readonly CultureInfo originalCultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture;
 
        private readonly CultureInfo originalUICultureInfo = System.Threading.Thread.CurrentThread.CurrentUICulture;
 
        private Guid _myId = Guid.NewGuid();
 
        public PSRunnerHost(PSRunnerInterface app, PSRunnerUI ui) {
            this.parent = app;
            this._ui = ui;
        }
 
        public class ConsoleColorProxy {
            private readonly PSRunnerUI _ui;
 
            public ConsoleColorProxy(PSRunnerUI 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 "PSEXE"; }
        }
 
        public override PSHostUserInterface UI {
            get { return _ui; }
        }
 
        public override Version Version {
            get { return new Version(0, 0, 0, 0); }
        }
 
        public override void EnterNestedPrompt() {}
 
        public override void ExitNestedPrompt() {}
 
        public override void NotifyBeginApplication() {}
 
        public override void NotifyEndApplication() {}
 
        public override void SetShouldExit(int exitCode) {
            this.parent.ShouldExit = true;
            this.parent.ExitCode = exitCode;
        }
    }
 
    internal interface PSRunnerInterface {
        bool ShouldExit {get;set;}
        int ExitCode {get;set;}
    }
 
    internal class PSRunner: PSRunnerInterface {
        private bool shouldExit;
 
        private int exitCode;
        public bool Inited;
 
        public bool ShouldExit {
            get { return this.shouldExit; }
            set { this.shouldExit = value; }
        }
 
        public int ExitCode {
            get { return this.exitCode; }
            set { this.exitCode = value; }
        }
 
        public PSRunnerUI ui;
        public PSRunnerHost host;
        public Runspace PSRunSpace;
        public PowerShell pwsh;
 
        public PSRunner() {
            this.shouldExit = false;
            this.exitCode = 0;
            this.ui = new PSRunnerUI();
            this.host = new PSRunnerHost(this, ui);
            this.PSRunSpace = RunspaceFactory.CreateRunspace(host);
            this.PSRunSpace.ApartmentState = System.Threading.ApartmentState.$threadingModel;
            this.PSRunSpace.Open();
            this.pwsh = PowerShell.Create();
            this.pwsh.Runspace = PSRunSpace;
            string exepath = System.Reflection.Assembly.GetExecutingAssembly().Location;
            this.pwsh.Runspace.SessionStateProxy.SetVariable("PSEXEpath", exepath);
            Assembly executingAssembly = Assembly.GetExecutingAssembly();
            string script;
            using(System.IO.Stream scriptstream = executingAssembly.GetManifestResourceStream("main.ps1")) {
                using(System.IO.StreamReader scriptreader = new System.IO.StreamReader(scriptstream, System.Text.Encoding.UTF8)) {
                    script = scriptreader.ReadToEnd();
                    this.PSRunSpace.SessionStateProxy.SetVariable("PSEXEscript", script);
                }
            }
            script = "function PSEXEMainFunction{"+script+"}";
            #if Pwsh20
                this.pwsh.AddScript(script);
            #else
            {
                Token[] tokens;
                ParseError[] errors;
                ScriptBlockAst AST = Parser.ParseInput(script, exepath, out tokens, out errors);
                this.PSRunSpace.SessionStateProxy.SetVariable("PSEXEIniter", AST.GetScriptBlock());
                if(errors.Length > 0)
                    throw new System.InvalidProgramException(errors[0].Message);
                this.pwsh.AddScript(".$PSEXEIniter");
            }
            #endif
        }
        ~PSRunner() {
            this.pwsh.Dispose();
            this.PSRunSpace.Close();
            this.PSRunSpace.Dispose();
            this.host = null;
            this.ui = null;
        }
 
        //base init
        public static void BaseInit() {
            #if UNICODEEncoding && !noConsole
            System.Console.OutputEncoding = new System.Text.UnicodeEncoding();
            #endif
 
            #if culture
            System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("$lcid");
            System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo("$lcid");
            #endif
 
            #if !noVisualStyles && noConsole
            Application.EnableVisualStyles();
            #endif
        }
    }
    static class PSRunnerEntry {
        static PSRunner me;
 
        // EXEMain
        [$threadingModelThread]
        private static int Main(string[] args) {
            PSRunner.BaseInit();
            me = new PSRunner();
            System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false);
 
            try {
                #if !noConsole
                Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs eventargs) => {
                    try {
                        me.pwsh.BeginStop((_) => {
                            mre.Set();
                            eventargs.Cancel = true;
                        }, null);
                    } catch {
                        // ignore because we are shutting down
                    }
                };
                #endif
 
                me.pwsh.Streams.Error.DataAdded += (object sender, DataAddedEventArgs eventargs) => {
                    me.ui.WriteErrorLine(((PSDataCollection<ErrorRecord>) sender)[eventargs.Index].Exception.Message);
                };
 
                PSDataCollection<string> colInput = new PSDataCollection<string> ();
                if (Console_Info.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 += (object sender, DataAddedEventArgs eventargs) => {
                    me.ui.WriteLine(((PSDataCollection<PSObject>) sender)[eventargs.Index].ToString());
                };
 
                for(int i = 0; i < args.Length; i++) {
                    if (!Regex.IsMatch(args[i], @"^(-|\$)\w*$"))
                        args[i] = "\'"+args[i].Replace("'", "''")+"\'";
                }
 
                me.pwsh.AddScript("$Input|PSEXEMainFunction "+String.Join(" ", args)+"|Out-String -Stream");
 
                me.pwsh.BeginInvoke<string, PSObject> (colInput, colOutput, null, (IAsyncResult ar) => {
                    if (ar.IsCompleted)
                        mre.Set();
                }, null);
 
                while (!mre.WaitOne(100))
                    if (me.ShouldExit) break;
 
                me.Inited = true;
                me.pwsh.Stop();
 
                if (me.pwsh.InvocationStateInfo.State == PSInvocationState.Failed)
                    me.ui.WriteErrorLine(me.pwsh.InvocationStateInfo.Reason.Message);
            }
            catch (Exception ex) {
                #if !noError
                    me.ui.WriteErrorLine(ex.Message);
                #endif
                me.ExitCode = 1;
            }
            finally {
                #if !Pwsh20 // bro wtf
                    mre.Dispose();
                #endif
            }
 
            return me.ExitCode;
        }
    }
}