Saturday, December 02, 2006 2:37 AM bart

PowerShell - Ask Merlin: a cool demo of using COM objects

Did you know Windows PowerShell has out-of-the-box support for COM objects? Many of you will know by now that Windows PowerShell is an object-oriented shell, so it supports objects like .NET classes:

PS C:\temp> $ts = new-object TimeSpan(1,0,0) PS C:\temp> $ts Days : 0 Hours : 1 Minutes : 0 Seconds : 0 Milliseconds : 0 Ticks : 36000000000 TotalDays : 0,0416666666666667 TotalHours : 1 TotalMinutes : 60 TotalSeconds : 3600 TotalMilliseconds : 3600000 PS C:\temp> [DateTime]::Now Thursday 30 November 2006 2:41:49 PS C:\temp> [DateTime]::Now + $ts Thursday 30 November 2006 3:41:53

However, interop with older technologies (in casu COM) will stay a need for the foreseeable future. So, Windows PowerShell can work with this kind of objects too. The classical example is using Internet Explorer in Windows PowerShell:

PS C:\temp> $ie = new-object -Com internetexplorer.application PS C:\temp> $ie | gm -MemberType Method TypeName: System.__ComObject#{d30c1661-cdaf-11d0-8a3e-00c04fc9e26e} Name MemberType Definition ---- ---------- ---------- ClientToWindow Method void ClientToWindow (int, int) ExecWB Method void ExecWB (OLECMDID, OLECMDEXECOPT, Variant, Var... GetProperty Method Variant GetProperty (string) GoBack Method void GoBack () GoForward Method void GoForward () GoHome Method void GoHome () GoSearch Method void GoSearch () Navigate Method void Navigate (string, Variant, Variant, Variant, ... Navigate2 Method void Navigate2 (Variant, Variant, Variant, Variant... PutProperty Method void PutProperty (string, Variant) QueryStatusWB Method OLECMDF QueryStatusWB (OLECMDID) Quit Method void Quit () Refresh Method void Refresh () Refresh2 Method void Refresh2 (Variant) ShowBrowserBar Method void ShowBrowserBar (Variant, Variant, Variant) Stop Method void Stop () PS C:\temp> $ie.Navigate2("http://blogs.bartdesmet.net/bart") PS C:\temp> $ie | gm V* TypeName: System.__ComObject#{d30c1661-cdaf-11d0-8a3e-00c04fc9e26e} Name MemberType Definition ---- ---------- ---------- Visible Property bool Visible () {get} {set} PS C:\temp> $ie.Visible = $true

In the sample above you can see all of the goodness of Windows PowerShell working together with COM, i.e. the ability to create objects, to reflect against the object to find out methods and properties, and to invoke operations. However, a much cooler sample of COM interop is to use the Microsoft Agent, which you might still know from the old Office days with the Office assistants. The cool thing about it is that it's exposed as a COM API, so you can use it everywhere you see fit. So, what about a PowerShell assistant that can speak (i.e. produce audio) to you? Here's the sample step-by-step.

Step 1 - Load and show Merlin

First we'll load and show our assistant: Merlin. To do this, we have to create an instance of the COM type Agent.Control.2 and load the character "Merlin" in it. Here's how:

PS C:\temp> $agent = new-object -com Agent.Control.2 PS C:\temp> $agent.Connected = 1 PS C:\temp> $agent.Characters.Load("Merlin") PS C:\temp> $merlin = $agent.Characters.Character("Merlin") PS C:\temp> $merlin.Show()

Needless to say you can discover this info too using get-member:

PS C:\temp> $agent | gm TypeName: System.__ComObject#{8563ff20-8ecc-11d1-b9b4-00c04fd97575} Name MemberType Definition ---- ---------- ---------- ShowDefaultCharacterProperties Method void ShowDefaultCharacterPropertie... AudioOutput Property IAgentCtlAudioObjectEx AudioOutput... Characters Property IAgentCtlCharacters Characters () ... CommandsWindow Property IAgentCtlCommandsWindow CommandsWi... Connected Property bool Connected () {get} {set} PropertySheet Property IAgentCtlPropertySheet PropertyShe... RaiseRequestErrors Property bool RaiseRequestErrors () {get} {... SpeechInput Property IAgentCtlSpeechInput SpeechInput (... Suspended Property bool Suspended () {get}

 

