Win 10 Notification Toasting for your daily bread!
Grumpy Admin here, is it the weekend yet? I wish… Over the last few days I’ve been doing some work for a mate. Nothing exciting but it has been keeping me out of trouble and away from doing actual work! Cause doing what I’m paid to do just isn’t the British way of working!
The plus side of doing a side PowerShell project is as everyone walks past my screen they see code and TechNet and MSDN webpages all over my screen, and hear me cursing and grumbling to myself every so often. I learned a long time ago, if you seem grumpy and intense on something, people don’t bother you with menial simple problems! Simply put, if you’re not on Facebook, people conclude your working!
Worse case, self-improvement always helps to make me a better Systems Administrator, so it all loosely work related anyway! That is my justification and I am sticking to it!
Now the problems my friend asked me to solve were not the normal problems and in fact there were very few hits on google for this type of thing before! Total bending PowerShell to do things that well you wouldn’t normally do!
So as all rules of code and PowerShell logic have been broken by me these last few days! Dam I feel dirty. But as ever, I broke the problems down and eventually came to working code, and now to just refactor to something presentable!
However, this usage means that I was using a while($true) firing off random PowerShell jobs. This works nice, but as ever sometimes there are errors.
Working with PowerShell jobs, and as you will know is they don’t interact with the console, so if there is an error it’s hard to see or be notified. Especially as I designed my jobs to fire and forget! I didn’t do any receive-job and handling code. As this wasn’t needed.
Naturally I have normal error handling in place, but I wasn’t get the output no sea of red and would have to cancel and receive-job to see my types.
When designing a system and testing and debugging this became a pain! Especially as the start-job launched at random intervals or based on other events. It got complex quickly! A symptom of bad code design I know! The crux of my issue was not having an easy way to be alerted on errors inside code blocks within my start-jobs.
As I was thinking about this I got an email and outlook produced an alert toast notification at the bottom of my screen! I expect there are other methods, but I wanted this, because it’s cool… well Grumpy Admin has a strange idea of what is cool but hey ho!
So I looked at how the Windows 10 Toast Notification works. After some reading research I found the following
Now it appears that his a Windows Universal App, Windows Runtime thing yuck WINRT code – in particularly this toast notification is using the windows.ui.notifications namespace.
As we know we can load .net name spaces and use them inside of PowerShell –
Now in order to send a notification to the Windows 10 Action Center, we need the namespace, and we also need to define the toast structure and this is defined as a XAML structure.
XAML actually stands for Extensible Application Markup Language
You can find more information about it here
https://msdn.microsoft.com/en-us/library/cc295302.aspx
but the bottom line is, it’s like XML for application that can define it layout and components and stuff like that – a simple view and other more technical people will cringe at that but meh! I’m not a programmer, I am a Grumpy System Administrator and I am very very lazy!
So the steps from my understanding is we need to do the following!
- create an XML object
- create a toast
- create a toast notifier object
- display said toast
This should be actually quite simple.
The first thing I need to do is to load the correct namespaces in. So I can access the correct structures and objects and tinker and play and call them!
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] [Windows.UI.Notifications.ToastNotification, Windows.UI.Notifications, ContentType = WindowsRuntime]
Excellent so far, next I need the XML for the toast notification – as you can see from the MSDN page the structure is simple… the thing I notice is that there is ToastImageAndText01
A quick google leads me to this page
Which gives me the template types that I can use, after some thought I settle on using the Template option number 4. This is how I want my notifications to look when I send them to my screen!
Now there is a programmable method that allows you to get the template content – but grumpy admin is lazy and well I know what I want. So I will just declare the XML statically and use the string features of PowerShell to add in the required text. So the bottom line this, my xml will look like this
<toast launch='Posh_Error'> <visual> <binding template='ToastImageAndText04'> <text id='1'>Grumpy Admin</text> <text id='2'>My Message will go here</text> <image id='1' src='file:///c:/scripts/test.png' /> </binding> </visual> </toast>
I save a random image from google and shrink it to the correct size and save it in to a local path! and I specify this in the xml <image> node!
Now the Toast notification needs a different data type than [XML] it requires a [Windows.Data.Xml.Dom.XmlDocument] which is actual an object
so we can create a new object the normal PowerShell way!
$msgxml =New-Object Windows.Data.Xml.Dom.XmlDocument
Now let’s do Grumpy Admins normal thing that he does on objects! You got it, let’s genetically modify it! or just run the get-member cmdlet…
$msgxml |gm
Oh good, lots of objects and lots of properties to use!
I have some xml! Yippy! However, I want to put it in to this object…. oh look there is a load XML method… let’s do that seems natural to me!
$msgxml.LoadXml("<toast launch='Posh_Error'> <visual> <binding template='ToastImageAndText04'> <text id='1'>Grumpy Admin</text> <text id='2'>My Message will go here</text> <image id='1' src='file:///c:/scripts/test.png' /> </binding> </visual> </toast>")
So we can now create of toast notification object! We do this by calling the ::new() function supplying our XML
$grumpymsg = [Windows.UI.Notifications.ToastNotification]::new($msgxml)
Let’s as ever, just do a quick Get-member on that object. Just so we know what we are dealing with!
interesting, now we need to create a toast notifier object,
$notice = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Grumpy")
and let’s look at that object for methods and properties.
Now we need to display this notification, let’s give it a go by calling the .Show() that was helpfully listed when we did the get-member
$notice.Show($grumpymsg)
fantastic that works great! Perfect, Grumpy Admin is less grumpy!
Now that I have some functional code, how can I use this to solve my problem of getting notifications when running in PowerShell start-jobs.
Now I’m running PowerShell 5 as I’m on Windows 10 – ummmm didn’t that implement classes.
I could create this as a class and create new-objects based on that class and use that inside the -script blocks being called by the start-jobs!
Excellent, So I produce the following PowerShell class, same base notification code just slight tweaks to make it class friendly. Using a bit of $this etc <– see what I did, Grumpy Admin can be funny at times!
class win10_posh_error { [string]$title="A Grumpy Error" [string]$message="a grumpy message here and how long will this line go on and on for i don't know will there be an overflow or something crazy like that?" constructor(){ [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] [Windows.UI.Notifications.ToastNotification, Windows.UI.Notifications, ContentType = WindowsRuntime] } set_title([string]$new_title) { $this.title =$new_title } set_message([string]$new_message) { $this.message =$new_message } diaplay(){ [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] $msgxml =New-Object Windows.Data.Xml.Dom.XmlDocument $msgxml.LoadXml("<toast launch='Posh_Error'> <visual> <binding template='ToastImageAndText04'> <text id='1'>$($this.title)</text> <text id='2'>$($this.message)</text> <image id='1' src='file:///c:/scripts/test.png' /> </binding> </visual> </toast>") $grumpymsg = [Windows.UI.Notifications.ToastNotification]::new($msgxml) $notice = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($this.title) $notice.Show($grumpymsg) } }
As you can see I am using a constructor to load the namespaces, I have three simple methods, two that accept a [string] to set the title and the actual message and another method that displays.
This is a very basic class, I’m not doing any validation or error checking etc! Hell I am not even checking if the user is running windows 10 etc! Grumpy Admin is a lazy coder and a very bad coder, I expect people will find faults and improve and do stuff with it! Feel free to comment and rip me, you only get better with feedback! This works for me at the moment so Meh! Not going to waste much more time on it as there are other problems to solve!
Now to test this out to make sure it works! I have to point out that as a script block is its own scope and instance you have to load the class. This can be done using the import-module code 🙂 So I have a divide by zero inside a try and catch block which should cause and error to be generated! Perfect test I think!
start-job –scriptblock { Import-Module 'D:\GDRIVE\Projects\Grumpy Admin\Work In Progress\BREENSIM\posherror.ps1' try { 1/0 } catch { $a = new-object win10_posh_error $ErrorMessage = $_.Exception.Message $a.Set_Message($ErrorMessage) $a.diaplay() } }
Excellent this works, so now I can spawn lots of jobs and if there errors I can get some notifications on screen in the Windows 10 Action Center, chances are I will blink and miss it but better than having to stop and receive-job to check if there was a syntax error!
Hazzy