I’m trying to automate disabling fonts via shell.ApplicationinvokeVerb('hide')
I believe the issue is related to -STA and -MTA and passing a reference to MesageLoop to shell.Application object .
I’ve found other font managers with powershell but they all do direct registry manipulation and I’m trying to avoid that.
Steps to Reproduce
execute pwsh -STA or pwsh -MTA using ⊞-R or existing powershell terminal
I am not a mind reader and I cannot see your screen. Where do you type this? In the start menu? Either way, I cannot reproduce your error in any version of powershell. I feel there is a critical piece of information missing here. The error makes me believe you are running this through some type of platform.
Can you attempt to just simply click start, click on powershell, and run these commands one at a time.
PowerShell 7.4.1
It’s possible i have a different log level which is showing this error. The error is coming from the com+ shell.application object. I’ll see if it’s in Windows Event log to help you reproduce.
in further research the “message_loop.cc” message may be a side effect of another shell extension installed on my machine. But the invokeVerb("hide") result is still failing. does it work for you?
.Verbs() is helpful to see what shell verbs are available. Can you try both hide and &Hide ?
$font = $sh.Namespace("C:\Windows\Fonts").ParseName("Playbill Regular")
❯ $font.Verbs()
2024-03-29T17:36:42.529ZE [27784:ShellIpcClient] message_loop.cc:132:Run Run called on MessageLoop that's already been Quit!
Application Parent Name
----------- ------ ----
Pre&view
&Print
&Hide
Edit with &Notepad++
Add to &Favorites
&Copy
&Delete
P&roperties
❯ $font.invokeVerb("hide")
2024-03-29T17:37:04.253ZE [18836:ShellIpcClient] message_loop.cc:132:Run Run called on MessageLoop that's already been Quit!
❯ $font.invokeVerb("&Hide")
2024-03-29T17:37:10.251ZE [16028:ShellIpcClient] message_loop.cc:132:Run Run called on MessageLoop that's already been Quit!
Apologies, but this question is really out of scope for this forum. Based on reading, I’m seeing nothing that suggests this truly is a PS question. It’s more like you’re using PS as a ‘path’ to get what you want accomplished, but the underlying windows api is throwing the error on your end, something Doug or I an’t replicate.
$font.InvokeVerb() makes the font ‘open’ for me when ran in PS, which suggests that the method without any parameters is working (likely makes the call using a default parameter). I’m thinking this default method corresponds with ‘Preview’ on the right click context pane.
I was not able to retrieve any errors using $font.InvokeVerb('hide'). I also don’t think I see what it did either. I presume you’re trying to ‘hide it’ from the GUI in C:\Windows\Fonts. I don’t have experience using that method, but are you sure it accepts ‘hide’ as a parameter or verb? $font.Verbs() seems to be a method designed to to get the available list of verbs associated with the item. However, testing, I can’t seem to get any of them to work. It’s worth pointing out that i see &Hide in the list, not Hide. That said I couldn’t get that to ‘hide’ the font’, and I’d assume a Explorer restart isn’t necessary.
The way I got this to work was to not use InvokeVerb but to capture the verb and run the DoIt() method. I’ve written a few functions to make this easier.
Function Get-Font {
Param(
[parameter(Mandatory)]$FontName
)
$shell = New-Object -ComObject "Shell.Application"
$namespace = $shell.Namespace("C:\Windows\Fonts")
$font = $namespace.ParseName($FontName)
[PSCustomObject]@{
Name = $font.Name
Path = $font.Path
Hidden = $namespace.GetDetailsOf($font,2) -eq 'Hide'
}
}
Function Hide-Font {
Param(
[parameter(Mandatory)]$FontName
)
$font = Get-Font $FontName
if($font.hidden){
Write-Verbose "Font '$FontName' is already set to 'Hide'"
}
else{
Write-Verbose "Setting font '$FontName' to 'Hide'"
$shell = New-Object -ComObject "Shell.Application"
$namespace = $shell.Namespace("C:\Windows\Fonts")
$font = $namespace.ParseName($FontName)
$hideverb = $font.Verbs() | Where-Object Name -like '*hide*'
$hideverb.DoIt()
}
}
Function Show-Font {
Param(
[parameter(Mandatory)]$FontName
)
$font = Get-Font $FontName
if($font.hidden){
Write-Verbose "Setting font '$FontName' to 'Show'"
$shell = New-Object -ComObject "Shell.Application"
$namespace = $shell.Namespace("C:\Windows\Fonts")
$font = $namespace.ParseName($FontName)
$showverb = $font.Verbs() | Where-Object Name -like '*show*'
$showverb.DoIt()
}
else{
Write-Verbose "Font '$FontName' is already set to 'Show'"
}
}
Now you can simply call Show-Font or Hide-Font
$Name = "Brush Script MT Italic"
Hide-Font $Name
Note the functions do not create output. You could alter them to return true or false, which I would recommend using $erroractionpreference = 'Stop' and a try/catch if you wanted to go that route. If you add -Verbose you can see more info