winforms - El uso de formularios Windows Forms bloquea PowerShell ISE minutos después de que el script haya terminado
powershell-v4.0 (4)
Tengo un tema interesante aquí. Estoy creando un selector de calendario para usar cuando creamos cuentas. Funciona bien y aún está en progreso, pero me he dado cuenta de que cuando ejecuto el script en el ISE de PowerShell, después de unos minutos se bloquea (puedo editar y guardar el código unos minutos antes). No hay nada en el registro de eventos. Aparece un cuadro de diálogo que dice que powershell no responde. El uso de la memoria parece normal también. No sé que está pasando.
Esto ocurre sin importar cómo ejecute Powershell ISE (Ejecutar como administrador, Ejecutar como otra cuenta e ISE normal) Estoy ejecutando Windows 8.1.
Un compañero de trabajo sugirió que podría ser el modelo de apartamento, así que probé STA y MTA, pero el problema ocurre de cualquier manera. No sucede cuando se ejecuta el mismo código desde el host de la consola.
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$objForm = New-Object Windows.Forms.Form
$objForm.Text = "Select a Date"
$objForm.Size = New-Object Drawing.Size @(490,250)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Enter")
{
$script:dtmDate=$objCalendar.SelectionStart
$objForm.Close()
}
})
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Escape")
{
$objForm.Close()
}
})
$objCalendar = New-Object System.Windows.Forms.MonthCalendar
$objCalendar.Text = "Start"
$objCalendar.ShowTodayCircle = $False
$objCalendar.MaxSelectionCount = 1
$objForm.Controls.Add($objCalendar)
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
if ($dtmDate)
{
Write-Host "Date selected: $dtmDate"
}
$objForm.Dispose()
En respuesta a @The Unique Paul Smith
function Find-CalenderDateTest {
[CmdletBinding()]
param(
[Parameter(
Mandatory=$false
)]
[ValidateSet(''long'',''short'',''powerpoint'')]
[ValidateNotNullOrEmpty()]
[string]
$DateFormat
)
Begin{
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$objForm = New-Object Windows.Forms.Form
$objForm.Text = "Select a Date"
$objForm.Size = New-Object Drawing.Size @(243,250)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$dtmDate = $null
$objForm.Add_KeyDown( {
if ($_.KeyCode -eq "Enter")
{
$dtmDate=$objCalendar.SelectionStart
$objForm.Close()
}
})
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Escape")
{
$objForm.Close()
}
})
#region OK Button
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(20,175)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
# Got rid of the Click event for OK Button, and instead just assigned its DialogResult property to OK.
$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$objForm.Controls.Add($OKButton)
# Setting the form''s AcceptButton property causes it to automatically intercept the Enter keystroke and
# treat it as clicking the OK button (without having to write your own KeyDown events).
$objForm.AcceptButton = $OKButton
#endregion
#region Cancel Button
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(80,175)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
# Got rid of the Click event for Cancel Button, and instead just assigned its DialogResult property to Cancel.
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$objForm.Controls.Add($CancelButton)
# Setting the form''s CancelButton property causes it to automatically intercept the Escape keystroke and
# treat it as clicking the OK button (without having to write your own KeyDown events).
$objForm.CancelButton = $CancelButton
#endregion
$objCalendar = New-Object System.Windows.Forms.MonthCalendar
$objCalendar.ShowTodayCircle = $False
$objCalendar.MaxSelectionCount = 1
$objForm.Controls.Add($objCalendar)
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
$Results = $objForm.ShowDialog()
}
Process{}
End{
if ($Results -eq "OK")
{
$objCalendar.SelectionStart
}
$objForm.Dispose()
}
}
El error es MTA / STA.
No usar
$form.showDialog()
Utilizar
[system.windows.forms.application]::run($form)
en lugar
y funciona bien cada vez
Otra forma es ponerlo en otro hilo:
$code
{
//form code here
$form.showDialog()
}
$newThread = [Powershell]::Create()
$newThread.AddScript($code)
$handle = $newThread.BeginInvoke()
Proporcionar variables desde el script de llamada:
$newThread.Runspace.SessionStateProxy.SetVariable("variablenname",value)
antes de que BeginInvoke
use variablenname
sin $ ...
Es una posibilidad remota, pero el problema podría ser que powershell no está cerrando el objeto $ objForm correctamente, dejándolo en ejecución en la memoria mientras el ISE espera una entrada después de que el script haya terminado. Si verifica su administrador de tareas, ¿el formulario aún se está ejecutando en segundo plano? También puede intentar agregar ''Remove-Variable objForm'' (no $) después de dispose () y ver si eso ayuda.
Más información: https://technet.microsoft.com/en-us/library/ff730962.aspx
Como digo, es un tiro largo.
Estaba usando combobox.items.add
:
$configCombo.Items.Add($wks)
y busqué cómo evitar que las claves se impriman en la consola, y cambié el complemento a:
[void]$configCombo.Items.Add($wks)
Desde entonces, he agregado el vacío, lo he estado ejecutando en ISE y no se ha bloqueado desde entonces.
Me encontré con este problema también. Generalmente ocurre cuando bloqueo mi estación de trabajo y regreso. Después de buscar y buscar en Google, encontré este https://support.microsoft.com/en-us/help/943139/windows-forms-application-freezes-when-system-settings-are-changed-or , Lo que parece ser el tema en cuestión.
Problema
La aplicación no responderá y el subproceso de la interfaz de usuario se bloqueará en una llamada de invocación mientras se maneja la notificación OnUserPreferenceChanged
Porque
Esto ocurre si se crea un control en un hilo que no bombea mensajes y el hilo de la interfaz de usuario recibe un mensaje WM_SETTINGCHANGE.
Resolución
Las aplicaciones nunca deben dejar los objetos de Control en los hilos sin una bomba de mensajes activa. Si no se pueden crear controles en el subproceso de la interfaz de usuario principal, deben crearse en un subproceso de la IU secundaria dedicada y desecharse tan pronto como ya no sean necesarios.