问题
Was wondering if there is a way to check what event closed the window, pretty much either clicking the red x in the top corner or if $form.Close() was called?
Each will automatically initiate the $form.Add_Closing({}) if I have it in my script, but I wanted to know which way of closing the window did this.
回答1:
The FormClosing event argument object's .CloseReason property doesn't allow you to distinguish between the .Close() method having been called on the form and the user closing the form via the title bar / window system menu / pressing Alt+F4 - all these cases equally result in the .CloseReason property reflecting enumeration value UserClosing.
However, you can adapt the technique from Reza Aghaei's helpful C# answer on the subject, by inspecting the call stack for a call to a .Close() method:
using assembly System.Windows.Forms
using namespace System.Windows.Forms
using namespace System.Drawing
# Create a sample form.
$form = [Form] @{
ClientSize = [Point]::new(400,100)
Text = 'Closing Demo'
}
# Create a button and add it to the form.
$form.Controls.AddRange(@(
($btnClose = [Button] @{
Text = 'Close'
Location = [Point]::new(160, 60)
})
))
# Make the button call $form.Close() when clicked.
$btnClose.add_Click({
$form.Close()
})
# The event handler called when the form is closing.
$form.add_Closing({
# Look for a call to a `.Close()` method on the call stack.
if ([System.Diagnostics.StackTrace]::new().GetFrames().GetMethod().Name -ccontains 'Close') {
Write-Host 'Closed with .Close() method.'
} else {
Write-Host 'Closed via title bar / Alt+F4.'
}
})
$null = $form.ShowDialog() # Show the form modally.
$form.Dispose() # Dispose of the form.
If you run this code and try various methods of closing the form, a message indicating the method used should print (.Close() call vs. title bar / Alt+F4).
Note that closing the form via buttons assigned to the form's .CancelButton and .SubmitButton properties that don't have explicit $form.Close() calls still causes .Close() to be called behind the scenes.
Note that the code requires PowerShell v5+, but it can be adapted to earlier versions.
回答2:
Checking call stack works fine and you can rely on it.
Just for the sake of completeness, specially for cases that you find a C# example and you want to use it in PowerShell in an easy way, I'll share an example showing how you can handle WM_SYSCOMMAND as shown in my linked post, in PowerShell.
using assembly System.Windows.Forms
using namespace System.Windows.Forms
using namespace System.Drawing
# Create the C# derived Form
$assemblies = "System.Windows.Forms", "System.Drawing"
$code = @'
using System;
using System.Windows.Forms;
public class MyForm:Form
{
public bool ClosedByXButtonOrAltF4 { get; private set;}
public const int SC_CLOSE = 0xF060;
public const int WM_SYSCOMMAND = 0x0112;
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_SYSCOMMAND && msg.WParam.ToInt32() == SC_CLOSE)
ClosedByXButtonOrAltF4 = true;
base.WndProc(ref msg);
}
protected override void OnShown(EventArgs e)
{
ClosedByXButtonOrAltF4 = false;
}
}
'@
Add-Type -ReferencedAssemblies $assemblies -TypeDefinition $code -Language CSharp
# Create an instance of MyForm.
$form = [MyForm] @{
ClientSize = [Point]::new(400,100)
Text = "Closing Demo"
}
# Create a button and add it to the form.
$form.Controls.AddRange(@(
($btnClose = [Button] @{
Text = "Close"
Location = New-Object System.Drawing.Point 160, 60
})
))
# Make the button call $form.Close() when clicked.
$btnClose.add_Click({
$form.Close()
})
# The event handler called when the form is closing.
$form.add_Closing({
if ($form.ClosedByXButtonOrAltF4) {
Write-Host 'Closed via title bar / Alt+F4.'
} else {
Write-Host 'Closed with .Close() method.'
}
})
$null = $form.ShowDialog()
$form.Dispose()
来源:https://stackoverflow.com/questions/59036628/checking-what-closed-your-windows-form-in-powershell