Can`t open a form in another thread.

Ask questions about creating Graphical User Interfaces (GUI) in PowerShell and using WinForms controls.
Forum rules
Do not post any licensing information in this forum.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
Post Reply
TolikTipaTut1
Posts: 8
Joined: Mon Oct 07, 2019 10:44 am

Can`t open a form in another thread.

Post by TolikTipaTut1 » Mon Oct 07, 2019 10:53 am

Hello!
I`m creating a Form in another thread using powershell.
I created my own class

Code: Select all

class MainForm : System.Windows.Forms.Form {
<#some code#>
}
Form name is "Start_Window". I`m using "ShowDialog()" method to show it.
If i run my script in ISE, "Start_Window" appears.
But if i run my script using Powershell Studio 2019, nothing happens ...
This is my code:

Code: Select all

$Start_Window_syncHash = [hashtable]::Synchronized(@{})
$Start_Window_Runspace = [runspacefactory]::CreateRunspace()
$Start_Window_Runspace.ApartmentState = "STA"
$Start_Window_Runspace.ThreadOptions = "ReuseThread"
$Start_Window_Runspace.Open()
$Start_Window_Runspace.SessionStateProxy.SetVariable("Start_Window_syncHash",$Start_Window_syncHash)
$Start_Window_Cmd = [PowerShell]::Create().AddScript({
class MainForm : System.Windows.Forms.Form {
<#some code#>
}
<#some code#> ....
$Start_Window_syncHash.Start_Window = [MainForm]::new()
$Start_Window_syncHash.Start_Window.ShowDialog() | Out-Null
$Start_Window_syncHash.Error = $Error
})
$Start_Window_Cmd.Runspace = $Start_Window_Runspace
$Start_Window_data = $Start_Window_Cmd.BeginInvoke()

User avatar
jvierra
Posts: 13793
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: Can`t open a form in another thread.

Post by jvierra » Mon Oct 07, 2019 12:13 pm

Your code shows a windows for me. Just copy and paste into a console but first add this line:
Add-Type -AssemblyName System.windows.Forms

The following will give you some idea of how this is done and how to get information back after the form has closed. Try not to use the synchash when not needed.

Code: Select all

$scriptCode = {

	class MainForm : System.Windows.Forms.Form {
		MainForm(){
			$this.StartPosition = 'CenterScreen'
			$b = [System.Windows.Forms.Button]@{Name='buttonTest';Text='Test Form'}
			$this.Controls.Add($b)
			$this.Controls['buttonTest'].add_Click({Write-Host 'hello'})
		}
	}
			
	<#some code#>
	$form = [MainForm]::new()
	$syncHash.Form = $form 
	$form.ShowDialog() | Out-Null
}

Add-Type -AssemblyName System.Windows.Forms

$syncHash = [hashtable]::Synchronized(@{})
$runspace = [runspacefactory]::CreateRunspace()
$runspace.ApartmentState = 'STA'
$runspace.ThreadOptions = 'ReuseThread'
$runspace.Open()
$runspace.SessionStateProxy.SetVariable('syncHash',$syncHash)
$ps = [PowerShell]::Create().AddScript($scriptCode)
$ps.Runspace = $runspace
$asyncResult = $ps.Invoke()
$ps.Streams.Information
$ps.Streams.Error

User avatar
jvierra
Posts: 13793
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: Can`t open a form in another thread.

Post by jvierra » Mon Oct 07, 2019 12:37 pm

This is how to use InvokeAsync to collect data returned.

Code: Select all

$scriptCode = {

	class MainForm : System.Windows.Forms.Form {
		MainForm(){
			$this.StartPosition = 'CenterScreen'
			$this.add_Load({
            })
			$b = [System.Windows.Forms.Button]@{Name='buttonTest';Text='Test Form'}
			$this.Controls.Add($b)
			$this.Controls['buttonTest'].add_Click({Write-Host 'hello'})
		}
		
	}
			
	<#some code#>
	$form = [MainForm]::new()
	$syncHash.Form = $form 
	$form.ShowDialog() | Out-Null
	# this will bcome output
	'This is an output message'
}

