Using Windows Forms Locks up PowerShell ISE minute

2020-05-31 08:51发布

I have an interesting issue here. I'm creating a calendar picker for use when we create accounts. It works fine and is still in progress but I have noticed that when I run the script in powershell ISE, after a few minutes it locks up (I am able to edit and save the code for a few minutes prior to that). There is nothing in the event log. I get a dialog box saying that powershell is non responsive. Memory usage seems normal as well. I do not know what is happening.

This occurs no matter how I run Powershell ISE (Run as Administrator, Run as another account, and normal ISE) I am running windows 8.1.

A coworker suggested it may be the apartment model, so I've tried STA and MTA, but the problem occurs either way. It does not happen when the same code is run from the console host.

[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()

In Response to @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()

}
}

4条回答
Rolldiameter
2楼-- · 2020-05-31 09:21

The error is MTA/STA

Don't use

$form.showDialog()

Use

[system.windows.forms.application]::run($form)

instead

and it works fine every time

Another way is to put it in another thread:

$code
{
  //form code here
  $form.showDialog()
}
$newThread = [Powershell]::Create()
$newThread.AddScript($code)
$handle = $newThread.BeginInvoke() 

Provide variables from the calling script:

$newThread.Runspace.SessionStateProxy.SetVariable("variablenname",value)

before the BeginInvoke use variablenname without $...

查看更多
\"骚年 ilove
3楼-- · 2020-05-31 09:32

It's a long shot but the problem might be that powershell is not closing the $objForm object correctly, leaving it running in memory while the ISE waits for input after the script has terminated. If you check your taskmanager, is the form still running in the background? You could also try adding 'Remove-Variable objForm' (no $) after the dispose() and see if that helps.

More information: https://technet.microsoft.com/en-us/library/ff730962.aspx

As I say, it's a long shot.

查看更多
▲ chillily
4楼-- · 2020-05-31 09:38

Ran into this issue too. Generally occurs when I lock my workstation and return. After a bit of poking about and googleing, I found this https://support.microsoft.com/en-us/help/943139/windows-forms-application-freezes-when-system-settings-are-changed-or, which seems like the issue at hand.

Issue

The application will not respond and the UI thread will hang in an Invoke call while handling the OnUserPreferenceChanged notification

Cause

This occurs if a control is created on a thread which doesn't pump messages and the UI thread receives a WM_SETTINGCHANGE message.

Resolution

Applications should never leave Control objects on threads without an active message pump. If Controls cannot be created on the main UI thread, they should be created on a dedicated secondary UI thread and Disposed as soon as they are no longer needed.

查看更多
等我变得足够好
5楼-- · 2020-05-31 09:45

I was using combobox.items.add:

$configCombo.Items.Add($wks)

and I looked up how to keep the keys from printing to the console - and changed the add to:

[void]$configCombo.Items.Add($wks)

Since then I have added the void - I have been running it in ISE and it has not hung since.

查看更多
登录 后发表回答