{"id":16941,"date":"2010-10-02T00:01:00","date_gmt":"2010-10-02T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2010\/10\/02\/weekend-scripter-tea-time\/"},"modified":"2010-10-02T00:01:00","modified_gmt":"2010-10-02T00:01:00","slug":"weekend-scripter-tea-time","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/weekend-scripter-tea-time\/","title":{"rendered":"Weekend Scripter: Tea Time!"},"content":{"rendered":"<p>&nbsp;<\/p>\n<p>Microsoft Scripting Guy Ed Wilson here. It has been a great week with some excellent guest bloggers. It is not that I took the week off; rather, I have spent the week working on some special projects I wanted to complete. The great thing about working with guest bloggers is that I get to tap into world-class writers who are experts in their field. This week we have had some excellent SharePoint articles and SQL server articles. In addition to reading and trying out the commands from the articles, I decided to take some time to work on a really cool script I have been wanting to write for some time. <\/p>\n<p>The New-TeaTimer.ps1 script falls into the really cool category. The complete code is seen here. <\/p>\n<blockquote>\n<p><strong>New-TeaTimer.ps1<\/strong><\/p>\n<p>function new-teatimer     <br \/>{      <br \/> Param([int]$seconds = 240)      <br \/> Start-Sleep -Seconds $seconds      <br \/>} #end function new-teatimer      <br \/>function New-BalloonTip      <br \/>{      <br \/> Param(      <br \/>&nbsp; [string]$Title = &#8220;tea is ready&#8221;,      <br \/>&nbsp; [string]$Text = &#8220;get your tea&#8221;,      <br \/>&nbsp; [int]$Timeout = 4000,      <br \/>&nbsp; [int]$eventTimeout = 5      <br \/> )      <br \/> Add-Type -AssemblyName system.windows.forms      <br \/>$toast = New-Object system.windows.forms.notifyicon      <br \/>$toast.icon = [system.drawing.systemicons]::information      <br \/>$toast.balloonTipTitle = $Title      <br \/>$toast.visible = $true      <br \/>$toast.BalloonTipText = $Text      <br \/>$toast.ShowBalloonTip($Timeout)      <br \/>Register-ObjectEvent -InputObject $toast -EventName BalloonTipClicked `      <br \/> -SourceIdentifier ballon_clicked       <br \/>if(Wait-Event -Timeout $eventTimeout -SourceIdentifier ballon_clicked)      <br \/> {      <br \/>&nbsp; Remove-Event -SourceIdentifier ballon_clicked       <br \/>&nbsp; Unregister-Event -SourceIdentifier ballon_clicked      <br \/>&nbsp; $toast.Dispose()      <br \/> }      <br \/>else      <br \/> {      <br \/>&nbsp; Unregister-Event -SourceIdentifier ballon_clicked      <br \/>&nbsp; $toast.Dispose()      <br \/> }      <br \/>} #end function new-balloontip      <br \/># *** Entry Point to script ***      <br \/>$st = Get-Date      <br \/>new-teatimer      <br \/>$et = Get-Date      <br \/>New-BalloonTip      <br \/>(New-TimeSpan -Start $st -End $et).totalSeconds<\/p>\n<\/blockquote>\n<p>The New-TeaTimer.ps1 script begins with a function called <strong>new-teatimer<\/strong>. This function basically calls the <strong>Start-Sleep<\/strong> cmdlet. The reason for creating a separate function is because most of the time I let my tea steep for four minutes. Therefore, what I have is a timer that defaults to four minutes. To time my tea, all I need to do is to call the <strong>new-teatimer<\/strong> function. I do not need to calculate how many seconds are in four minutes; I just call the function and leave it at that. The <strong>new-teatimer<\/strong> function is shown here:<\/p>\n<blockquote>\n<p>function new-teatimer     <br \/>{      <br \/> Param([int]$seconds = 240)      <br \/> Start-Sleep -Seconds $seconds      <br \/>} #end function new-teatimer<\/p>\n<\/blockquote>\n<p>The first thing that is done in the <strong>New-BalloonTip<\/strong> function is to define a number of parameters. The <strong>title<\/strong> is the bold portion of the balloon, and the <strong>test<\/strong> parameter governs the text that is displayed in the body of the balloon. The <strong>timeout<\/strong> governs how long the balloon displays before disappearing, and the <strong>eventtimeout<\/strong> controls how long the script will wait for the event to timeout. This portion of the script is shown here:<\/p>\n<blockquote>\n<p>function New-BalloonTip     <br \/>{      <br \/> Param(      <br \/>&nbsp; [string]$Title = &#8220;tea is ready&#8221;,      <br \/>&nbsp; [string]$Text = &#8220;get your tea&#8221;,      <br \/>&nbsp; [int]$Timeout = 4000,      <br \/>&nbsp; [int]$eventTimeout = 5      <br \/> )<\/p>\n<\/blockquote>\n<p>The <strong>forms<\/strong> assembly from the <strong>system.windows<\/strong> .NET Framework namespace is not loaded by default. The easiest way to load the assembly is to use the <strong>Add-Type<\/strong> cmdlet, and specify the assembly name. The <strong>New-Object<\/strong> cmdlet is used to create a new instance of the <strong>notifyicon<\/strong> class&mdash;the class that makes the popup toast that appears in the message area. The icon used by the <strong>notifyicon<\/strong> .NET Framework class is selected from the <strong>systemicons<\/strong> class that is found in the <strong>system.drawing<\/strong> .NET Framework namespace. Each icon in the <strong>systemicons<\/strong> class is a static property from this class. This is shown here:<\/p>\n<blockquote>\n<p>PS C:\\&gt; Add-Type -AssemblyName system.windows.forms     <br \/>PS C:\\&gt; [system.drawing.systemicons] | Get-Member -MemberType property &ndash;Static<\/p>\n<p>&nbsp;&nbsp; TypeName: System.Drawing.SystemIcons     <\/p>\n<p>Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MemberType&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Definition      <br \/>&#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;-      <br \/>Application&nbsp;&nbsp;&nbsp;&nbsp; Property&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static System.Drawing.Icon Application {get;}      <br \/>Asterisk&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Property&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static System.Drawing.Icon Asterisk {get;}      <br \/>Error&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Property&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static System.Drawing.Icon Error {get;}      <br \/>Exclamation&nbsp;&nbsp;&nbsp; Property&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static System.Drawing.Icon Exclamation {get;}      <br \/>Hand&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Property&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static System.Drawing.Icon Hand {get;}      <br \/>Information&nbsp;&nbsp;&nbsp; Property&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static System.Drawing.Icon Information {get;}      <br \/>Question&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Property&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static System.Drawing.Icon Question {get;}      <br \/>Shield&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Property&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static System.Drawing.Icon Shield {get;}      <br \/>Warning&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Property&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static System.Drawing.Icon Warning {get;}      <br \/>WinLogo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Property&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static System.Drawing.Icon WinLogo {get;}<\/p>\n<p>PS C:\\&gt;<\/p>\n<\/blockquote>\n<p>The remaining lines of code set properties that have names that are relatively self-explanatory. This portion of the function is shown here: <\/p>\n<blockquote>\n<p>Add-Type -AssemblyName system.windows.forms     <br \/>$toast = New-Object system.windows.forms.notifyicon      <br \/>$toast.icon = [system.drawing.systemicons]::information      <br \/>$toast.balloonTipTitle = $Title      <br \/>$toast.visible = $true      <br \/>$toast.BalloonTipText = $Text      <br \/>$toast.ShowBalloonTip($Timeout)<\/p>\n<\/blockquote>\n<p>To receive an event when the balloon is clicked, use the <strong>Register-ObjectEvent<\/strong> cmdlet. The <strong>BalloonTipClicked<\/strong> event occurs when the user clicks the balloon. Other <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.forms.notifyicon_events.aspx\">NotifyIcon events<\/a> are available and would be wired up in the same manner as the <strong>BalloonTipClicked<\/strong> event. The <strong>sourceidentifier<\/strong> property value is used to track the event, and is a string that can be anything I wish to use. The <strong>input<\/strong> object is the new <strong>notifyicon<\/strong> instance that is stored in the <strong>$toast<\/strong> variable. This portion of the script is shown here: <\/p>\n<blockquote>\n<p>Register-ObjectEvent -InputObject $toast -EventName BalloonTipClicked `     <br \/> -SourceIdentifier ballon_clicked<\/p>\n<\/blockquote>\n<p>If an event is generated by clicking the balloon, I want to remove the event from the event queue and unregister the event. When I have completed those two actions, I wish to call the <strong>dispose<\/strong> method of the <strong>notifyicon<\/strong> class to cause the balloon to disappear. This portion of the code is shown here: <\/p>\n<p>if(Wait-Event -Timeout $eventTimeout -SourceIdentifier ballon_clicked)<\/p>\n<blockquote>\n<p> {<\/p>\n<p>&nbsp; Remove-Event -SourceIdentifier ballon_clicked <\/p>\n<p>&nbsp; Unregister-Event -SourceIdentifier ballon_clicked<\/p>\n<p>&nbsp; $toast.Dispose()<\/p>\n<p> }<\/p>\n<\/blockquote>\n<p>On the other hand, it is entirely possible that the event may time out, or the balloon could be closed out by clicking the &ldquo;x&rdquo; in the upper right hand corner of the balloon. If this should happen, the close <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.forms.notifyicon.balloontipclosed.aspx\">BalloonTipClosed<\/a> event is generated, and not the <strong>BalloonTipClicked<\/strong> event. Therefore, to get rid of the balloon, it is necessary to unregister the event and call the dispose method. This portion of the function is shown here: <\/p>\n<blockquote>\n<p>else<\/p>\n<p> {<\/p>\n<p>&nbsp; Unregister-Event -SourceIdentifier ballon_clicked<\/p>\n<p>&nbsp; $toast.Dispose()<\/p>\n<p> }<\/p>\n<p>} #end function new-balloontip<\/p>\n<\/blockquote>\n<p>The entry point to the script gets a <strong>datetime<\/strong> object and stores it in a variable named <strong>$st<\/strong>. It then calls the <strong>new-teatimer<\/strong> function. It then calls the <strong>Get-Date<\/strong> cmdlet once again and stores a <strong>datetime<\/strong> object in the <strong>$et<\/strong> variable. These two timestamps are called in order to verify that the <strong>new-teatimer<\/strong> is working properly. Because tea gets bitter if it is allowed to steep too long, it is important to see how well the <strong>new-teatimer<\/strong> function is working. On my computer, the value between the desired timer and the actual timer was milliseconds, which is good enough for a cup of <a href=\"http:\/\/en.wikipedia.org\/wiki\/Darjeeling_tea\">Darjeeling tea<\/a>. The <strong>New-BalloonTip<\/strong> function is called, and finally the total number of seconds from the <strong>timespan<\/strong> object that is used to compare the two <strong>datetime<\/strong> values is queried from the <strong>New-TimeSpan<\/strong> cmdlet. This portion of the script is shown here:<\/p>\n<blockquote>\n<p>$st = Get-Date     <br \/>new-teatimer      <br \/>$et = Get-Date      <br \/>New-BalloonTip      <br \/>(New-TimeSpan -Start $st -End $et).totalSeconds<\/p>\n<\/blockquote>\n<p>When the script runs, the balloon shown in the following image appears in the notification area. <\/p>\n<p><img decoding=\"async\" style=\"border: 0px initial initial\" alt=\"Image of notification balloon shown when script runs\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8836.WES-10-2-10-01.jpg\" \/><\/p>\n<p>Join me tomorrow for another Weekend Scripter article. I would love for you to follow us on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\">Facebook<\/a>. If you have any questions, send email to us at <a href=\"mailto:scripter@microsoft.com\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/social.technet.microsoft.com\/Forums\/en\/ITCG\/threads\/\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>Ed Wilson and Craig Liebendorfer, Scripting Guys<\/strong><\/p>\n<p><strong><br \/><\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>&nbsp; Microsoft Scripting Guy Ed Wilson here. It has been a great week with some excellent guest bloggers. It is not that I took the week off; rather, I have spent the week working on some special projects I wanted to complete. The great thing about working with guest bloggers is that I get to [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[13,25,42,3,4,61,45],"class_list":["post-16941","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-dates-and-times","tag-displaying-output","tag-events-and-monitoring","tag-scripting-guy","tag-scripting-techniques","tag-weekend-scripter","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>&nbsp; Microsoft Scripting Guy Ed Wilson here. It has been a great week with some excellent guest bloggers. It is not that I took the week off; rather, I have spent the week working on some special projects I wanted to complete. The great thing about working with guest bloggers is that I get to [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/16941","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=16941"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/16941\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=16941"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=16941"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=16941"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}