Sunday, February 3, 2008

Windows Messenger like popup

 

This script invokes a Windows Messenger like dialog, sending messages to the current user. You can call it in two ways:

 

Static

New-Popup.ps1 -message "hello world" -title "PowerShell Popup"

The form pops up at the right hand side, above the system tray and waits for the user to close it via the Close button. Script execution is halted until the form is closed.

static

 

 

With animation

New-Popup.ps1 -slide -message "hello world" -title "PowerShell Popup"

The dialog is sliding, no close button, it waits for a specified number of seconds based on the $wait parameter and then closes it self. Script execution is halted until the form is closed.

 

 slide

 

The script can be also downloaded here.

 

###################################
## New-Popup.ps1
##
## Messenger like popup dialog
##
## Usage:
## New-Popup.ps1
## New-Popup.ps1 -slide -message "hello world" -title "PowerShell Popup"


param(
    [int]$formWidth=200, 
    [int]$formHeight=110,
    [string]$title="Your title here",
    [string]$message="Your message here",
    [int]$wait=4,
    [switch]$slide
)

[void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")

###############################
# extract powershell icon if doesn't exist
$icon = "$env:temp\posh.ico"
if( !(test-path -pathType leaf $icon)){
    [System.Drawing.Icon]::ExtractAssociatedIcon((get-process -id $pid).path).ToBitmap().Save($icon)
}

###############################
# Create the form
$form = new-object System.Windows.Forms.Form
$form.ClientSize = new-object System.Drawing.Size($formWidth,$formHeight)
$form.BackColor = [System.Drawing.Color]::LightBlue
$form.ControlBox = $false
$form.ShowInTaskbar = $false
$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedSingle
$form.topMost=$true

# initial form position
$screen = [System.Windows.Forms.Screen]::PrimaryScreen
$form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual

if($slide){
    $top = $screen.WorkingArea.height + $form.height
    $left = $screen.WorkingArea.width - $form.width
    $form.Location = new-object System.Drawing.Point($left,$top)
} else {
    $top = $screen.WorkingArea.height - $form.height
    $left = $screen.WorkingArea.width - $form.width
    $form.Location = new-object System.Drawing.Point($left,$top)
}

###############################
## pictureBox for icon 
$pictureBox = new-object System.Windows.Forms.PictureBox 
$pictureBox.Location = new-object System.Drawing.Point(2,2)
$pictureBox.Size = new-object System.Drawing.Size(20,20)
$pictureBox.TabStop = $false
$pictureBox.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::StretchImage
$pictureBox.Load($icon)


###############################
## create textbox to display the  message
$textbox = new-object System.Windows.Forms.TextBox
$textbox.Text = $message
$textbox.BackColor = $form.BackColor
$textbox.Location = new-object System.Drawing.Point(4,26)
$textbox.Multiline = $true
$textbox.TabStop = $false
$textbox.BorderStyle = [System.Windows.Forms.BorderStyle]::None
$textbox.Size = new-object System.Drawing.Size(192,77)
$textbox.Cursor = [System.Windows.Forms.Cursors]::Default
$textbox.HideSelection = $false


###############################
## Create 'Close' button, when clicked hide and dispose the form
$button = new-object system.windows.forms.button 
$button.Font = new-object System.Drawing.Font("Webdings",5)
$button.Location = new-object System.Drawing.Point(182,3)
$button.Size = new-object System.Drawing.Size(16,16)
$button.Text = [char]114
$button.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
$button.Add_Click({ $form.hide(); $form.dispose() })
if($slide) {$button.visible=$false}


###############################
## Create a label, for title text
$label = new-object System.Windows.Forms.Label
$label.Font = new-object System.Drawing.Font("Microsoft Sans Serif",8,[System.Drawing.FontStyle]::Bold)
$label.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
$label.Text = $title
$label.Location = new-object System.Drawing.Point(24,3)
$label.Size = new-object System.Drawing.Size(174, 20)


###############################
## Create a timer to slide the form
$timer = new-object System.Windows.Forms.Timer
$timer.Enabled=$false
$timer.Interval=10
$timer.tag="up"
$timer.add_tick({

    if(!$slide){return}

    if($timer.tag -eq "up"){
        $timer.enabled=$true
        $form.top-=2
        if($form.top -le ($screen.WorkingArea.height - $form.height)){
            #$timer.enabled=$false
            $timer.tag="down"
            start-sleep $wait
        }
    } else {

        $form.top+=2
        if($form.top -eq ($screen.WorkingArea.height + $form.height)){
            $timer.enabled=$false
            $form.dispose()
        }
    }
})


## add form event handlers
$form.add_shown({
    $form.Activate()
    (new-Object System.Media.SoundPlayer "$env:windir\Media\notify.wav").play()
    $timer.enabled=$true
})

## draw seperator line 
$form.add_paint({    
    $gfx = $form.CreateGraphics()        
    $pen = new-object System.Drawing.Pen([System.Drawing.Color]::Black)
    $gfx.drawLine($pen,0,24,$form.width,24)      
    $pen.dispose()
    $gfx.dispose()
})


###############################
## add controls to the form
## hide close button if form is not sliding

if($slide){
    $form.Controls.AddRange(@($label,$textbox,$pictureBox))
} else {
    $form.Controls.AddRange(@($button,$label,$textbox,$pictureBox))
}

###############################
## show the form
[void]$form.showdialog()

11 comments:

Per Østergaard said...

Cool - but try to do it from a loop and press ctrl+c. PowerShell does not like that ;)

