frasernator
frasernator

Reputation: 11

Create a popup message in Powershell

I'm attempting to make a function that will allow me to popup a please wait message run some more script then close the popup

Function Popup-Message {

    param ([switch]$show,[switch]$close)

    Add-Type -AssemblyName System.Windows.Forms  

    # Build Form
    $objForm = New-Object System.Windows.Forms.Form
    $objForm.Text = "Test"
    $objForm.Size = New-Object System.Drawing.Size(220,100)

    # Add Label
    $objLabel = New-Object System.Windows.Forms.Label
    $objLabel.Location = New-Object System.Drawing.Size(80,20) 
    $objLabel.Size = New-Object System.Drawing.Size(100,20)
    $objLabel.Text = "Hi there!"
    $objForm.Controls.Add($objLabel)



    If ($show)
    {
        $objForm.Show() | Out-Null
        $global:test = "Show"
    }


    If ($close)
    {
        # destroy form
        $objForm.Close() | Out-Null
        $global:test = "Close"
    }
}

I can then get the popup to display by:

Popup-Message -show

At this point I can see the $test variable as Show

But when I try to close the window with:

Popup-Message -close

But the popup window will not close If I look at $test again it will show as Close

I'm assuming this has something to do with keeping the function in the Global Scope but I can't figure out how to do this with the form

Upvotes: 1

Views: 10238

Answers (3)

mklement0
mklement0

Reputation: 437197

kpogue's helpful answer explains your problem well and offers a solution that is effective, but suboptimal due to relying on global variables.

Let me suggest a different approach, where you use a function to simply define the form and return that definition, on which you can call the .Show() and .Close() methods as needed, but note that the .Show() method is overridden via Add-Member to include a call to [System.Windows.Forms.Application]::DoEvents(), so as to ensure that the form is properly drawn.

function New-PopUpForm {

  Add-Type -AssemblyName System.Windows.Forms  

  # Create the form.
  $objForm = New-Object System.Windows.Forms.Form -Property @{
    Text            = "Test"
    Size            = New-Object System.Drawing.Size 220, 100
    StartPosition   = 'CenterScreen' # Center on screen.
    FormBorderStyle = 'FixedSingle'  # fixed-size form
    # Remove interaction elements (close button, system menu).
    ControlBox      = $false
  }

  # Create a label...
  $objLabel = New-Object System.Windows.Forms.Label -Property @{
    Location = New-Object System.Drawing.Size 80, 20
    Size     = New-Object System.Drawing.Size 100, 20
    Text     = "Hi there!"
  }

  # ... and add it to the form.
  $objForm.Controls.Add($objLabel)

  # Override the `.Show()` method to include
  # a [System.Windows.Forms.Application]::DoEvents(), so as 
  # to ensure that the form is properly drawn.
  $objForm | Add-Member -Force -Name Show -MemberType ScriptMethod -Value {
    $this.psbase.Show() # Call the original .Show() method.
    # Activate the form (focus it).
    $this.Activate()
    [System.Windows.Forms.Application]::DoEvents() # Ensure proper drawing.
  }

  # Since this form is meant to be called with .Show() but without 
  # a [System.Windows.Forms.Application]::DoEvents() *loop*, it
  # it is best to simply *hide* the cursor (mouse pointer), so as not
  # to falsely suggest that interaction with the form is possible
  # and so as not to end up with a stuck "wait" cursor (mouse pointer) on 
  # the first instantiation in a session.
  [System.Windows.Forms.Cursor]::Hide()

  # Return the form.
  return $objForm
}

You can then use the function as follows:

$form = New-PopupForm

$form.Show() 

# ...

$form.Close()

Note:

  • Once you call .Close(), the form instance stored in $form is disposed of and cannot be reused - simply call New-PopupForm again to create a new instance.

  • If the PowerShell session running your script exits, any pop-up windows created in the session close automatically.

Caveats:

  • Note that, due to use of the .Show() method (without additional effort), the user won't be able to interact with the pop-up window, notably not even in order to move the window or close it manually.

    • If, such as in your case, the window isn't meant to be interacted with, this isn't a problem, however.
    • Setting ControlBox = $false above removes the window's close button and system menu so as to make it obvious that no interaction is possible.
    • Hiding the cursor (mouse pointer) with [System.Windows.Forms.Cursor]::Hide() serves the same purpose.
      • Note: Not hiding the cursor causes the "wait" cursor to be shown (indicating that the form is busy processing something) indefinitely while mousing over the form, but curiously only for the very first instance created in a session. The only way to avoid it that I know of is to enter an event loop after calling .Show(), as discussed in the last bullet point.
  • By contrast, .ShowDialog() would allow interaction, but blocks further execution of your script until the window is closed by the user.

  • If you need to combine the two approaches - allowing the user to interact with the window while continuing to do processing in your PowerShell script - you need to call [System.Windows.Forms.Application]::DoEvents() in a loop, as shown in this answer.

    • If you use this approach, you should remove the [System.Windows.Forms.Cursor]::Hide() call from the function.

Upvotes: 2

thiyagu selvaraj
thiyagu selvaraj

Reputation: 100

we can use One line to create the powershell popup with Wscript.Shell COM object

Syntax: intButton = objShell.Popup(strText,[nSecondsToWait],[strTitle],[nType])

#creating object os WScript
$wshell = New-Object -ComObject Wscript.Shell -ErrorAction Stop
#invoking the POP method using object
$wshell.Popup("please wait message?",5,"Wait Until Scrip execution",48+4)

Upvotes: 1

kpogue
kpogue

Reputation: 700

You're creating a new instance of the form in each call to Popup-Message. So, the form is not closing becausing you're trying to close a new form which has never been opened. Meanwhile, the old form no longer has anyone referencing it, it's just sitting out in space. You can create a global reference as shown below, there are probably more artful ways of doing this.

Function Popup-Message {

param ([switch]$show,[switch]$close)

Add-Type -AssemblyName System.Windows.Forms  

# Build Form
if ($Global:objForm -eq $null){
$Global:objForm = New-Object System.Windows.Forms.Form
$Global:objForm.Text = "Test"
$Global:objForm.Size = New-Object System.Drawing.Size(220,100)

# Add Label
$Global:objLabel = New-Object System.Windows.Forms.Label
$Global:objLabel.Location = New-Object System.Drawing.Size(80,20) 
$Global:objLabel.Size = New-Object System.Drawing.Size(100,20)
$Global:objLabel.Text = "Hi there!"
$Global:objForm.Controls.Add($objLabel)
}



If ($show)
{
    $Global:objForm.Show() | Out-Null
    $global:test = "Show"
}


If ($close)
{
    # destroy form
    $Global:objForm.Close() | Out-Null
    $global:test = "Close"
}
}

Upvotes: 1

Related Questions