Add-Type -AssemblyName System.Windows.Forms

$syncHash = [hashtable]::Synchronized(@{})
$runspace = [runspacefactory]::CreateRunspace()
$runspace.ApartmentState = 'STA'
$runspace.ThreadOptions = 'ReuseThread'
$runspace.Open()
$runspace.SessionStateProxy.SetVariable('syncHash',$syncHash)
$ps = [PowerShell]::Create().AddScript($scriptCode)
$ps.Runspace = $runspace
$asyncResult = $ps.BeginInvoke()
pause  # let this pause until you close the form
$ps.EndInvoke($asyncResult)  # regular output is collected here

# alternate streams are here
$ps.Streams.Information
$ps.Streams.Error
$ps.Streams.Verbose
# ... etc

TolikTipaTut1
Posts: 8
Joined: Mon Oct 07, 2019 10:44 am

Re: Can`t open a form in another thread.

Post by TolikTipaTut1 » Tue Oct 08, 2019 3:32 am

jvierra, thank you for your answer!

But I still don`t understand...
In your code you used "$ps.Invoke()" method to open the window. This method opens a window. But while the window is open, the code should execute further in another thread... And if I use "Invoke()" method instead "BeginInvoke()", I should close the window to continue executing the code...

My code is below

Code: Select all