Alexander_A said...

Very nice! Excellent work mate!

Now all I have to do is find a way to implement it into SCCM ;)

Tom said...

Hello,
Thanks for you work.
Is there a way to put it in background so i can continue script during the popup.
I've tried many different way but without success

$hay@Israel said...

Hi Tom

With PowerShell v2 you can invoke the popup inside a background job (make sure to specify the full path to the popup script).

$job = Start-Job { & D:\Scripts\New-Popup.ps1 -message message -title title -slide}

$null = Register-ObjectEvent -InputObject $job -EventName StateChanged -SourceIdentifier PopupJobEnd -Action {
if($sender.State -eq 'Completed') {
Get-Job -Name PopupJobEnd | Remove-Job -Force
Get-Job -Id $sender.id | Remove-Job -Force
Unregister-Event -SourceIdentifier PopupJobEnd -Force
}
}

tom said...

Thanks for your help.

I've already tried that but the popup never show

Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. Tous droits réservés.

PS C:\Documents and Settings\Administrateur> &"C:\Documents and Settings\Adminis
trateur\Bureau\test9.ps1"

Nothing is visible on the window

PS C:\Documents and Settings\Administrateur> get-job

AVERTISSEMENT : la colonne « Command » ne tient pas à l'écran et a été supprimé
e.

Id Name State HasMoreData Location
-- ---- ----- ----------- --------
1 Job1 Running True localhost
3 PopupJobEnd NotStarted False

Thanks again

$hay@Israel said...

I'm not sure why you don't see the popup. I've tested it on several computers and it worked on all of them. What is the ApartmentState of your shell (should be MTA)?

[threading.thread]::CurrentThread.GetApartmentState()

Tom said...

Hello,

Yes i'm in MTA but its' not working.

Can you send me your files or the code you are running?

Thanks
Tom

$hay@Israel said...

Tom, shoot me an email:

scriptolog AT gmail DOT com

David said...

Hi. This is one great popup message. I have seen examples on creating a ballon tip message box but this is so much better. I really like the slide and the time value you can change. This was exactly what I was looking for.

One question: Is there a way to run this against multiple computers so you could use it to send out quick alerts to the users logged on? If so please email me at newsomd@gmail.com. Thanks so much, David...

$hay@Israel said...

Hi David

Glad you like the script :)

I'm not sure it'll work, I never tried it, but if you have remoting enables then you can use the Invoke-Command cmdlet to run the file on the remote machines:

Invoke-Command -FilePath .\New-Popup.ps1 -ComputerName pc1,pc2,pc3

Anonymous said...

When I atttempt to run the script at a remote machine, I get an error:

PS C:\Users\charlie\Desktop> Invoke-Command -computername imd04 .\New-Popup.ps1
Exception calling "ShowDialog" with "0" argument(s): "Showing a modal dialog box or form when the application is not ru
nning in UserInteractive mode is not a valid operation. Specify the ServiceNotification or DefaultDesktopOnly style to
display a notification from a service application."
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

What do I need to do?

TIA