Using Windows System.Speech for TTS in PowerShell 7 (is there an alternative?)

I have the same profile.ps1 in both WindowsPowerShell and PowerShell.
It includes commands that invoke Windows Text-To-Speech.

However, these commands fail when run in PowerShell 7.

The errors occur when I try to use the $PomrptTTS object I create with the following code:

<span class="typ">Add</span><span class="pun">-</span><span class="typ">Type</span> <span class="pun">-</span><span class="typ">AssemblyName</span> <span class="typ">System</span><span class="pun">.</span><span class="pln">speech
$PromptTTS </span><span class="pun">=</span> <span class="typ">New</span><span class="pun">-</span><span class="typ">Object</span> <span class="typ">System</span><span class="pun">.</span><span class="typ">Speech</span><span class="pun">.</span><span class="typ">Synthesis</span><span class="pun">.</span><span class="typ">SpeechSynthesizer</span>

In PowerShell 7, any attempt to access or use my $PormptTTS object, produces the following:

SetValueInvocationException: ....\profile.ps1:82
Line |
  82 |  $PromptTTS.Rate = 0 ; $PromptTTS.Speak("Time for the $((Get-Date).DayofWeek) shuffle")
     |  ~~~~~~~~~~~~~~~~~~~
     | Exception setting "Rate": "Object reference not set to an instance of an object."

MethodInvocationException: ....\profile.ps1:82
Line |
  82 |   e = 0 ; $PromptTTS.Speak("Time for the $((Get-Date).DayofWeek) shuffle")
     |                                             ~~~~~~~~~~~~~~~~~~~~
     | Exception calling "Speak" with "1" argument(s): "Object reference not set to an instance of an object."

<p class=“lang-bsh prettyprint prettyprinted”></p>

If you’re script works fine in windows powershell you could possibly use this?

PowerShell 7.0 marks a move a to .NET Core 3.1, enabling significantly more backwards compatibility with existing Windows PowerShell modules. This includes many modules on Windows that require GUI functionality like Out-GridView and Show-Command, as well as many role management modules that ship as part of Windows.

For Windows, a new switch parameter UseWindowsPowerShell is added to Import-Module. This switch creates a proxy module in PowerShell 7 that uses a local Windows PowerShell process to implicitly run any cmdlets contained in that module. For more information on Import-Module.


https://docs.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7

 

Hi @KrzyDoug,

I’m afraid I do not follow …

How does “Add-Type” and “New-Object” correlate to “Import-Module”?

I was just thinking you could try to run your existing script that works fine in 5.1 using this, but it may not do what I’m thinking. From what I gather it creates an implicit remoting session to 5.1, so you should have access to properties. However most methods won’t be available. When you say “access $PromptTTS” what types of actions do you take up on it? Either way, it’s just an idea!

According to my research, the issue is stated as such

Beginning in PowerShell 6, ReferencedAssemblies doesn't include the default .NET assemblies. You must include a specific reference to them in the value passed to this parameter.
So basically if you want to use the .net assemblies, you'll need to add them into your powershell session. I was able to do the following and at least got the type of object we were after. Perhaps adding this to the top of your script will fix it.
[Reflection.Assembly]::LoadFile('C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Speech.dll')

Then I was able to run those two commands.

Add-Type -AssemblyName System.speech
$PromptTTS = New-Object System.Speech.Synthesis.SpeechSynthesizer

And get-member showed this

$prompttts | gm

TypeName: System.Speech.Synthesis.SpeechSynthesizer

I hope this helps.

Hi @Doug,

Yes, I too got that far, BUT, the object created was effectively empty and so unusable

I have been provided with an answer – by mklement0 at StackOverflow

>>>

As of PowerShell 7.0 / .NET Core 3.1, System.Speech.Synthesis.SpeechSynthesizer is considered a .NET Framework-only API and therefore not supported in .NET Core.

  • A discussion about this is ongoing in this GitHub issue; since the underlying API is specific to Windows, the question is whether it's worth exposing via the cross-platform .NET Core framework.
The workaround is to use the SAPI.SpVoice COM object (which the .NET Framework implementation is ultimately based on, I presume):
$sp = New-Object -ComObject SAPI.SpVoice
$sp.Speak("Time for the $((Get-Date).DayOfWeek) shuffle")
>>>
 

I saw his post and was coming to share it here if needed. Mr. Klement is amazing, I regularly just go to his profiles and view his answers (at various sites) I always learn a ton from his extensive answers. So this workaround directly with the COM object has allowed you to use this in powershell 7?