I was reading Tony’s Blog MSHFORFUN  about using Microsoft Text to Speech engine to output using voice.  He created an Interop assembly for Microsoft TTS com object and used the resulting .Net assembly to do voice output.  Monad provides great support for .Net classes and in many cases, generating Interop assembly is the only way to access unmanaged components.   However, there are cases where this is not needed.  The above example is one of them.

 

Monad provides great support for creating and using COM objects.  You can create a COM object using the same new-object cmdlet that you use for .Net objects. You do this by specifying –ComObject parameter with the ProgID of the COM object you want to create.

 

For Example:

    Let us create Internet explorer application and have it navigate to a particular website.

 

$ie = new-object -comobject Internetexplorer.application

$ie.Navigate2("http://www.microsoft.com")

$ie.visible=1

 

    This creates a new Internet Explorer application COM object.  Invokes a method and sets a property on the COM object.

 

 

Determining what methods and properties are supported in a COM object is a snap using get-member cmdlet.

 

MSH C:\Scripts\> $fso = new-object –com scripting.filesystemobject

MSH C:\Scripts\> $fso | get-member

 

 

   TypeName: System.__ComObject#{2a0b9d10-4b87-11d3-a97a-00104b365c9f}

 

Name                             Member           Type Definition

----                               ----------                        ----------

BuildPath                       Method             string BuildPath (string, string)

CopyFile                        Method             void CopyFile (string, string, bool)

CopyFolder                    Method             void CopyFolder (string, string, bool)

CreateFolder                 Method             IFolder CreateFolder (string)

CreateTextFile              Method             ITextStream CreateTextFile (string, bool, bool)

DeleteFile                      Method             void DeleteFile (string, bool)

DeleteFolder                  Method             void DeleteFolder (string, bool)

DriveExists                    Method             bool DriveExists (string)

FileExists                      Method             bool FileExists (string)

FolderExists                  Method             bool FolderExists (string)

GetAbsolutePathName    Method              string GetAbsolutePathName (string)

GetBaseName                Method             string GetBaseName (string)

GetDrive                       Method             IDrive GetDrive (string)

GetDriveName               Method             string GetDriveName (string)

GetExtensionName        Method             string GetExtensionName (string)

GetFile                          Method             IFile GetFile (string)

GetFileName                 Method             string GetFileName (string)

GetFileVersion               Method             string GetFileVersion (string)

GetFolder                      Method             IFolder GetFolder (string)

GetParentFolderName    Method              string GetParentFolderName (string)

GetSpecialFolder            Method             IFolder GetSpecialFolder (SpecialFolderConst)

GetStandardStream   Method              ITextStream GetStandardStream (StandardStreamTypes, bool)

GetTempName              Method             string GetTempName ()

MoveFile                       Method             void MoveFile (string, string)

MoveFolder                   Method             void MoveFolder (string, string)

OpenTextFile                Method             ITextStream OpenTextFile (string, IOMode, bool, Tristate)

Drives                          Property            IDriveCollection Drives () {get}

 

 

 

 

Now, coming back to using TTS engine, we could rewrite Tony’s script as follows:

 

begin

{  

    $SpVoice = new-object -com SAPI.SpVoice

    $count = 0

   

}

process

{

    if ($_)

    {

       $count++

       $StringToSay = $_ | out-string

       "$count: $StringToSay"

  

        $SpVoice.Speak($StringToSay)

    }

    else

    {

       "null object"

    }

}

end

{

  "Number of objects successfully output: $count"

}

 

 

I have removed the code that saves the voice output to a file, but it is easy to add that functionality to the above code.  This code creates the Speech API’s SpVoice COM object directly, for each object coming through the pipeline, converts the object to string, and uses the Speak method on COM object to do Voice output.

 

Try it out!

 

There is one caveat with using COM objects in Monad though.   In VBScript or JavaScript, You can release a COM object by setting the variable to null.  Scripting engine underneath calls the Release method on the COM object to decrement the reference count and when the reference count reaches zero, COM object is released.   In managed world, releasing COM object is not deterministic.  COM object is released when the variable holding the COM object is garbage collected.  This might be ok in many cases, but there are cases where we need to release the COM object immediately.  .Net framework provides a way to solve this problem.  You can use

 

System.Runtime.InteropServices.Marshal.ReleaseComObject()

 

to release the COM object.  However,  I do not recommend this.    .Net framework Interop layer maintains only one reference count on the underlying COM object regardless of how many managed clients refer to that object.  When you call ReleaseComObject, underlying COM object is released regardless of the number of clients.  These other clients of COM object will get InvalidComObjectException when they try to invoke a method or access a property.  Be careful when you use this approach and be sure that you are releasing the last reference to the COM object.

 

 

Monad is theonly scripting platform that provides access to .Net, WMI, COM in a well-integrated manner. Enjoy the power but use it with caution. J