1

I have created a GUI for my script. The user selects from two lists, and then clicks a button to load the programs. In ISE, everything works fine. However, when run from a batch file, (or *ps1 file directly) it opens the GUI ... but when you click the button, it cannot find the function "RunOption" (???)

$tmp = Split-Path $script:MyInvocation.MyCommand.Path;
$ScriptDir=Split-Path $tmp  ## To move back up to parent folder from path quoted

if (!(Test-Path $ScriptDir/fnt_files/fontList.csv)){
    $reg = Get-ItemProperty "REGISTRY::HKLM\SOFTWARE\MICROSOFT\Windows NT\CurrentVersion\Fonts"
    $data = $reg.PSObject.Properties.Where({$_.MemberType -eq "NoteProperty" -and $_.Value -match "\..{3}$"}) | Select-Object Name, Value
    $data | Export-Csv -Path $ScriptDir/fnt_files/fontList.csv -NoTypeInformation
}

Add-Type -assembly System.Windows.Forms
$main_form = New-Object System.Windows.Forms.Form
$main_form.Text ='Subtitle Software Suite'
$main_form.Width = 450
$main_form.Height = 190
$main_form.BackColor='cyan'


$Label = New-Object System.Windows.Forms.Label
$Label.Text = "Select Browser to use:"
$Label.Location  = New-Object System.Drawing.Point(10,10)
$Label.Width = 150
$main_form.Controls.Add($Label)

$ComboBox = New-Object System.Windows.Forms.ComboBox
$ComboBox.Width = 100

$FileNames = Get-ChildItem -Path Registry::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths"|Select-Object Name

    if ($FileNames -Match "firefox.exe"){
    $ComboBox.Items.Add('Firefox');
    }
    if ($FileNames -Match "chrome.exe"){
    $ComboBox.Items.Add('Chrome');
    }
     if ($FileNames -Match "edge.exe"){
    $ComboBox.Items.Add('Edge');
    }
    if ($FileNames -Match 'opera.exe'){
     $ComboBox.Items.Add('Opera');
    }
    

$ComboBox.Location  = New-Object System.Drawing.Point(160,10)
$main_form.Controls.Add($ComboBox)

$OptLabel = New-Object System.Windows.Forms.Label
$OptLabel.Text = "Select an option"
$OptLabel.Location  = New-Object System.Drawing.Point(10,50)
$OptLabel.Width = 150
$main_form.Controls.Add($OptLabel)

$Options = New-Object System.Windows.Forms.ComboBox
$Options.Width = 250
$Options.Items.Add('Select Fonts to use')
$Options.Items.Add('Audio To Text -Transcribe-')
$Options.Items.Add('Caption Timecode + Font Viewer')
$Options.Items.Add('SRT using FFMPEG')
$Options.Items.Add('SSA using FFMPEG')
$Options.Items.Add('SSA using FFMPEG + Drawbox')
$Options.Items.Add('Start Handbrake')

$Options.Location  = New-Object System.Drawing.Point(160,50)
$main_form.Controls.Add($Options)
  
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(290,90)
$Button.Size = New-Object System.Drawing.Size(120,25)
$Button.Text = "Load Software"
$Button.height=40
$Button.BackColor='red'
$Button.ForeColor='white'
$Button.Add_Click({RunOption})
$main_form.Controls.Add($Button)

$main_form.StartPosition = "manual"
$main_form.Location = New-Object System.Drawing.Size(500, 300)

$main_form.ShowDialog()

function RunOption(){
$browser=$ComboBox.SelectedIndex;
$choice=$Options.SelectedIndex;

    switch ($browser){
        0{
        $browserChoice='firefox'
        }
        1{
        $browserChoice='chrome'
        }
        2{
        $browserChoice='msedge'
        }
        3{
        $browserChoice='opera'
        }
        }
write-host $browserchoice

   switch ( $choice ) {      
        1{                              # Select fonts
        Start-Process $browserChoice "file:$ScriptDir/fnt_files/fontSelect.html" -WindowStyle Maximized  
        }

        2{                              # Audio to text transcribe
        Start-Process $browserChoice "file:$ScriptDir/Vid2Txt/vidTranscript.html" -WindowStyle Maximized
         }
        3{                             # Captions plus timecode
       Start-Process $browserChoice "file:$ScriptDir/fnt_files/fontView.html" -WindowStyle Maximized
       Start-Process $browserChoice "file:$ScriptDir/standard.html"  -WindowStyle Maximized
         }
        
        4{                             # SRT using FFMPEG
        #-WindowStyle Maximized
        write-host "You entered SRT FFMMEG"
        }

        5{                             # SSA using FFMPEG
        #-WindowStyle Maximized
        write-host "You entered SSA with FFMPEG"
        }

        6{                             # SSA using FFMEG+Drawbox
        #-WindowStyle Maximized
        write-host "You entered FFMPG-DRAWBOX"
        }

        7{                             # Start handbrake
        Start-Process "C:\Windows\Handbrake\handbreak.exe"
        }
    }
    

$main_form.Close();
}  