Add-Type -AssemblyName System.Windows.Form
function Main
{
try
{
[xml]$Script_Conf = Get-Content .\Main_Conf.xml -ErrorAction Stop
Write-Host "Конфигурационный файл приложения существует! Запускаю выполнение!" -ForegroundColor Green
$Plane_Pattern_Path = $Script_Conf.config.Plane_Pattern_Path
}
Catch [System.Management.Automation.ItemNotFoundException] {
Write-Host "В папке со скриптом не найден конфигурационный приложения!" -ForegroundColor Red
Write-Host "Переместите конфигурационный файл в папку, где будет запусакться скрипт!" -ForegroundColor Red
Write-Host "Прекращаю работу..." -ForegroundColor Red
return;
}

$Start_Window_syncHash = [hashtable]::Synchronized(@{ })
$Start_Window_Runspace = [runspacefactory]::CreateRunspace()
$Start_Window_Runspace.ApartmentState = "STA"
$Start_Window_Runspace.ThreadOptions = "ReuseThread"
$Start_Window_Runspace.Open()
$Start_Window_Runspace.SessionStateProxy.SetVariable("Start_Window_syncHash", $Start_Window_syncHash)
$Start_Window_Cmd = [PowerShell]::Create().AddScript({
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
param ([xml]$_config,
[string]$_plane_pattern_path)
Class StartModeling: System.Windows.Forms.Button
{
[System.Windows.Forms.Button]$Butt

[void] Init ()
{
$this.Location = [System.Drawing.Point]::new(100, 70)
$this.Size = [System.Drawing.Size]::new(200, 60)
$this.Font = [System.Drawing.Font]::new('Times New Roman', 14, [System.Drawing.FontStyle]::Bold)
$this.Text = 'Начать моделирование'
}

StartModeling ()
{
$this.Init()
}

[void] Check_Config ()
{

}
}

Class ShowMe: System.Windows.Forms.Button
{
[System.Windows.Forms.Button]$Butt

[void] Init ()
{
$this.Size = [System.Drawing.Size]::new(200, 60)
$this.Location = [System.Drawing.Point]::new(100, 250)
$this.Text = 'Автор'
$this.Font = [System.Drawing.Font]::new('Times New Roman', 14, [System.Drawing.FontStyle]::Bold)
$this.Add_Click({
$this.ShowAuthor()
})
}

ShowMe ()
{
$this.Init()
}

[Void] ShowAuthor ()
{
[System.Windows.Forms.MessageBox]::SHow("Автор: Я.Я. Месенгисер", "Автор")
}
}

Class ShowPatterns: System.Windows.Forms.Button
{
[System.Windows.Forms.Button]$Butt

[void] Init ()
{
$this.Size = [System.Drawing.Size]::new(200, 60)
$this.Location = [System.Drawing.Point]::new(100, 160)
$this.Font = [System.Drawing.Font]::new('Times New Roman', 14, [System.Drawing.FontStyle]::Bold)
$this.Text = 'Настроить шаблоны'
}

ShowPatterns ()
{
$this.Init()
}
}

Class MainForm: System.Windows.Forms.Form
{
[StartModeling]$StartModeling
[ShowMe]$ShowAuthor
[ShowPatterns]$ShowPatterns
[System.Windows.Forms.Form]$PatConf

[void] Init ()
{
$this.Size = [System.Drawing.Size]::new(400, 500)

$this.StartModeling = [StartModeling]::new()
$this.ShowAuthor = [ShowMe]::new()
$this.ShowPatterns = [ShowPatterns]::new()

$this.ShowPatterns.Add_Click({
$this.Parent.PatConf.ShowDialog()
})

$this.Controls.Add($this.StartModeling)
$this.Controls.Add($this.ShowAuthor)
$this.Controls.Add($this.ShowPatterns)
}

MainForm ([System.Windows.Forms.Form]$_patConf)
{
$this.Init()
$this.PatConf = $_patConf
}
}

Class CreatePattern: System.Windows.Forms.Button
{
[void] Init ()
{
$this.Location = [System.Drawing.Point]::new(50, 300)
$this.Size = [System.Drawing.Size]::new(250, 30)
$this.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$this.Text = "Создать шаблон"
}

CreatePattern ()
{
$this.Init()
}
}

Class UpdatePattern: System.Windows.Forms.Button
{

[bool]$IsShown

[void] Init ()
{
$this.Location = [System.Drawing.Point]::new(370, 300)
$this.Size = [System.Drawing.Size]::new(250, 30)
$this.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$this.Text = "Просмотр шаблона"
}

UpdatePattern ()
{
$this.Init()
}

ViewPatternConf ([xml]$PatternConf, [System.Windows.Forms.DataGridView]$GetPatterns, [System.Windows.Forms.DataGridView]$UpdatePatterns)
{
$this.ClearPatternWindow($UpdatePatterns)
$Selected = $GetPatterns.SelectedCells.Count
if ($Selected -eq 1)
{
$this.Parent.UpdatePatterns.Visible = $true
$this.Parent.SavePattern.Visible = $true
$this.Parent.DeletePattern.Visible = $true
$PatternNum = $GetPatterns.CurrentCell.RowIndex
$this.UpdateRows([xml]$PatternConf, [System.Windows.Forms.DataGridView]$UpdatePatterns, $PatternNum)
}
if ($Selected -gt 1)
{
[System.Windows.Forms.MessageBox]::Show("Вы не можете выбрать более одного шаблона для редактирования!")
}
}

ClearPatternWindow ([System.Windows.Forms.DataGridView]$UpdatePatterns)
{
$Rows = $UpdatePatterns.Rows.Count
if ($Rows -gt 0)
{
for ($i = 0; $i -le $Rows - 1; $i++)
{
$UpdatePatterns.Rows.RemoveAt(0)
}
}
}

UpdateRows ([xml]$PatternConf, [System.Windows.Forms.DataGridView]$UpdatePatterns, [int]$PatternNum)
{
if ($PatternConf.Patterns.ChildNodes.Count -eq 1)
{
$UpdatePatterns.Rows.Add('Имя шаблона', $PatternConf.Patterns.Pattern.PatternName)
$UpdatePatterns.Rows.Add('Модель', $PatternConf.Patterns.Pattern.Model)
$UpdatePatterns.Rows.Add('Минимальная скорость полета', $PatternConf.Patterns.Pattern.MinSpeed)
$UpdatePatterns.Rows.Add('Максимальная скорость полета', $PatternConf.Patterns.Pattern.MaxSpeed)
$UpdatePatterns.Rows.Add('Максимальный прирост скорости (1 сек)', $PatternConf.Patterns.Pattern.SpeedAdjust)
$UpdatePatterns.Rows.Add('Максимальный сброс скорости (1 сек)', $PatternConf.Patterns.Pattern.SpeedDecrease)
$UpdatePatterns.Rows.Add('Максимальный угол поворота (1 сек)', $PatternConf.Patterns.Pattern.MaxRotAng)
}
if ($PatternConf.Patterns.ChildNodes.Count -gt 1)
{
$UpdatePatterns.Rows.Add('Имя шаблона', $PatternConf.Patterns.Pattern[$PatternNum].PatternName)
$UpdatePatterns.Rows.Add('Модель', $PatternConf.Patterns.Pattern[$PatternNum].Model)
$UpdatePatterns.Rows.Add('Минимальная скорость полета', $PatternConf.Patterns.Pattern[$PatternNum].MinSpeed)
$UpdatePatterns.Rows.Add('Максимальная скорость полета', $PatternConf.Patterns.Pattern[$PatternNum].MaxSpeed)
$UpdatePatterns.Rows.Add('Максимальный прирост скорости (1 сек)', $PatternConf.Patterns.Pattern[$PatternNum].SpeedAdjust)
$UpdatePatterns.Rows.Add('Максимальный сброс скорости (1 сек)', $PatternConf.Patterns.Pattern[$PatternNum].SpeedDecrease)
$UpdatePatterns.Rows.Add('Максимальный угол поворота (1 сек)', $PatternConf.Patterns.Pattern[$PatternNum].MaxRotAng)
}
}
}

Class SavePattern: System.Windows.Forms.Button
{
[void] Init()
{
$this.Location = [System.Drawing.Point]::new(50, 620)
$this.Size = [System.Drawing.Size]::new(250, 30)
$this.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$this.Text = "Сохранить шаблон"
$this.Visible = $false
}

SavePattern ()
{
$this.Init()
}

Request_Answer()
{
[System.Windows.Forms.DialogResult]$User_Answer = [System.Windows.Forms.MessageBox]::Show("Вы уверены ?", "Запрос подтверждения", [System.Windows.Forms.MessageBoxButtons]::YesNoCancel)
}

}

Class DeletePattern: System.Windows.Forms.Button
{
[void] Init ()
{
$this.Location = [System.Drawing.Point]::new(370, 620)
$this.Size = [System.Drawing.Size]::new(250, 30)
$this.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$this.Text = "Удалить шаблон"
$this.Visible = $false
}

DeletePattern ()
{
$this.Init()
}
}

Class GetPatterns: System.Windows.Forms.DataGridView
{
[void] Init()
{
$this.Location = [System.Drawing.Point]::new(50, 50)
$this.Size = [System.Drawing.Size]::new(570, 230)
$this.AllowUserToAddRows = $false
$this.AllowUserToDeleteRows = $false
}

GetPatterns ()
{
$this.Init()
}
}

Class UpdatePatterns: System.Windows.Forms.DataGridView
{
[void] Init()
{
$this.Location = [System.Drawing.Point]::new(50, 360)
$this.Size = [System.Drawing.Size]::new(570, 230)
$this.AllowUserToAddRows = $false
$this.AllowUserToDeleteRows = $false
$this.Visible = $false
}

UpdatePatterns ()
{
$this.Init()
}
}

Class PatternProperties: System.Windows.Forms.DataGridViewColumn
{
[void] Init ()
{
$this.Frozen = $true
$this.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$this.CellTemplate = [System.Windows.Forms.DataGridViewTextBoxCell]::new()
$this.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
}

PatternProperties ()
{
$this.Init()
}
}

Class Main_Window: System.Windows.Forms.Form
{
[CreatePattern]$CreatPattern
[UpdatePattern]$UpdatePattern
[SavePattern]$SavePattern
[DeletePattern]$DeletePattern
[GetPatterns]$GetPatterns
[UpdatePatterns]$UpdatePatterns
[PatternProperties]$PatternProperty
[PatternProperties]$PatternName
[PatternProperties]$PatternHost
[xml]$PatternConf

[void] Init ()
{
$a = $this.PatternConf.Patterns.Pattern.Count
$this.Size = [System.Drawing.Size]::new(700, 800)
$this.BackColor = [System.Drawing.Color]::AliceBlue
$this.Add_load({
try
{
$_path = $_plane_pattern_path
$this.PatternConf = Get-Content $_path -ErrorAction Stop
}
catch [System.Management.Automation.ItemNotFoundException] {
[System.Windows.Forms.MessageBox]::Show("Файл шаблонов отсутствует! Переместите файл с шаблонами в папку, где находится скрипт, и перезапустите скрипт, или создайте шаблоны в интерфейсе!", "Внимание!")
}
if ($this.PatternConf.Patterns.ChildNodes.Count -eq 1)
{
$this.GetPatterns.Rows.Add($this.PatternConf.Patterns.Pattern.PatternName)
}
if ($this.PatternConf.Patterns.ChildNodes.Count -gt 1)
{
for ($i = 0; $i -lt $this.PatternConf.Patterns.Pattern.Count; $i++)
{
$this.GetPatterns.Rows.Add($this.PatternConf.Patterns.Pattern[$i].PatternName)
}
}
})

$this.CreatPattern = [CreatePattern]::new()
$this.Controls.Add($this.CreatPattern)

$this.UpdatePattern = [UpdatePattern]::new()
$this.Controls.Add($this.UpdatePattern)
$this.UpdatePattern.Add_Click({
$this.ViewPatternConf($this.Parent.PatternConf, $this.Parent.GetPatterns, $this.Parent.UpdatePatterns)
})

$this.SavePattern = [SavePattern]::new()
$this.Controls.Add($this.SavePattern)
$this.SavePattern.Add_Click({
$this.Request_Answer()
$this.Parent.UpdatePatterns.Visible = $false
$this.Visible = $false
$this.Parent.DeletePattern.Visible = $false
})

$this.DeletePattern = [DeletePattern]::new()
$this.Controls.Add($this.DeletePattern)

$this.GetPatterns = [GetPatterns]::new()
$this.Controls.Add($this.GetPatterns)

$this.UpdatePatterns = [UpdatePatterns]::new()
$this.Controls.Add($this.UpdatePatterns)

$this.PatternProperty = [PatternProperties]::new()
$this.PatternProperty.Width = 528
$this.PatternProperty.HeaderText = 'Название шаблона'
$this.PatternProperty.DataPropertyName = 'Pattern Property'
$this.PatternProperty.ReadOnly = $true
$this.PatternProperty.Name = 'PatternProperty'
$this.GetPatterns.Columns.Add($this.PatternProperty)

$this.PatternName = [PatternProperties]::new()
$this.PatternName.Width = 352
$this.PatternName.HeaderText = 'Параметр'
$this.PatternName.DataPropertyName = 'Pattern Name'
$this.PatternName.ReadOnly = $true
$this.PatternName.Name = 'Param'
$this.UpdatePatterns.Columns.Add($this.PatternName)

$this.PatternHost = [PatternProperties]::new()
$this.PatternHost.Width = 176
$this.PatternHost.HeaderText = 'Значение'
$this.PatternHost.DataPropertyName = 'Pattern Host'
$this.PatternHost.ReadOnly = $false
$this.PatternHost.Name = 'Host'
$this.UpdatePatterns.Columns.Add($this.PatternHost)

}

Main_Window ()
{
$this.Init()
}
} #::Main_Window

$Start_Window_syncHash.PatternConfigWindow = [Main_Window]::new()
$Start_Window_syncHash.Start_Window = [MainForm]::new($Start_Window_syncHash.PatternConfigWindow)
$Start_Window_syncHash.Start_Window.ShowDialog() | Out-Null
$Start_Window_syncHash.Error = $Error
})
$Start_Window_Cmd.AddArgument($Script_Conf) | Out-Null
$Start_Window_Cmd.AddArgument($Plane_Pattern_Path) | Out-Null
$Start_Window_Cmd.Runspace = $Start_Window_Runspace
$Start_Window_data = $Start_Window_Cmd.BeginInvoke()
Start-Sleep 4
$Start_Window_data
}
Main

