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: Windows PowerShell