Bjorn
Bjorn

Reputation: 21

Problems converting ps1 to exe using PowerGUI

I'm trying to convert my PowerShell project to an executable program (.exe). After some research I found PowerGUI. After converting my .ps1 file into an exe I ran into some problems:

Firstly it takes ages to start the program (about 15 seconds), is this normal or is there something I can do to improve this?

Secondly, if i exit the program I get a windows error message saying the program stopped working unexpectedly. Is there a way to hide this message?

Here is my ps1 code, I got a part of it from a blog and it is my first PowerShell code so don't be to harsh on me ;)

 $inputXML = @"
<Window x:Class="BlogPostIII.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BlogPostIII"
        mc:Ignorable="d"
        Title="Organizer" Height="540" Width="540" FontSize="18.667">
    <Grid x:Name="background">
        <Button x:Name="OK" Content="OK" HorizontalAlignment="Left" Height="41" Margin="420,458,0,0" VerticalAlignment="Top" Width="100" FontSize="18.667"/>
        <Button x:Name="Cancel" Content="Cancel" HorizontalAlignment="Left" Height="41" Margin="315,458,0,0" VerticalAlignment="Top" Width="100" FontSize="18.667"/>
        <TextBox x:Name="TextBox1" HorizontalAlignment="Left" Height="30" Margin="108,216,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
        <TextBlock x:Name="TextBlock1" HorizontalAlignment="Left" Height="30" Margin="108,36,0,0" TextWrapping="Wrap" Text="Soort bewerking:" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
        <TextBox x:Name="TextBox2" HorizontalAlignment="Left" Height="30" Margin="108,291,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
        <TextBlock x:Name="TextBlock2" HorizontalAlignment="Left" Height="30" Margin="108,111,0,0" TextWrapping="Wrap" Text="Naam Machine:" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
        <TextBox x:Name="TextBox3" HorizontalAlignment="Left" Height="30" Margin="108,366,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
        <TextBlock x:Name="TextBlock3" HorizontalAlignment="Left" Height="30" Margin="108,185,0,0" TextWrapping="Wrap" Text="Naam van opdrachtgevend bedrijf:" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
        <TextBlock x:Name="TextBlock4" HorizontalAlignment="Left" Height="30" Margin="108,261,0,0" TextWrapping="Wrap" Text="Naam product:" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
        <TextBlock x:Name="TextBlock5" HorizontalAlignment="Left" Height="30" Margin="108,336,0,0" TextWrapping="Wrap" Text="Product ID:" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
        <ComboBox x:Name="combobox1" HorizontalAlignment="Left" Margin="108,66,0,0" VerticalAlignment="Top" Width="300"/>
        <ComboBox x:Name="combobox2" HorizontalAlignment="Left" Margin="108,140,0,0" VerticalAlignment="Top" Width="300"/>
    </Grid>
</Window>
"@        

$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N'  -replace '^<Win.*', '<Window'


[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML
#Read XAML

    $reader=(New-Object System.Xml.XmlNodeReader $xaml) 
  try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch{Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."}

#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================

$xaml.SelectNodes("//*[@Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}

Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}

Get-FormVariables

#===========================================================================
# List the Comboboxes
#===========================================================================

$WPFcombobox1.AddText('Draaien')
$WPFcombobox1.AddText('Frezen')
$WPFcombobox1.AddText('Slijpen')

$WPFcombobox2.AddText('Doosan 3100LM')
$WPFcombobox2.AddText('Doosan 123')
$WPFcombobox2.AddText('machine 3')
$WPFcombobox2.AddText('machine 4')
$WPFcombobox2.AddText('machine 5')


#===========================================================================
# Actually make the objects work
#===========================================================================


#$WPFMakeUserbutton.Add_Click({(Get-FormFields)})


$WPFOK.Add_Click({
    $1 = $WPFcomboBox1.Text
    $2 = $WPFcomboBox2.Text
    $3 = $WPFtextBox1.Text
    $4 = $WPFtextBox2.Text
    $5 = $WPFtextBox3.Text + " Werkblad"
    New-Item C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4\ -Force -type directory
    Copy-Item C:\Users\Bjorn\Documents\Powershell\Test_werkblad.docx C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4\
    Rename-Item C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4\Test_werkblad.docx C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4\$5.docx
    Invoke-Item C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4
    Invoke-Item C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4\$5.docx
    $Form.Close()})

$WPFCancel.Add_Click({
    $Form.Close()})


#===========================================================================
# Shows the form
#===========================================================================
write-host "To show the form, run the following" -ForegroundColor Cyan

function Show-Form{
$Form.ShowDialog() | out-null

}

Show-Form

Upvotes: 1

Views: 1459

Answers (2)

Mr. Annoyed
Mr. Annoyed

Reputation: 571

The 15 second pause is PowerShell initializing in the background. This is especially noticeable on Windows 7

CAVEAT: "compiling" PowerShell to EXE in most cases (PowerGUI included) is stuffing your original PS1 file inside a Self-extracting EXE. You could do the same with 7-Zip or WinZip.

A PowerGUI "compiled" EXE locks your script execution to whatever version or PowerShell + .Net you had on your DEV box. IOW: If you compile on a PC with PowerShell v4 but only use Write-host in your script (for example), the target PCs will need PowerShell v4+ for it to run!

Upvotes: 1

Oscar Foley
Oscar Foley

Reputation: 7025

About the 15 seconds I am not sure why. Could you give more details? You should add logging and put time stamp in every message... This way you should be able to locate slow part.

About avoiding the error message, you should surround your code with try/catch. For example:

    [...]

    write-host "To show the form, run the following" -ForegroundColor Cyan

    function Show-Form
    {
     $Form.ShowDialog() | out-null
    }

    try
    {
     Show-Form
    }
    catch
    {
     $ErrorMessage = $_.Exception.Message
     # Show friendly message with error and/or log the error)
    }

After reading your code here is some extra advice that could be useful:

  • Do not hardcode paths. (Like "C:\Users\Bjorn\Documents\Powershell\")
  • Do you really need PowerShell? Sounds like C# WinForms would be better for this task.
  • Store XML in a file and read it rather having it in code in $inputXML variable.

Upvotes: 1

Related Questions