User avatar
jvierra
Posts: 13793
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: Can`t open a form in another thread.

Post by jvierra » Tue Oct 08, 2019 9:01 am

I posted two examples that you can run at a prompt. The second one uses BeginInvoke. Run them at a prompt and try to understand what I am showing you. Posting a very long sript does not help to solve the issue. Understanding how to use runspaces is the first thing to do.

When posting long scripts please attach them as a file usable in PowerShell Studio (PSF) to illustrate your issue.

Run and try to understand m code and you will begin to see how this needs to work.

TolikTipaTut1
Posts: 8
Joined: Mon Oct 07, 2019 10:44 am

Re: Can`t open a form in another thread.

Post by TolikTipaTut1 » Tue Oct 08, 2019 10:45 am

Thank you very much!

Actually, I`ve got some kind of "homework".
The task is:
I need to get the shortest car route. So, I need 3 threads:
GUI for users to interact with, the engine and some kind of "bridge" thread. Sorry for my english.

How can I create events in powershell ?
For example:
A user presses a button on form (calculate route)
Then I need to sart the calculation process. I know that i can use "button.Add_Click({<# code here #>})", but I need to create threads instead...

User avatar
jvierra
Posts: 13793
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: Can`t open a form in another thread.

Post by jvierra » Tue Oct 08, 2019 11:01 am