PS C:\temp> $merlin | gm TypeName: System.__ComObject#{de8ef600-2f82-11d1-acac-00c04fd97575} Name MemberType Definition ---- ---------- ---------- Activate Method bool Activate (Variant) GestureAt Method IAgentCtlRequest GestureAt (short, short) Get Method IAgentCtlRequest Get (string, string, Variant) Hide Method IAgentCtlRequest Hide (Variant) Interrupt Method IAgentCtlRequest Interrupt (IDispatch) Listen Method bool Listen (bool) MoveTo Method IAgentCtlRequest MoveTo (short, short, Variant) Play Method IAgentCtlRequest Play (string) Show Method IAgentCtlRequest Show (Variant) ShowPopupMenu Method bool ShowPopupMenu (short, short) Speak Method IAgentCtlRequest Speak (Variant, Variant) Stop Method void Stop (Variant) StopAll Method void StopAll (Variant) Think Method IAgentCtlRequest Think (string) Wait Method IAgentCtlRequest Wait (IDispatch) Active Property short Active () {get} AnimationNames Property IAgentCtlAnimationNames AnimationNames () {get} AutoPopupMenu Property bool AutoPopupMenu () {get} {set} Balloon Property IAgentCtlBalloonEx Balloon () {get} Commands Property IAgentCtlCommandsEx Commands () {get} Description Property string Description () {get} {set} ExtraData Property string ExtraData () {get} GUID Property string GUID () {get} HasOtherClients Property bool HasOtherClients () {get} Height Property short Height () {get} {set} HelpContextID Property int HelpContextID () {get} {set} HelpFile Property string HelpFile () {get} {set} HelpModeOn Property bool HelpModeOn () {get} {set} IdleOn Property bool IdleOn () {get} {set} LanguageID Property int LanguageID () {get} {set} Left Property short Left () {get} {set} MoveCause Property short MoveCause () {get} Name Property string Name () {get} {set} OriginalHeight Property short OriginalHeight () {get} OriginalWidth Property short OriginalWidth () {get} Pitch Property int Pitch () {get} SoundEffectsOn Property bool SoundEffectsOn () {get} {set} Speed Property int Speed () {get} SRModeID Property string SRModeID () {get} {set} SRStatus Property int SRStatus () {get} Top Property short Top () {get} {set} TTSModeID Property string TTSModeID () {get} {set} Version Property string Version () {get} VisibilityCause Property short VisibilityCause () {get} Visible Property bool Visible () {get} Width Property short Width () {get} {set}

 

Step 2 - Playing animations

One of the things characters can do is play some animation. To get a list of the animations, query $agent.AnimationNames. A sample to find all animations with an o in it is shown below:

PS C:\temp> $merlin.AnimationNames | where { $_ -match "o" } RestPose GestureDown Show Processing Acknowledge DontRecognize StopListening GetAttention GetAttentionReturn Congratulate_2 Announce Congratulate Confused MoveRight MoveLeft MoveUp MoveDown WriteContinued DoMagic1 DoMagic2 LookDown LookDownBlink LookDownReturn LookLeft LookLeftBlink LookLeftReturn LookRight LookRightBlink LookRightReturn LookUp LookUpBlink LookUpReturn ReadContinued GetAttentionContinued Process

To play an animation it's suffient to call $agent.Play(animationname), for example:

PS C:\temp> $merlin.Play("Congratulate")

with the following result:

Step 3 - Think and speak

Time to make Merlin do some work. One thing he can (pretend to) do is think using the Think method taking a string:

PS C:\temp> $merlin.Think("PowerShell is fun!")

Next we'll make Merlin speak. You can better turn sound on a low volume if colleagues are in the near neighborhood (or maybe you just want to track attention, your choice). Let's start with a simple sample:

PS C:\temp> $merlin.Speak("Welcome to Windows PowerShell COM support!")

 

Step 4 - Something more useful

So, Speak takes a string as an argument. Windows PowerShell can provide us with strings on lots of places. Time to bring these two together. For example, do you want a speaking directory listing?

PS C:\temp> dir *.dll | foreach { $merlin.Speak("Start of directory listing") } { $_.Name; $merlin.Speak($_.Name + " " + $_.Length +" bytes."); Start-Sleep 5; } { "End of directory listing" }

Or what about a service state indicator?

PS C:\temp> get-service [c-d]* | foreach { $merlin.Speak($_.Name + " is " + $_.Status + "."); Start-Sleep 5 }

Or printing a set of memory intensive processes?

PS C:\temp> get-process | sort WorkingSet -descending | select -first 5 | foreach { $merlin.Speak($_.ProcessName + " uses " + $_.WorkingSet + " bytes of memory."); Start-Sleep 5 }

And for real geeks, what about a C# program listing?

PS C:\temp> type downloadfilecmdlet.cs | foreach { $merlin.Speak($_); Start-Sleep 5 }

 

Step 5 - Suppress Merlin's output

In all the screenshots above you see some unwanted output. In order to suppress this, here's a little trick: append out-null, like this:

PS C:\temp> dir *.dll | foreach { $merlin.Speak("Start of directory listing") | out-null } { $_; $merlin.Speak($_.Name + " " + $_.Length +" bytes.") | out-null; Start-Sleep 5; } { "End of directory listing" } PS C:\temp> get-service | foreach { $_; $merlin.Speak($_.Name + " is " + $_.Status + ".") | out-null; Start-Sleep 5 } PS C:\temp> get-process | foreach { $_; $merlin.Speak($_.ProcessName + " uses " + $_.WorkingSet + " bytes of memory.") | out-null; Start-Sleep 5 } PS C:\temp> type downloadfilecmdlet.cs | foreach { $_; $merlin.Speak($_) | out-null; Start-Sleep 5 }

Using $_ you can print the objects themselves to retain typical output, as shown below (for the first line):

 

Step 6 - Say goodbye to Merlin

To hide the Merlin character, just ask him kindly to disappear:

PS C:\temp> $merlin.Hide()

Enjoy COM interop in Windows PowerShell!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:

Comments

# links for 2006-12-04

Monday, December 04, 2006 2:21 AM by Impersonation Failure

PowerShell - Ask Merlin Nifty sample using COM objects from Powershell to create a PowerShell Assistant

# ms agent | keyongtech

Sunday, January 18, 2009 8:47 AM by ms agent | keyongtech

Pingback from  ms agent | keyongtech