(When run, the console lists 0120123456 in a column - which I can see is something to do with the switch commands, but no idea why they show)

For completeness, the batch is:

@ECHO OFF
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%sm.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%'";

Done that way as I don't know where user will store program on their PC. The batch is called from a cmd shortcut (so I can change the desktop icon!)

This is my first Powershell script, so be kind to me :-)

  • powershell runs code IN SEQUENCE. [grin] you define RunOption AFTER you use it ... and that will only work after the code has been run once. in the ISE, you likely had run the code at least one time before successfully calling that function. ///// the fix for that sequence problem is to define things before you use them. – Lee_Dailey Jul 8 at 13:15
  • I'll bear that in mind, and shift the function up. Not like PERL or javascript then where doesn't matter where functions are, as long as they're somewhere in the code. Thanks again ... but see the 'lesson' I've been given by contributor below. – Chris Brown Jul 9 at 11:02
  • yes, PoSh is rather different in some ways. everything is handled in sequence - not "scan 1st, then run". ///// i see that postanote has written a detailed "note" on the various things to keep in mind. glad to know that you got things working as needed ... [grin] – Lee_Dailey Jul 9 at 14:09
1

Code refactored.

Semicolons, like you are using them are not needed in PowerShell. It is a habit brought over from other languages. PowerShell ignores/sees them as code terminators.

So, semicolons denote code separation where stuff on either side of one have no operational relationship. and are really only needed when you put different code on the same line. See PowerShell calculated properties, Hash table, PSCustomObjects for examples.

FYI... Semicolons with all code on the same line do not make that line a one-liner.

PowerShell MS Docs

See also:

Use single quotes for simple strings, and double for variable expansion and a few other output formatted needs.

When it comes to drive navigation, Powershell provides native FIleSytem properties for that as well, even for the registry. See Get-PSDrive help for details.

# Get specifics for a module, cmdlet, or function
(Get-Command -Name Get-PSDrive).Parameters
(Get-Command -Name Get-PSDrive).Parameters.Keys
Get-help -Name Get-PSDrive -Examples
Get-help -Name Get-PSDrive -Full
Get-help -Name Get-PSDrive -Online

(Get-PSDrive).Provider | 
Format-Table -AutoSize
# Results
<#

Name        Capabilities                       Drives               
----        ------------                       ------               
Alias       ShouldProcess                      {Alias}              
FileSystem  Filter, ShouldProcess, Credentials {C, ...}
Certificate ShouldProcess                      {Cert}               
FileSystem  Filter, ShouldProcess, Credentials {C, ...}
FileSystem  Filter, ShouldProcess, Credentials {C, ...}
Environment ShouldProcess                      {Env}                
FileSystem  Filter, ShouldProcess, Credentials {C, ...}
Function    ShouldProcess                      {Function}           
FileSystem  Filter, ShouldProcess, Credentials {C, ...}
FileSystem  Filter, ShouldProcess, Credentials {C, ...}
Registry    ShouldProcess, Transactions        {HKLM, HKCU}         
Registry    ShouldProcess, Transactions        {HKLM, HKCU}         
FileSystem  Filter, ShouldProcess, Credentials {C, ...}
Variable    ShouldProcess                      {Variable}           
WSMan       Credentials                        {WSMan}              
#>

The code alignment is just my habit for readability.

You can change the shortcut icon of PowerShell to whatever you'd like, batch file, or not.

Add-Type -assembly System.Windows.Forms

#region Begin GUI code ############################################

$main_form               = New-Object System.Windows.Forms.Form
$main_form.Text          ='Subtitle Software Suite'
$main_form.Width         = 450
$main_form.Height        = 190
$main_form.BackColor     ='cyan'


$Label                   = New-Object System.Windows.Forms.Label
$Label.Text              = 'Select Browser to use:'
$Label.Location          = New-Object System.Drawing.Point(10,10)
$Label.Width             = 150

$ComboBox                = New-Object System.Windows.Forms.ComboBox
$ComboBox.Width          = 100
$ComboBox.Location       = New-Object System.Drawing.Point(160,10)