I cannot even begin to guess what you are trying to do.

PowerShell is not multithreaded, You can use jobs to do calculations in the background.
If you are not an experienced programmer or an experienced PowerShell coder then I suggest not doing this in a form.

Why is this "homework"? What are you studying that requires using PowerShell?

TolikTipaTut1
Posts: 8
Joined: Mon Oct 07, 2019 10:44 am

Re: Can`t open a form in another thread.

Post by TolikTipaTut1 » Tue Oct 08, 2019 11:15 am

I am a university of geodesy and cartography student.
Our lecturer gave us this homework.
I insisted on using С# perform this task ...

User avatar
jvierra
Posts: 13793
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: Can`t open a form in another thread.

Post by jvierra » Tue Oct 08, 2019 12:01 pm

Did your professor require threads? PowerShell does not do threads. Runspaces are much more complex and do not do forms well unless you are a vvery experienced programmer with Windows forms.

I cannot believe a professor of "mapping" would require PowerShell. It is not an application development language. It is an automation language for admins and developers to use to built tools for diagnostics and reporting. Applications requires a different system. C# is an applications development language and more.

Also you do not need to use threads to do any simple calculations. You can just executer good mathematical formulas that are design to solve issues like the "salesmeans' route problem".

Just add the code to an event on a button and click the button.

On modern processors the shortest route algorithms for routes with fewer that 50 segments can execute in a second or two. I see no need for threads.

TolikTipaTut1
Posts: 8
Joined: Mon Oct 07, 2019 10:44 am

Re: Can`t open a form in another thread.

Post by TolikTipaTut1 » Tue Oct 08, 2019 12:11 pm

Yes, our professor didn't tell me that threads are required.
So, I'll paste code into button.add_click)

P.S. But I'll continue to understand runspaces, because it's really interesting)
Thank you very much for your help!

Post Reply