$OptLabel                = New-Object System.Windows.Forms.Label
$OptLabel.Text           = 'Select an option'
$OptLabel.Location       = New-Object System.Drawing.Point(10,50)
$OptLabel.Width          = 150


$Options                 = New-Object System.Windows.Forms.ComboBox
$Options.Width           = 250
@(
'Select Fonts to use',
'Audio To Text -Transcribe-',
'Caption Timecode + Font Viewer',
'SRT using FFMPEG',
'SSA using FFMPEG',
'SSA using FFMPEG + Drawbox',
'Start Handbrake'
) | 
ForEach-Object {[void] $OPtions.Items.Add($PSItem)}


$Options.Location        = New-Object System.Drawing.Point(160,50)
  
$Button                  = New-Object System.Windows.Forms.Button
$Button.Location         = New-Object System.Drawing.Size(290,90)
$Button.Size             = New-Object System.Drawing.Size(120,25)
$Button.Text             = 'Load Software'
$Button.height           = 40
$Button.BackColor        = 'red'
$Button.ForeColor        = 'white'
$Button.Add_Click({Start-RunOption})

$main_form.StartPosition = 'manual'
$main_form.Location      = New-Object System.Drawing.Size(500, 300)


$main_form.controls.AddRange(@(
        $Label,
        $ComboBox,
        $OptLabel,
        $Options,
        $Button
    )
)


$tmp       = Split-Path $script:MyInvocation.MyCommand.Path
$ScriptDir = Split-Path $tmp

if (!(Test-Path $ScriptDir/fnt_files/fontList.csv))
{
    $reg  = Get-ItemProperty -Path 'HKLM:\SOFTWARE\MICROSOFT\Windows NT\CurrentVersion\Fonts'
    $data = $reg.PSObject.Properties.Where({$PSItem.MemberType -eq 'NoteProperty' -and 
            $PSItem.Value -match '\..{3}$'}) | 
            Select-Object Name, Value

    $data | 
    Export-Csv -Path "$ScriptDir/fnt_files/fontList.csv -NoTypeInformation"
}

$FileNames = Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths' | 
             Select-Object Name

'firefox.exe', 'chrome.exe', 'edge.exe', 'opera.exe' | 
ForEach {
    if ($FileNames -Match $PSItem)
    {
        $Combobox.Items.Add($PSItem) | 
        Out-Null
    }
}

#endregion End GUI code ############################################


#region Begin app logic code############################################

function Start-RunOption()
{
    $ComboBox.SelectedItem | 
    Out-Host

    $Options.SelectedItem | 
    Out-Host

<#
$StartProcessSplat = @{
    FilePath     = $ComboBox.SelectedItem 
    ArgumentList = $Options.SelectedItem 
    # WindowStyle  = Maximized
}
Start-Process @StartProcessSplat
#>


<#
    switch ($browser)
    {
        0 {$browserChoice = 'firefox'}
        1 {$browserChoice = 'chrome'}
        2 {$browserChoice = 'msedge'}
        3 {$browserChoice = 'opera'}
    }


   switch ( $choice ) 
   {      
        1 {Start-Process $browserChoice "file:$ScriptDir/fnt_files/fontSelect.html" -WindowStyle Maximized }
        2 {Start-Process $browserChoice "file:$ScriptDir/Vid2Txt/vidTranscript.html" -WindowStyle Maximized}
        3 {                            
           Start-Process $browserChoice "file:$ScriptDir/fnt_files/fontView.html" -WindowStyle Maximized
           Start-Process $browserChoice "file:$ScriptDir/standard.html"  -WindowStyle Maximized
          }
        
        4 {'You entered SRT FFMMEG'}
        5 {'You entered SSA with FFMPEG'}
        6 {'You entered FFMPG-DRAWBOX'}
        7 {Start-Process 'C:\Windows\Handbrake\handbreak.exe'}
    }
#>

}

#endregion End app logic code############################################

# App load
$main_form.ShowDialog()
$main_form.Close()

Running this...

PowerShell -NoProfile -ExecutionPolicy Bypass -Command "D:\Scripts\formtest.ps1"

... from cmd.exe, runs the code as expected.

# Results from the function and closing the form
#>
<#
firefox.exe
Select Fonts to use
Cancel
#>

So, as you can see, you really don't need the switch case, just calling the .SelectedItem or even the .Text properties will give you what you need.

Long Update to cover all your comments. ;-}

WOW! Most detailed response I've EVER had ...

--- No worries, as a MCT (Microsoft Certified Trainer), I always try to explain what could be of interest.

and you've re-written the code, so I can study how if should have been done.

--- This is just a set of suggestions with some corrections. Of course, you need to make changes and test as I cannot test your complete use case. There many docs/videos on Youtube on UX/UI (WinForms and WPF) and PowerShell GUI design and PowerShell Best Practice.

So look into Set-StrictMode, the #requires statement, PSScriptAnalyzer and Best practices.

There are a bunch of Best/Accepted Practices info, but it's really all about choices, habits, and goals. Here are just a few.

Each of the above, IMHO have their pros and cons, and I don't agree with all of it as some take some convoluted approaches to justify their positions. Again, it's all about beliefs, choice and style acceptance. You choose what works for you, your team, your customers, and ignore other stuff/people.

When it comes to GUI design, let the tools do most of the work. See my answers on this thread.

As I plan to release the broswer based subtitling software, would you like a credit in the guide, maybe to a business website or similar?

--- Well, sure, and we can connect offline about that, because on the forums I prefer to be anonymous. Yet there are tools super user to have a direct chat vs here.

Oh, semi-colons; throwback to PERL.

--- Not just a PERL thing. I come from a VB/VB.Net/C#/JavaScript developer background, so, I had to get out os that habit as well.

Will have to look at shortcuts. I only know about using CMD.exe as everything else doesn't seem to have the button to change the icon

-- You can export shortcuts form anywhere or create your own to use. I do/show this all the time in classes I deliver and in enterprise engagements.

(Sorry for third comment) First time of using switch. Normally, I would have done a series of "If--then" conditions Oh, and your "running this" wouldn't work as it specifies the "D" drive. I don't know where the user will be installing the files, so got to make the run time 'generic'

--- No worries, I choose a real drive just to test the code. THat PSSCriptRoot thing is a common approach. I was not indicating in any way, that you'd use a specific drive.

When I write code, I keep things as simple as possible during dev, then refactor for distribution to allow for dynamic/unknown deployment scenarios. This way I know what does and does not work natively/statically. So, if it won't work in that state, then it won't work dynamically. If it works statically and fails dynamically then it's a potential environmental thing or the approach in use.

Just read those links, and makes for some very interesting reading

--- Glad to know they help

Have just tried the script, and not quite there. (Nice neat layout, so will try to use that in future) It outputs the options to console - as expected by Out-Host ...

--- Just remove the Out-Host. I put that there but it there to show that what you needed to get was as expected. Never output if you don't have to. Just know this when it comes to silencing output.

See:

doesn't start the selected browser, and load the HTML pages, which is the reason for the switch statements dependant on what browser they choose.

--- Again, I was just showing how you can get the results without extra code, but to execute other lines, sure, you are going tot have to code for that.

So I will still need to set browser and choice. Also, it doesn't now close window / script once button clicked and browsers opened. Don't think this is an "ungreatful" comment - it's not. Just adding a few glitches. –

--- No worries. It's just info to get you going. You make further changes to your use case, only you'd know what that is.

| improve this answer | |
  • WOW! Most detailed response I've EVER had ... and you've re-written the code, so I can study how if should have been done. As I plan to release the broswer based subtitling software, would you like a credit in the guide, maybe to a business website or similar? Oh, semi-colons; throwback to PERL. (I shall now study your re-written program to see how it SHOULD have been done. Thanks again – Chris Brown Jul 9 at 11:08
  • Will have to look at shortcuts. I only know about using CMD.exe as everything else doesn't seem to have the button to change the icon – Chris Brown Jul 9 at 11:11
  • (Sorry for third comment) First time of using switch. Normally, I would have done a series of "If--then" conditions Oh, and your "running this" wouldn't work as it specifies the "D" drive. I don't know where the user will be installing the files, so got to make the run time 'generic' – Chris Brown Jul 9 at 11:12
  • Just read those links, and makes for some very interesting reading - especially the double and single quotes, execute mode or literal mode etc. THIS HAS BEEN MOST HELPFUL – Chris Brown Jul 9 at 12:42
  • Have just tried the script, and not quite there. (Nice neat layout, so will try to use that in future) It outputs the options to console - as expected by Out-Host ... but it doesn't start the selected browser, and load the HTML pages, which is the reason for the switch statements dependant on what browser they choose. So I will still need to set browser and choice. Also, it doesn't now close window / script once button clicked and browsers opened. Don't think this is an "ungreatful" comment - it's not. Just adding a few glitches. – Chris Brown Jul 9 at 14:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.