Introduction

A colleague of mine asked me to put some fonts in the C:\WINDOWS\FONTS directory. I found a script that did the trick. The install went fine but after the uninstall of the fonts, there were some gaps in Wordpad:

Gaps in Wordpad

That was not how it should work. As I did not have the time to investigate further, I decided to create a Windows Installer package that did the job.

Using C Sharp Code to add and remove fonts

But it kept me busy, so I investigated further. On Github, I found C Sharp code that also adds the font resource to the system font table using gdi32.dll.

I modified that code so I also removes the resource from the system font table during uninstall. And there were no font gaps in Wordpad anymore.

This technique is used in the first version InstallorUninstallFonts_v01.ps1.

Additionally, I added a GUI for a nice frontend. And the GUI language should be the same as the OS language.

Problems and solutions

The following issues have been solved while creating this script.

Powershell ConstrainedLanguage and the logged-on user

You can restrict PowerShell with AppLocker. So the majority of the functions should be able to work with a limited PowerShell. The PowerShell language is stored in $ExecutionContext.SessionState.LanguageMode.

See about Language Modes for more information.

If the languagemode is RestrictedLanguage or NoLanguage than the script will not start at all. But if the languagemode is set to ConstrainedLanguage then there is a limited set of commands available.

With ConstrainedLanguage is not possible to run this command to get the current logged in user:

[System.Security.Principal.WindowsIdentity]::GetCurrent().Name

ConstrainedLanguage

So you have to work around this issue.

The command whoami /user gives you the current logged on userid. But be carefull here: the output differs per language!

On an US-based OS you will find this:

USER INFORMATION
----------------

User Name SID
==================== ==============================================
windows10wkg_02\test S-1-5-21-1753037473-1212035439-1379364385-1001

But on a German-based OS the output looks like this:

BENUTZERINFORMATIONEN
---------------------

Benutzername SID
==================== ==============================================
windows10wkg_02\test S-1-5-21-1753037473-1212035439-1379364385-1001

The only way to work around this issue is to read the header and get the first item.

And this is the code to get it done:

Clear-Host
$tmpScriptAccount   = (whoami /user /FO csv | convertfrom-csv)
$tmpScriptAccount
$TranslatedUserName = $tmpScriptAccount.PSObject.Properties.Name[0]
$TranslatedUserName
$ScriptAccount      = $($tmpScriptAccount.$TranslatedUserName).ToUpper()
$ScriptAccount

Find the username with whomami

Powershell ConstrainedLanguage and detection of elevated rights

And the same to detect if a user is local admin or not. The code
([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"544")

does not work when PowerShell ConstrainedLanguage is set.

Then we can use whoami /groups and check if we find the admin sid S-1-5-32-544.

"S-1-5-32-544" -in (whoami /groups /FO csv | convertfrom-csv).SID

Not elevated but still local admin

That looks right... but it is not right. If the user is a member of the local admins group but starts Powershell not as an admin, the user is still considered an admin. So only one option left: try to save something in the C:\WINDOWS\Fonts folder. If that succeeds, then the user runs the script with elevated rights, otherwise not. So I made a piece of code that creates a random file in C:\Windows\Fonts, and checks if the file exists.

Clear-Host
$RandomFile = ""
For($a=1; $a -le 8; $a++)
{

 $RandomFile += [char](Random(97..122))
}

$RandomFile = "$($env:windir)\Fonts\$RandomFile.txt"

Try
 {
  New-Item $RandomFile -Force -ErrorAction SilentlyContinue | Out-Null
  if(test-path($RandomFile))
   {
    Write-Host "Succes! Run with elevated rights."
    Remove-Item $RandomFile -Force
   }
    else
   {
    Write-Host "Not run with elevated rights."
   }
 }
  Catch
 {
  Write-Host "Not run with elevated rights."
 }

No elevated rights.

Elevated rights

Language

The GUI language should be in the same language as the OS language. You can read the language settings with Get-Culture and Get-UICulture. When you start a new cmd process as a different user, other culture settings apply:

Get-UICulture and Get-Culture

So, I used the registry to read the language settings:

Key:   HKEY_CURRENT_USER\Control Panel\Desktop
Value: PreferredUILanguages

And if the above-mentioned key /value combination does not exist:

Key:   HKEY_USERS\.DEFAULT\Control Panel\Desktop\MuiCached
Value: MachinePreferredUILanguages

Via the registry

And that works always.

The retrieved language is used for the GUI. The json file InstallorUninstallFonts_v02.json contains all the translations. The content from the mentioned json file is read in the variable $FontActions. With $FontActions.es you see all the Spanish translations:

Translations

Only do this after running and exiting the script.

You can override the GUI language with the command line option LanguageOverride.

Error codes

My next goal is clear error handling. So I decided to use bit-wise error handling:

Bit  1 set: <not used>
Bit 2 set: Powershell ConstrainedMode blocks running this script.
Bit 4 set: Error: the folder '<FolderName>' does not exists.
Bit 8 set: You need elevated (admin) rights to install the fonts per machine.
Bit 16 set: The screenwidth of '<DetectedWidth>' is greater than the allowed width of '<AllowedWidth>'.
Bit 32 set: The screenheight of '<DetectedHeight>' is greater than the allowed height of '<AllowedHeight>'.

I thought that something like $ErrorNumber -AND 4 will do the trick. But that appears to be true, even if $ErrorNumber has the value 5.
What works however is the 'binary and': $ErrorNumber -BAND 4 will return 4 if $ErrorNumber has the value 5. But $ErrorNumber -BAND 2 will return 0. So you can check easily with ($ErrorNumber -BAND 4) -eq 4 and ($ErrorNumber -BAND 2) -eq 2.

Logical BAND

And if you test this on a machine with a German UI:

Error handling ConstrainedLanguage

Error handling (German)

Error handling (English)

Logfile

The logfile has been modified so it reflects the calling function. You can find the calling function with the command $PSCmdlet.MyInvocation.MyCommand.Name.

In action

All these examples are created on a Windows 10 machine with a German User Interface.

Install and uninstall fonts with the GUI

The installation command is ."C:\Fonts\InstallorUninstallFonts_v02\InstallorUninstallFonts_v02.ps1"

Including subfolders

Select 'Including subfolders' to include the subfolders as well. Otherwise, there are no fonts found.

Select the suggested folder.

By default, the folder where the script is in is selected. That is why it is important to include the subfolders as well.

Install the fonts

Install the fonts.

Completed

Completion

Fonts are available in Wordpad

The installed fonts are available in Wordpad.

Remove the selected fonts.

Remove the selected fonts. As an additional test some fonts that have not been installed first, are selected as well.

Completion notice.

The completion notice. There are two errors: two fonts that have not been installed cannot be uninstalled.

Check Wordpad again.

Check Wordpad again. The fonts are not available. And that is correct.

Click for exit.

Click for exit.

Silent installs in the English language

The installation command is ."C:\Fonts\InstallorUninstallFonts_v02\InstallorUninstallFonts_v02.ps1" -Silent -ActionToPerform Install -FolderWithFonts "C:\Fonts\InstallorUninstallFonts_v02" -IncludeSubFolders -ForcePerMachine -LanguageOverride en

Elevated rights are needed.

The installation command is ."C:\Fonts\InstallorUninstallFonts_v02\InstallorUninstallFonts_v02.ps1" -Silent -ActionToPerform Install -FolderWithFonts "C:\Fonts\InstallorUninstallFonts_v02" -IncludeSubFolders -ForcePerMachine -LanguageOverride en -Verbose

Install with elevated rights.

Install with elevated rights.

And the check in Wordpad.

The silent uninstall:

."C:\Fonts\InstallorUninstallFonts_v02\InstallorUninstallFonts_v02.ps1" -Silent -ActionToPerform Uninstall -FolderWithFonts "C:\Fonts\InstallorUninstallFonts_v02" -IncludeSubFolders -ForcePerMachine -LanguageOverride en

No feedback.

There is no feedback as the parameter 'verbose' has not been used.

Wordpad check.

And the well-known check in Wordpad.

The help information. This information has been gathered from a Windows 10 machine with the English language.


NAME
C:\Fonts\InstallorUninstallFonts_v02\InstallorUninstallFonts_v02.ps1

SYNOPSIS
Installs or uninstalls fonts. By default the fonts are installed in the users' font directory. That can be overruled.
If the fonts are installed machine wide than the uninstall should also be machine wide. The same for per user font
installs.

SYNTAX
C:\Fonts\InstallorUninstallFonts_v02\InstallorUninstallFonts_v02.ps1 -Silent [-DetailedLogging] [-LanguageOverride
<String>] -ActionToPerform <String> -FolderWithFonts <String> [-ForcePerMachine] [-IncludeSubFolders] [<CommonParameters>]

C:\Fonts\InstallorUninstallFonts_v02\InstallorUninstallFonts_v02.ps1 [-Silent] [-DetailedLogging] [-LanguageOverride <String>]
[-ActionToPerform <String>] [-FolderWithFonts <String>] [-ForcePerMachine] [-IncludeSubFolders] [<CommonParameters>]

DESCRIPTION
This script installs or uninstalls fonts

PARAMETERS
-Silent [<SwitchParameter>]
-DetailedLogging [<SwitchParameter>]
-LanguageOverride <String>
-ActionToPerform <String>
-FolderWithFonts <String>
-ForcePerMachine [<SwitchParameter>]
-IncludeSubFolders [<SwitchParameter>]

<CommonParameters>
This cmdlet supports the common parameters: Verbose, Debug,
ErrorAction, ErrorVariable, WarningAction, WarningVariable,
OutBuffer, PipelineVariable, and OutVariable. For more information, see
about_CommonParameters (https:/go.microsoft.com/fwlink/?LinkID=113216).

-------------------------- EXAMPLE 1 --------------------------

PS C:\>Start the application and show the GUI.
."InstallorUninstallFonts_v02.ps1"


-------------------------- EXAMPLE 2 --------------------------

PS C:\>Start the application, show the GUI and set the folder \\server\share as the folder with the fonts.
."InstallorUninstallFonts_v02.ps1" -FolderWithFonts \\server\share


-------------------------- EXAMPLE 3 --------------------------

PS C:\>Start the application, show the GUI and set the folder \\server\share as the folder with the fonts. Include
subfolders
."InstallorUninstallFonts_v02.ps1" -FolderWithFonts \\server\share -IncludeSubFolders


-------------------------- EXAMPLE 4 --------------------------

PS C:\>Start the application, show the GUI and set the folder \\server\share as the folder with the fonts. Force the Italian GUI language.

."InstallorUninstallFonts_v02.ps1" -FolderWithFonts \\server\share -LanguageOverride it
The following languages are available:
- en -> English
- de -> German
- fr -> French
- nl -> Dutch
- it -> Italian
- sp -> Spanish
- id -> Indonesian


-------------------------- EXAMPLE 5 --------------------------

PS C:\>Start the application and install all the fonts that are in \\server\share silently (without a GUI).
."InstallorUninstallFonts_v02.ps1" -Silent -ActionToPerform Install -FolderWithFonts \\server\share


-------------------------- EXAMPLE 6 --------------------------

PS C:\>Start the application with verbose logging and uninstall all the fonts that are in \\server\share silently (without a GUI).
."InstallorUninstallFonts_v02.ps1" -Silent -ActionToPerform Uninstall -FolderWithFonts \\server\share -Verbose


-------------------------- EXAMPLE 7 --------------------------

PS C:\>Start the application and install all the fonts machine wide that are in \\server\share silently (without a
GUI).
."InstallorUninstallFonts_v02.ps1" -Silent -ActionToPerform Install -FolderWithFonts \\server\share -ForcePerMachine


REMARKS
To see the examples, type: "get-help C:\Fonts\InstallorUninstallFonts_v02\InstallorUninstallFonts_v02.ps1 -examples".
For more information, type: "get-help C:\Fonts\InstallorUninstallFonts_v02\InstallorUninstallFonts_v02.ps1 -detailed".
For technical information, type: "get-help C:\Fonts\InstallorUninstallFonts_v02\InstallorUninstallFonts_v02.ps1 -full".

 

The JSON file InstallorUninstallFonts_v02.json with the translations :
{
"en" :
 {
     "Error02Text"                : "Powershell ConstrainedMode blocks running this script.",
     "Error04Text"                : "Error: the folder '<FolderName>' does not exists.",
     "Error08Text"                : "You need elevated (admin) rights to install the fonts per machine.",
     "Error16Text"                : "The screenwidth of '<DetectedWidth>' is greater than the allowed width of '<AllowedWidth>'.",
     "Error32Text"                : "The screenheight of '<DetectedHeight>' is greater than the allowed height of '<AllowedHeight>'.",
     "Error"                      : "Error!",
     "Information"                : "Information!",
     "Install"                    : "install",
     "Uninstall"                  : "uninstall",
     "FolderCreated"              : "The folder '<FolderName>' has been created.",
     "ErrorCreatingFolder"        : "Something went wrong while creating the folder '<FolderName>'.",
     "FolderAlreadyExists"        : "The folder '<FolderName>' already exists.",
     "Details"                    : "Details:",
     "SourceFile"                 : "Source file:",
     "FontName"                   : "Font name:",
     "FontNameRegistry"           : "Font name for registry:",              
     "ErrorCopyFont"              : "There was an error while copying '<FullFileName>' to 'NewFontFile': <ErrorMessage>.",
     "ErrorInstallingFont"        : "    Result: there was an error installing font '<FontName>' (File '<NewFontFile>').",
     "SuccessInstallingFont"      : "    Result: the font '<FontName>' (File '<NewFontFile>') has been installed successfully.",
     "ErrorUpdatingRegistry"      : "    Result: there was an error while updating the registry: <ErrorMessage>",
     "SuccessUpdatingRegistry"    : "    Result: the registry has been updated.",
     "FileAlreadyExists"          : "The file '<NewFontFile>' already exists. So nothing is done.",
     "ErrorFontNotUninstalled"    : "Error: the font '<FolderName>' (File '<NewFontFile>') has not been uninstalled.",
     "RetryInstallFont"           : "    I will try to install and uninstall the font again.",
     "RetrySuccess"               : "    Result: the font '<FontName>' (File '<NewFontFile>') has been uninstalled.",
     "RetryFailure"               : "    Still not successfull with uninstalling '<FontName>' (File '<NewFontFile>').",
     "RetryFailureAgain"          : "    Still no luck with installing the font '<FontName>' (File '<NewFontFile>') again.",
     "FileNotFound"               : "    Error: The file '<NewFontFile>' does not exists.",
     "ProgressbarText"            : "Processing file: '<File>' for <ActionToDo>.",
     "ProgressbarCompleted"       : "The operation has been completed:",
     "ProgressbarSuccess"         : "  - <numSuccess> successfull",
     "ProgressbarFailures"        : "  - <numFailures> failures",
     "SpecifyFontsFolder"         : "Specify the folder with the fonts to install",
     "ReturnedFontsFolder"        : "The user has choosen the folder '<FolderName>'.",
     "frmFonts"                   : "Add or Remove Fonts",
     "chkRecurse"                 : "Include sub folders",
     "btnExit"                    : "&Exit",
     "chkMachineWide"             : "Install the fonts for all users.",
     "chkAdmin"                   : "User has elevated (admin) rights.",
     "grpFolder"                  : "Install from folder",
     "chkAllItems"                : "Select or unselect all items",
     "btnUninstallSelectedFonts"  : "&Uninstall selected fonts",
     "btnInstallFonts"            : "&Install selected fonts",
     "btnGetFolderContent"        : "&Get folder content",
     "txtMessage"                 : "Reading the fonts in '<FolderName>'."
 },

"de" :
 {
     "Error02Text"                : "Powershell ConstrainedMode blockiert die Ausführung dieses Skripts.", 
     "Error04Text"                : "Fehler: Der Ordner '<FolderName>' existiert nicht.",
     "Error08Text"                : "Sie benötigen erhöhte (Administrator-)Rechte, um die Schriftarten pro Computer zu installieren.",
     "Error16Text"                : "Die Bildschirmbreite von '<DetectedWidth>' ist größer als die zulässige Breite von '<AllowedWidth>'.",
     "Error32Text"                : "Die Bildschirmhöhe von '<DetectedHeight>' ist größer als die zulässige Höhe von '<AllowedHeight>'.",
     "Error"                      : "Fehler!",
     "Information"                : "Information!",
     "Install"                    : "installieren",
     "Uninstall"                  : "entfernen",
     "FolderCreated"              : "Der Ordner '<FolderName>' wurde erstellt.",
     "ErrorCreatingFolder"        : "Beim Erstellen des Ordners '<FolderName>' ist etwas schief gelaufen.",
     "FolderAlreadyExists"        : "Der Ordner '<FolderName>' existiert bereits.",
     "Details"                    : "Einzelheiten:",
     "SourceFile"                 : "Quelldatei:",
     "FontName"                   : "Schriftartenname:",
     "FontNameRegistry"           : "Schriftartname für die Registrierung:",              
     "ErrorCopyFont"              : "Beim Kopieren von '<FullFileName>' nach '<NewFontFile>' ist ein Fehler aufgetreten: <ErrorMessage>.",
     "ErrorInstallingFont"        : "    Ergebnis: Beim Installieren der Schriftart '<FontName>' (Datei '<NewFontFile>') ist ein Fehler aufgetreten.",
     "SuccessInstallingFont"      : "    Ergebnis: Die Schriftart '<FontName>' (Datei '<NewFontFile>') wurde erfolgreich installiert.",
     "ErrorUpdatingRegistry"      : "    Ergebnis: Beim Aktualisieren der Registrierung ist ein Fehler aufgetreten: <ErrorMessage>",
     "SuccessUpdatingRegistry"    : "    Ergebnis: Die Registrierung wurde aktualisiert.",
     "FileAlreadyExists"          : "Die Datei '<NewFontFile>' existiert bereits. Es wird also nichts gemacht.",
     "ErrorFontNotUninstalled"    : "Fehler: Die Schriftart '<FolderName>' (Datei '<NewFontFile>') wurde nicht deinstalliert.",
     "RetryInstallFont"           : "    Ich werde versuchen, die Schriftart erneut zu installieren und zu deinstallieren.",
     "RetrySuccess"               : "    Ergebnis: Die Schriftart '<FontName>' (Datei '<NewFontFile>') wurde deinstalliert.",
     "RetryFailure"               : "    Immer noch nicht erfolgreich mit der Deinstallation von '<FontName>' (Datei '<New Font File>').",
     "RetryFailureAgain"          : "    Immer noch kein Glück mit der erneuten Installation der Schriftart '<FontName>' (Datei '<NewFontFile>').",
     "FileNotFound"               : "    Fehler: Die Datei '<NewFontFile>' existiert nicht.",
     "ProgressbarText"            : "Verarbeitungsdatei: '<File>' zum <ActionToDo>.",
     "ProgressbarCompleted"       : "Der Vorgang ist abgeschlossen:",
     "ProgressbarSuccess"         : "  - <numSuccess> erfolgreich",
     "ProgressbarFailures"        : "  - <numFailures> Fehler",
     "SpecifyFontsFolder"         : "Geben Sie den Ordner mit den zu installierenden Schriftarten an",
     "ReturnedFontsFolder"        : "Der Benutzer hat den Ordner '<FolderName>' ausgewählt.",
     "frmFonts"                   : "Schriftarten hinzufügen oder entfernen (Add or Remove Fonts)",
     "chkRecurse"                 : "Inklusive Unterordner",
     "btnExit"                    : "&Ausgang",
     "chkMachineWide"             : "Installieren Sie die Schriftarten für alle Benutzer.",
     "chkAdmin"                   : "Der Benutzer hat erhöhte (Administrator-)Rechte.",
     "grpFolder"                  : "Aus Ordner installieren",
     "chkAllItems"                : "Alle Elemente auswählen oder abwählen",
     "btnUninstallSelectedFonts"  : "Ausgewählte Schriftarten &deinstallieren",
     "btnInstallFonts"            : "Ausgewählte Schriftarten &installieren",
     "btnGetFolderContent"        : "&Ordnerinhalt abrufen",
     "txtMessage"                 : "Lesen der Schriftarten in '<FolderName>'."
 },

"nl" :
 {
     "Error02Text"                : "Powershell ConstrainedMode blokkeert dit script.",
     "Error04Text"                : "Fout: de map '<FolderName>' bestaat niet.",
     "Error08Text"                : "Je hebt verhoogde (admin) rechten nodig om deze fonts per machine te installeren.",
     "Error16Text"                : "De schermbreedte van '<DetectedWidth>' is groter dan de toegestane breedte van '<AllowedWidth>'.",
     "Error32Text"                : "De schermhoogte van '<DetectedHeight>' is groter dan de toegestane hoogte van '<AllowedHeight>'.",
     "Error"                      : "Fout!",
     "Information"                : "Informatie",
     "Install"                    : "voor installatie",
     "Uninstall"                  : "om te verwijderen",
     "FolderCreated"              : "De map '<FolderName>' is aangemaakt.",
     "ErrorCreatingFolder"        : "Er ging iets fout bij het aanmaken van de map '<FolderName>'.",
     "FolderAlreadyExists"        : "De map '<FolderName>' bestaat al.",
     "Details"                    : "Details:",
     "SourceFile"                 : "Oorspronkelijk bestand:",
     "FontName"                   : "Font naam:",
     "FontNameRegistry"           : "Font naam voor het register:",              
     "ErrorCopyFont"              : "Er was een fout bij het kopiëren van '<FullFileName>' naar 'NewFontFile': <ErrorMessage>.",
     "ErrorInstallingFont"        : "    Resultaat: er was een fout bij het installeren van font '<FontName>' (Bestand '<NewFontFile>').",
     "SuccessInstallingFont"      : "    Resultaat: font '<FontName>' (bestand '<NewFontFile>') is succesvol geïnstalleerd.",
     "ErrorUpdatingRegistry"      : "    Resultaat: er was een fout bij het bijwerken van het register: <ErrorMessage>",
     "SuccessUpdatingRegistry"    : "    Resultaat: het register is bijgewerkt.",
     "FileAlreadyExists"          : "Het bestand '<NewFontFile>' bestaat al. Er is niets uitgevoerd.",
     "ErrorFontNotUninstalled"    : "Fout: font '<FolderName>' (bestand '<NewFontFile>') is niet verwijderd.",
     "RetryInstallFont"           : "    Font wordt opnieuw geïnstalleerd en weer verwijderd.",
     "RetrySuccess"               : "    Resultaat: het font '<FontName>' (bestand '<NewFontFile>') is verwijderd.",
     "RetryFailure"               : "    Font '<FontName>' (bestand '<NewFontFile>') is nog niet verwijderd.",
     "RetryFailureAgain"          : "    Nog steeds geen succes bij het opnieuw herinstalleren van font '<FontName>' (bestand '<NewFontFile>').",
     "FileNotFound"               : "    Fout: het bestand '<NewFontFile>' bestaat niet.",
     "ProgressbarText"            : "Verwerken bestand: '<File>' <ActionToDo>.",
     "ProgressbarCompleted"       : "De vewerking is uitgevoerd:",
     "ProgressbarSuccess"         : "  - <numSuccess> succesvol",
     "ProgressbarFailures"        : "  - <numFailures> fouten",
     "SpecifyFontsFolder"         : "Geef aan in welke map de te installeren fonts staan",
     "ReturnedFontsFolder"        : "Gekozen map '<FolderName>'.",
     "frmFonts"                   : "Toevoegen of verwijderen van fonts (Add or Remove Fonts)",
     "chkRecurse"                 : "Inclusief onderliggende mappen",
     "btnExit"                    : "&Verlaat",
     "chkMachineWide"             : "Installeer de fonts voor alle gebruikers.",
     "chkAdmin"                   : "De gebruiker heeft verhoogde (beheer) rechten.",
     "grpFolder"                  : "Installeer van de map",
     "chkAllItems"                : "Selecteer of deselecteer alle items",
     "btnUninstallSelectedFonts"  : "V&erwijder de geselecteerde fonts",
     "btnInstallFonts"            : "&Installeer de geselecteerde fonts",
     "btnGetFolderContent"        : "&Lees de inhoud van de map",
     "txtMessage"                 : "Uitlezen map voor fonts in '<FolderName>'."
 },

"fr" :
 {
     "Error02Text"                : "Blocs Powershell ConstrainedMode exécutant ce script.",
     "Error04Text"                : "Erreur : le dossier '<FolderName>' n'existe pas.",
     "Error08Text"                : "Vous avez besoin de droits élevés (administrateur) pour installer les polices par machine.",
     "Error16Text"                : "La largeur d'écran de '<DetectedWidth>' est supérieure à la largeur autorisée de '<AllowedWidth>'.",
     "Error32Text"                : "La hauteur d'écran de '<DetectedHeight>' est supérieure à la hauteur autorisée de '<AllowedHeight>'.",
     "Error"                      : "Erreur!",
     "Information"                : "Information!",
     "Install"                    : "installer",
     "Uninstall"                  : "uninstaller",
     "FolderCreated"              : "Le dossier '<FolderName>' a été créé.",
     "ErrorCreatingFolder"        : "Une erreur s'est produite lors de la création du dossier '<FolderName>'.",
     "FolderAlreadyExists"        : "Le dossier '<FolderName>' existe déjà.",
     "Details"                    : "Des détails:",
     "SourceFile"                 : "Fichier source:",
     "FontName"                   : "Nom de la police :",
     "FontNameRegistry"           : "Nom de police pour le registre :",              
     "ErrorCopyFont"              : "Une erreur s'est produite lors de la copie de '<FullFileName>' vers '<NewFontFile>' : <ErrorMessage>.",
     "ErrorInstallingFont"        : "    Résultat : une erreur s'est produite lors de l'installation de la police '<FontName>' (Fichier '<NewFontFile>').",
     "SuccessInstallingFont"      : "    Résultat : la police '<FontName>' (Fichier '<NewFontFile>') a été installée avec succès.",
     "ErrorUpdatingRegistry"      : "    Résultat : une erreur s'est produite lors de la mise à jour du registre : <ErrorMessage>",
     "SuccessUpdatingRegistry"    : "    Résultat : le registre a été mis à jour.",
     "FileAlreadyExists"          : "Le fichier '<NewFontFile>' existe déjà. Donc rien n'est fait.",
     "ErrorFontNotUninstalled"    : "Erreur : la police '<FolderName>' (Fichier '<NewFontFile>') n'a pas été désinstallée.",
     "RetryInstallFont"           : "    Je vais réessayer d'installer et de désinstaller la police.",
     "RetrySuccess"               : "    Résultat : la police '<FontName>' (Fichier '<NewFontFile>') a été désinstallée.",
     "RetryFailure"               : "    Toujours pas de succès avec la désinstallation de '<FontName>' (Fichier '<New Font File>').",
     "RetryFailureAgain"          : "    Toujours pas de chance d'installer à nouveau la police '<FontName>' (Fichier '<NewFontFile>').",
     "FileNotFound"               : "    Erreur : Le fichier '<NewFontFile>' n'existe pas.",
     "ProgressbarText"            : "Fichier de traitement : '<File>' pour <ActionToDo>.",
     "ProgressbarCompleted"       : "L'opération est terminée:",
     "ProgressbarSuccess"         : "  - <numSuccess> réussi",
     "ProgressbarFailures"        : "  - <numFailures> les échecs",
     "SpecifyFontsFolder"         : "Spécifiez le dossier avec les polices à installer",
     "ReturnedFontsFolder"        : "L'utilisateur a choisi le dossier '<FolderName>'.",
     "frmFonts"                   : "Ajouter ou Supprimer des polices (Add or Remove Fonts)",
     "chkRecurse"                 : "Inclure les sous-dossiers",
     "btnExit"                    : "&Sortie",
     "chkMachineWide"             : "Installez les polices pour tous les utilisateurs.",
     "chkAdmin"                   : "L'utilisateur a des droits (admin) élevés.",
     "grpFolder"                  : "Installer à partir du dossier",
     "chkAllItems"                : "Sélectionner ou désélectionner tous les éléments",
     "btnUninstallSelectedFonts"  : "&Désinstaller les polices sélectionnées",
     "btnInstallFonts"            : "&Installer les polices sélectionnées",
     "btnGetFolderContent"        : "&Obtenir le contenu du dossier", 
     "txtMessage"                 : "Lire les polices dans '<FolderName>'."
 },

"es" :
 {
     "Error02Text"                : "Powershell ConstrainedMode bloquea la ejecución de este script.",
     "Error04Text"                : "Error: la carpeta '<FolderName>' no existe.",
     "Error08Text"                : "Necesita derechos elevados (administrador) para instalar las fuentes por máquina.",
     "Error16Text"                : "El ancho de pantalla de '<DetectedWidth>' es mayor que el ancho permitido de '<AllowedWidth>'.",
     "Error32Text"                : "La altura de la pantalla de '<DetectedHeight>' es mayor que la altura permitida de '<AllowedHeight>'.",
     "Error"                      : "Error!",
     "Information"                : "Información!",
     "Install"                    : "Instalar en pc",
     "Uninstall"                  : "desinstalar",
     "FolderCreated"              : "Se ha creado la carpeta '<FolderName>'.",
     "ErrorCreatingFolder"        : "Algo salió mal al crear la carpeta '<FolderName>'.",
     "FolderAlreadyExists"        : "La carpeta '<FolderName>' ya existe.",
     "Details"                    : "Detalles:",
     "SourceFile"                 : "Archivo fuente:",
     "FontName"                   : "Nombre de la fuente:",
     "FontNameRegistry"           : "Nombre de fuente para el registro:",              
     "ErrorCopyFont"              : "Hubo un error al copiar '<FullFileName>' a 'NewFontFile': <ErrorMessage>.",
     "ErrorInstallingFont"        : "    Resultado: hubo un error al instalar la fuente '<FontName>' (Archivo '<NewFontFile>').",
     "SuccessInstallingFont"      : "    Resultado: la fuente '<FontName>' (Archivo '<NewFontFile>') se instaló correctamente.",
     "ErrorUpdatingRegistry"      : "    Resultado: hubo un error al actualizar el registro: <ErrorMessage>",
     "SuccessUpdatingRegistry"    : "    Resultado: el registro se ha actualizado.",
     "FileAlreadyExists"          : "El archivo '<NewFontFile>' ya existe. Así que no se hace nada.",
     "ErrorFontNotUninstalled"    : "Error: la fuente '<FolderName>' (Archivo '<NewFontFile>') no se ha desinstalado.",
     "RetryInstallFont"           : "    Intente instalar y desinstalar la fuente nuevamente.",
     "RetrySuccess"               : "    Resultado: la fuente '<FontName>' (Archivo '<NewFontFile>') se ha desinstalado.",
     "RetryFailure"               : "    Todavía no se ha logrado desinstalar '<Nombre de fuente>' (Archivo '<Nuevo archivo de fuente>').",
     "RetryFailureAgain"          : "    Todavía no tuve suerte con la instalación de la fuente '<FontName>' (Archivo '<NewFontFile>') nuevamente.",
     "FileNotFound"               : "    Error: El archivo '<NewFontFile>' no existe.",
     "ProgressbarText"            : "Archivo de procesamiento: '<Archivo>' para <ActionToDo>.",
     "ProgressbarCompleted"       : "La operación se ha completado:",
     "ProgressbarSuccess"         : "  - <numSuccess> Exitosa",
     "ProgressbarFailures"        : "  - <numFailures> fracasos",
     "SpecifyFontsFolder"         : "Especifique la carpeta con las fuentes para instalar",
     "ReturnedFontsFolder"        : "La usuario ha elegido la carpeta '<FolderName>'.",
     "frmFonts"                   : "Agregar o quitar Fuentes (Add or Remove Fonts)",
     "chkRecurse"                 : "Incluir subcarpétas",
     "btnExit"                    : "&Salida",
     "chkMachineWide"             : "Instalar las fuentes para todas las usuarias.",
     "chkAdmin"                   : "El usuario tiene derechos elevados (administrador).",
     "grpFolder"                  : "Instalar desde carpeta",
     "chkAllItems"                : "Seleccionar o deseleccionar todos los elementos",
     "btnUninstallSelectedFonts"  : "&Desinstalar fuentes seleccionadas",
     "btnInstallFonts"            : "&Instalar fuentes seleccionadas",
     "btnGetFolderContent"        : "&Obtener el contenido de la carpeta",
     "txtMessage"                 : "Lectura de las fuentes en '<FolderName>'."
 },

"it" :
 {
     "Error02Text"                : "Powershell ConstrainedMode blocca l'esecuzione di questo script.",
     "Error04Text"                : "Errore: la cartella '<FolderName>' non esiste.",
     "Error08Text"                : "Sono necessari diritti elevati (amministratore) per installare i caratteri per macchina.",
     "Error16Text"                : "La larghezza dello schermo di '<DetectedWidth>' è maggiore della larghezza consentita di '<AllowedWidth>.'",
     "Error32Text"                : "The screenheight of '<DetectedHeight>' is greater than the allowed height of '<AllowedHeight>.'",
     "Error"                      : "Errore!",
     "Information"                : "Informazione!",
     "Install"                    : "installare",
     "Uninstall"                  : "disinstallare",
     "FolderCreated"              : "La cartella '<FolderName>' è stata creata.",
     "ErrorCreatingFolder"        : "Qualcosa è andato storto durante la creazione della cartella '<FolderName>'.",
     "FolderAlreadyExists"        : "La cartella '<FolderName>' esiste già.",
     "Details"                    : "Dettagli:",
     "SourceFile"                 : "File sorgente:",
     "FontName"                   : "Nome carattere:",
     "FontNameRegistry"           : "Nome del carattere per il registro:",              
     "ErrorCopyFont"              : "Si è verificato un errore durante la copia di '<FullFileName>' in '<NewFontFile>': <ErrorMessage>.",
     "ErrorInstallingFont"        : "    Risultato: si è verificato un errore durante l'installazione del font '<FontName>' (File '<NewFontFile>').",
     "SuccessInstallingFont"      : "    Risultato: il carattere '<FontName>' (File '<NewFontFile>') è stato installato correttamente.",
     "ErrorUpdatingRegistry"      : "    Risultato: si è verificato un errore durante l'aggiornamento del registro: <ErrorMessage>",
     "SuccessUpdatingRegistry"    : "    Risultato: il registro è stato aggiornato.",
     "FileAlreadyExists"          : "Il file '<NewFontFile>' esiste già. Quindi non si fa nulla.",
     "ErrorFontNotUninstalled"    : "Errore: il carattere '<NomeCartella>' (File '<NewFontFile>') non è stato disinstallato.",
     "RetryInstallFont"           : "    Prova a installare e disinstallare nuovamente il carattere.",
     "RetrySuccess"               : "    Risultato: il carattere '<FontName>' (File '<NewFontFile>') è stato disinstallato.",
     "RetryFailure"               : "    Ancora non riuscita con la disinstallazione di '<FontName>' (File '<NewFontFile>').",
     "RetryFailureAgain"          : "    Ancora nessuna fortuna con l'installazione di nuovo del carattere '<FontName>' (File '<NewFontFile>').",
     "FileNotFound"               : "    Errore: il file '<NewFontFile>' non esiste.",
     "ProgressbarText"            : "File di elaborazione: '<File>' per <ActionToDo>.",
     "ProgressbarCompleted"       : "L'operazione è stata completata:",
     "ProgressbarSuccess"         : "  - <numSuccess> riuscito",
     "ProgressbarFailures"        : "  - <numFailures> fallimenti",
     "SpecifyFontsFolder"         : "Specificare la cartella con i caratteri da installare",
     "ReturnedFontsFolder"        : "L'utente ha scelto la cartella '<FolderName>'.",
     "frmFonts"                   : "Aggiungi o rimuovi caratteri (Add or Remove Fonts)",
     "chkRecurse"                 : "Includere sottocartelle",
     "btnExit"                    : "&Uscita",
     "chkMachineWide"             : "Installa i caratteri per tutti gli utenti.",
     "chkAdmin"                   : "L'utente ha diritti (admin) elevati.",
     "grpFolder"                  : "Installa dalla cartella",
     "chkAllItems"                : "Seleziona o deseleziona tutti gli elementi",
     "btnUninstallSelectedFonts"  : "&Disinstalla i caratteri selezionati",
     "btnInstallFonts"            : "&Installa i caratteri selezionati",
     "btnGetFolderContent"        : "&Ottieni il contenuto della cartella",
     "txtMessage"                 : "Leggere i caratteri in '<FolderName>'."
 },

"id" :
 {
     "Error02Text"                : "Blok Powershell ConstrainedMode menjalankan skrip ini.",
     "Error04Text"                : "Kesalahan: folder '<FolderName>' tidak ada.",
     "Error08Text"                : "Anda memerlukan hak (admin) yang lebih tinggi untuk menginstal font per mesin.",
     "Error16Text"                : "Lebar layar '<DetectedWidth>' lebih besar dari lebar yang diizinkan '<AllowedWidth>'.",
     "Error32Text"                : "Tinggi layar '<DetectedHeight>' lebih besar dari tinggi yang diizinkan '<AllowedHeight>'.",
     "Error"                      : "Kesalahan!",
     "Information"                : "Informasi!",
     "Install"                    : "install",
     "Uninstall"                  : "hapus instalan",
     "FolderCreated"              : "Folder '<FolderName>' telah dibuat.",
     "ErrorCreatingFolder"        : "Terjadi kesalahan saat membuat folder '<FolderName>'.",
     "FolderAlreadyExists"        : "Folder '<FolderName>' sudah ada.",
     "Details"                    : "Detail:",
     "SourceFile"                 : "Sumber data:",
     "FontName"                   : "Nama font:",
     "FontNameRegistry"           : "Nama font untuk registri:",              
     "ErrorCopyFont"              : "Terjadi kesalahan saat menyalin '<FullFileName>' ke 'NewFontFile': <ErrorMessage>.",
     "ErrorInstallingFont"        : "    Hasil: terjadi kesalahan saat memasang font '<FontName>' (File '<NewFontFile>').",
     "SuccessInstallingFont"      : "    Hasil: font '<FontName>' (File '<NewFontFile>') telah berhasil diinstal.",
     "ErrorUpdatingRegistry"      : "    Hasil: ada kesalahan saat memperbarui registri: <ErrorMessage>",
     "SuccessUpdatingRegistry"    : "    Hasil: registri telah diperbarui.",
     "FileAlreadyExists"          : "Berkas '<NewFontFile>' sudah ada. Jadi tidak ada yang dilakukan.",
     "ErrorFontNotUninstalled"    : "Kesalahan: font '<FolderName>' (File '<NewFontFile>') belum dihapus.",
     "RetryInstallFont"           : "    Coba instal dan hapus instalan font lagi.",
     "RetrySuccess"               : "    Hasil: font '<FontName>' (File '<NewFontFile>') telah dihapus.",
     "RetryFailure"               : "    Masih belum berhasil menghapus instalan '<FontName>' (File '<File Font Baru>').",
     "RetryFailureAgain"          : "    Masih belum berhasil menginstal font '<FontName>' (File '<NewFontFile>') lagi.",
     "FileNotFound"               : "    Kesalahan: file '<FolderName>' tidak ada.",
     "ProgressbarText"            : "Memproses file: '<File>' untuk <ActionToDo>.",
     "ProgressbarCompleted"       : "Operasi telah selesai:",
     "ProgressbarSuccess"         : "  - <numSuccess> berhasil",
     "ProgressbarFailures"        : "  - <numFailures> kegagalan",
     "SpecifyFontsFolder"         : "Tentukan folder dengan font yang akan diinstal",
     "ReturnedFontsFolder"        : "Pengguna telah memilih folder '<Nama Folder>'.",
     "frmFonts"                   : "Tambah atau Hapus Font (Add or Remove Fonts)",
     "chkRecurse"                 : "Sertakan sub folder",
     "btnExit"                    : "&Keluar",
     "chkMachineWide"             : "Instal font untuk semua pengguna.",
     "chkAdmin"                   : "Pengguna memiliki hak tinggi (admin).",
     "grpFolder"                  : "Instal dari folder",
     "chkAllItems"                : "Pilih atau batalkan pilihan semua item",
     "btnUninstallSelectedFonts"  : "&Copot pemasangan font yang dipilih",
     "btnInstallFonts"            : "&Instal font yang dipilih",
     "btnGetFolderContent"        : "&Dapatkan konten folder",
     "txtMessage"                 : "Membaca font di '<FolderName>'."
 }
}
And the PowerShell script InstallorUninstallFonts_v02.ps1:
<#
.SYNOPSIS
    Installs or uninstalls fonts. By default the fonts are installed in the users' font directory. That can be overruled.
    If the fonts are installed machine wide than the uninstall should also be machine wide. The same for per user font
    installs.

.DESCRIPTION
    This script installs or uninstalls fonts

.EXAMPLE
     Start the application and show the GUI.
     ."InstallorUninstallFonts_v02.ps1" 

.EXAMPLE
     Start the application, show the GUI and set the folder \\server\share as the folder with the fonts.
     ."InstallorUninstallFonts_v02.ps1" -FolderWithFonts \\server\share 

.EXAMPLE
     Start the application, show the GUI and set the folder \\server\share as the folder with the fonts. Include subfolders
     ."InstallorUninstallFonts_v02.ps1" -FolderWithFonts \\server\share -IncludeSubFolders

.EXAMPLE
     Start the application, show the GUI and set the folder \\server\share as the folder with the fonts. Force the Italian GUI language.
     ."InstallorUninstallFonts_v02.ps1" -FolderWithFonts \\server\share -LanguageOverride it
     The following languages are available:
       - en -> English
       - de -> German
       - fr -> French
       - nl -> Dutch
       - it -> Italian
       - sp -> Spanish
       - id -> Indonesian

.EXAMPLE
     Start the application and install all the fonts that are in \\server\share silently (without a GUI).
     ."InstallorUninstallFonts_v02.ps1" -Silent -ActionToPerform Install -FolderWithFonts \\server\share  

.EXAMPLE
     Start the application with verbose logging and uninstall all the fonts that are in \\server\share silently (without a GUI).
     ."InstallorUninstallFonts_v02.ps1" -Silent -ActionToPerform Uninstall -FolderWithFonts \\server\share -Verbose

.EXAMPLE
     Start the application and install all the fonts machine wide that are in \\server\share silently (without a GUI). 
     ."InstallorUninstallFonts_v02.ps1" -Silent -ActionToPerform Install -FolderWithFonts \\server\share -ForcePerMachine

.NOTES
    Author:  Willem-Jan Vroom
    Website: 
    Twitter: @TheStingPilot

v0.1:
   * Initial version. 

v0.2:
   * added Silent option
   * removed LogPath option
   * Forms
   * User interface language is detected and shown in either English, German, Dutch, French, Spanish, Italian or Indonesian.

Resources:
 - https://github.com/microsoft/navcontainerhelper/blob/master/addfonts.ps1
 - https://silentinstallhq.com/windows-font-silent-uninstall-powershell/

#>

[CmdletBinding()]

Param
  (
   [Parameter(HelpMessage='Silent, no GUI and no prompts. Only a progressbar')]
   [Parameter(Mandatory=$True, ParameterSetName='Silent')]
   [Switch]   $Silent,  
   
   [Parameter(HelpMessage='Enable detailed logging to a log file.')]
   [Parameter(Mandatory=$False, ParameterSetName='NotSilent')]
   [Parameter(Mandatory=$False, ParameterSetName='Silent')]
   [Switch]   $DetailedLogging,

   [Parameter(HelpMessage = 'Override the language.')]
   [Parameter(Mandatory = $False, ParameterSetName = 'NotSilent')]
   [Parameter(Mandatory = $False, ParameterSetName = 'Silent')]
   [ValidateSet('en','de',"nl","fr","es","it","id")]
   [String]   $LanguageOverride,

   [Parameter(HelpMessage='Action to perform, could be install or uninstall.')]
   [Parameter(Mandatory=$True, ParameterSetName='Silent')]
   [ValidateNotNullOrEmpty()]
   [ValidateSet('Install','Uninstall')]
   [String]   $ActionToPerform,

   [Parameter(HelpMessage='Specify the folder name where the font files are located to be installed or uninstalled.')]
   [Parameter(Mandatory=$False, ParameterSetName='NotSilent')]
   [Parameter(Mandatory=$True, ParameterSetName='Silent')]
   [ValidateNotNullOrEmpty()]
   [String]   $FolderWithFonts,

   [Parameter(HelpMessage='Force a per machine install or uninstall.')]
   [Parameter(Mandatory=$False, ParameterSetName='NotSilent')]
   [Parameter(Mandatory=$False, ParameterSetName='Silent')]
   [Switch]   $ForcePerMachine,

   [Parameter(HelpMessage='Include sub folders.')]
   [Parameter(Mandatory=$False, ParameterSetName='NotSilent')]
   [Parameter(Mandatory=$False, ParameterSetName='Silent')]
   [Switch]   $IncludeSubFolders
  )

# =============================================================================================================================================
# Function block
# =============================================================================================================================================

  Function Display-MessageBox
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       24-May-2021 / 24-Apr-2024
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Display-MessageBox
    =============================================================================================================================================
    .SYNOPSIS

    This function displays a message box.

    #>

    param
     (
      [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()][String] $Text,
      [Parameter(Mandatory=$False)][Switch] $GUI,
      [Parameter(Mandatory=$False)][Switch] $Error
     )
         
    if($GUI)
     {
      if($Error)
       {
        [void][System.Windows.MessageBox]::Show($Text.Trim(),$($FontActions.$Language.Error),"OK","Error")
       }
        else
       {
        [void][System.Windows.MessageBox]::Show($Text.Trim(),$($FontActions.$Language.Information),"OK","Asterisk")
       }
     }
      else
     {
      Write-Host "$Text`n"
     }
   }


  Function Add-EntryToLogFile
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-May-2020 / Modified 09-May-2022: Includes the function name
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Add-EntryToLogFile
    =============================================================================================================================================
    .SYNOPSIS

    This function adds a line to a log file

    #>

    Param
     (
      [Parameter(Mandatory=$True)]  [string] $Entry,
      [Parameter(Mandatory=$False)] [String] $FunctionName

     )
      
     Write-Verbose "[Function: $FunctionName] - $Entry"
     if($Global:gblDetailedLogging -and $Global:gblLogFile)
      {
       $Timestamp = (Get-Date -UFormat "%a %e %b %Y %X").ToString()
       Add-Content $Global:gblLogFile -Value $($Timestamp + "[Function: $FunctionName] - $Entry") -Force -ErrorAction SilentlyContinue
      }
   }

  Function Create-Folder
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       03-August-2018
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Create-Folder
    =============================================================================================================================================
    .SYNOPSIS

    This function creates the given folder.
    The function returns two results:
     1. A message.
     2. True or false (success or failure)

    #>
    
    param
     (
      [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()][String] $FolderName
     )

   $bolResult     = $True
   $ResultMessage = ""

   if(-not(Test-Path $('FileSystem::' + $FolderName)))
    {
     New-Item -Path $FolderName -ItemType Directory | Out-Null
     Sleep 1
     if(test-path $('FileSystem::' + $FolderName))
      {
       $ResultMessage = $($FontActions.$Language.FolderCreated).Replace("<FolderName>", $FolderName)
      }
       else
      {
       $ResultMessage = $($FontActions.$Language.ErrorCreatingFolder).Replace("<FolderName>", $FolderName)
       $bolResult     = $False
      }
    }
     else
    {
     $ResultMessage = $($FontActions.$Language.FolderAlreadyExists).Replace("<FolderName>", $FolderName)
    }

    Return $ResultMessage,$bolResult 

   }

  Function Check-HasElevatedRights
   {

    <#
    .NOTES
    ========================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       11-January-2019 / Modified 22-Apr-2022 / Modified 05-May-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Check-HasElevatedRights
    ========================================================================================================================
    .SYNOPSIS

    This function checks if an user has admin rights. The function returns $True or $False

    Initially, the function was ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]'544')
    But that command throws up an error due to the PowerShell ConstrainedMode.
    "S-1-5-32-544" -in (whoami /groups /FO csv | convertfrom-csv).sid does not work either as it will always return 'true', even if not elevated but
    the logged on user is an admin
    So the only good test: check if you can create a file in c:\windows\fonts. If yes, then admin / elevated rights otherwise not. 

    #>

    $RandomFile = ""
    For($a=1; $a -le 8; $a++)
     {
      $RandomFile += [char](Random(97..122))
     }

    $RandomFile = "$($env:windir)\Fonts\$RandomFile.txt"

    Try
     {
      New-Item $RandomFile -Force -ErrorAction SilentlyContinue | Out-Null
      if(test-path($RandomFile))
       {
        Remove-Item $RandomFile -Force
        Return $True
       }
        else
       {
        Return $False
       }
     }
      Catch
     {  
      Return $False
     } 
   }

  Function Read-Font
   {
    <#
    .NOTES
    ========================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       19-April-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Read-Font
    ========================================================================================================================
    .SYNOPSIS

    This function reads the font name from a given file
    #>

    [CmdletBinding()]

    Param
     (
      [Parameter(Mandatory=$True)][ValidateNotNullOrEmpty()][System.IO.FileInfo] $FileNameFromFont
     )

     $FontFile       = Split-Path $FileNameFromFont -Leaf
     $Extension      = (Get-Item -Path $FileNameFromFont).Extension
     $BaseName       = $FontFile -replace($Extension,"")
     
     $objShell       = New-Object -ComObject Shell.Application
     $Folder         = $objShell.namespace($FileNameFromFont.DirectoryName)  
     $Item           = $Folder.Items().Item($FileNameFromFont.Name)  
     $FontName       = $Folder.GetDetailsOf($Item, 21)

     if($FontName -eq "")
      {
       $FontName = $BaseName
      }  

     Return $FontName
     
   }

  Function Test-RegistryKeyValue
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       30-Dec-20
    Created by:       (C) Aaron Jensen 
                      https://stackoverflow.com/questions/5648931/test-if-registry-value-exists
    Organization:     Carbon Module
    Functionname:     Test-RegistryKeyValue
    =============================================================================================================================================
    .SYNOPSIS

    #>

    [CmdletBinding()]
    param 
     (
      [Parameter(Mandatory = $True)][string]  $Path,
      [Parameter(Mandatory = $True)][string]  $Name
     )

    if (-not (Test-Path -Path $Path -PathType Container))
     {
      return $False
     }

    $properties = Get-ItemProperty -Path $Path
    if (-not $properties)
     {
      return $False
     } 

    $member = Get-Member -InputObject $properties -Name $Name
    if ($member)
     {
      return $True
     }
      else
     {
      return $False
     }
   }

  Function Set-Font
   {

    <#
    .NOTES
    ========================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       11-January-2019
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Set-Font
    ========================================================================================================================
    .SYNOPSIS

    This function installs or uninstalls a font

    #>
    
    [CmdletBinding()]
      
    Param
     (
      [Parameter(Mandatory=$True)][ValidateSet('Install','Uninstall')]  [String]             $Action,
      [Parameter(Mandatory=$True)][ValidateNotNullOrEmpty()]            [System.IO.FileInfo] $FullFileName,
      [Parameter(Mandatory=$False)]                                     [Switch]             $PerMachine
     )

     # =============================================================================================================================================
     # Preperations
     # =============================================================================================================================================
     
     $ThisFunctionName = $PSCmdlet.MyInvocation.MyCommand.Name

     if($PerMachine)
      {
       $FontRegKey     = "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Fonts"
       $FontsFolder    = "$($Env:WINDIR)\Fonts"
      }
       else
      {
       $FontRegKey     = "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Fonts"
       $FontsFolder    = "$($Env:LOCALAPPDATA)\Microsoft\Windows\Fonts"
      }

     if(-not(Test-Path($FontsFolder)))
      {
       Create-Folder -FolderName $FontsFolder 
      }

     $FontFile       = Split-Path $FullFileName -Leaf
     $Extension      = (Get-Item -Path $FullFileName).Extension
     $NewFontFile    = Join-Path $FontsFolder $FontFile 
     $FontName       = Read-Font -FileNameFromFont $FullFileName 

     $hashFontFileTypes = @{}
     $hashFontFileTypes.Add(".fon", "")
     $hashFontFileTypes.Add(".fnt", "")
     $hashFontFileTypes.Add(".ttf", " (TrueType)")
     $hashFontFileTypes.Add(".ttc", " (TrueType)")
     $hashFontFileTypes.Add(".otf", " (OpenType)")

     $FontNameForRegistry = "$($FontName)$($hashFontFileTypes.item($Extension))"

     Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.Details)  
     Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry " -> $($FontActions.$Language.SourceFile)               $FullFileName"  
     Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry " -> $($FontActions.$Language.FontName)                 $FontName"  
     Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry " -> $($FontActions.$Language.FontNameRegistry)    $FontNameForRegistry"  
    
     if($Action -eq "Install")
      {
       if(-not(Test-Path($NewFontFile)))
        {
         Try
          {
           Copy-Item -Path $FullFileName -Destination $NewFontFile -ErrorAction SilentlyContinue  
          }
           Catch
          {
           Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.ErrorCopyFont).Replace("<FullFileName>", $FullFileName).Replace("<NewFontFile>",$NewFontFile).Replace("<ErrorMessage>",$($_.Exception.Message))  
           Return $False
          }
         $InstalledNumberOfFonts = [FontResource.AddRemoveFonts]::AddFont($NewFontFile)
         if($InstalledNumberOfFonts -eq 0)
          {
           Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.ErrorInstallingFont).Replace("<FontName>", $FontName).Replace("<NewFontFile>", $NewFontFile)  
           if(Test-Path($NewFontFile))
            {
             Remove-Item -Path $NewFontFile -Force
            }
           Return $False
          }
           else
          {
           Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.SuccessInstallingFont).Replace("<FontName>", $FontName).Replace("<NewFontFile>", $NewFontFile)  
           Try
            {
             New-ItemProperty -Path $FontRegKey -Name $FontNameForRegistry -Value $FontFile -PropertyType String -ErrorAction SilentlyContinue | Out-Null
             Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.SuccessUpdatingRegistry)  
            }
             Catch
            {
             Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.ErrorUpdatingRegistry).Replace("<ErrorMessage>",$($_.Exception.Message))  
             Return $False
            }

          }
        }
         else
        {
         Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.FileAlreadyExists).Replace("<NewFontFile>",$NewFontFile)  
         Return $False
        }
      }
       else
      {
       if(test-path($NewFontFile))
        {
         $RemovedNumberOfFonts = [FontResource.AddRemoveFonts]::RemoveFont($NewFontFile)
         if($RemovedNumberOfFonts -eq 0)
          {
           Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.ErrorFontNotUninstalled).Replace("<FontName>",$FontName).Replace("<NewFontFile>",$NewFontFile)  
           Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.RetryInstallFont)  
           $InstalledNumberOfFonts = [FontResource.AddRemoveFonts]::AddFont($NewFontFile)
           if($InstalledNumberOfFonts -gt 0)
            {
             $RemovedNumberOfFonts = [FontResource.AddRemoveFonts]::RemoveFont($NewFontFile)
             If($RemovedNumberOfFonts -gt 0)
              {
               Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.RetrySuccess).Replace("<FontName>",$FontName).Replace("<NewFontFile>",$NewFontFile)  
               Remove-Item -Path $NewFontFile -Force
               If(Test-RegistryKeyValue -Path $FontRegKey -Name $FontNameForRegistry)
                {
                 Remove-ItemProperty -Path $FontRegKey -Name $FontNameForRegistry | Out-Null
                }
               Return $True
              }
               else
              {
               Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.RetryFailure).Replace("<FontName>",$FontName).Replace("<NewFontFile>",$NewFontFile)
               Return $False
              }            
            }
             else
            {
             Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.RetryFailureAgain).Replace("<FontName>",$FontName).Replace("<NewFontFile>",$NewFontFile)
             Return $False
            }
           Return $False
          }
           else
          {
           Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.RetrySuccess).Replace("<FontName>",$FontName).Replace("<NewFontFile>",$NewFontFile)
           Remove-Item -Path $NewFontFile -Force
           If(Test-RegistryKeyValue -Path $FontRegKey -Name $FontNameForRegistry)
            {
             Remove-ItemProperty -Path $FontRegKey -Name $FontNameForRegistry | Out-Null
            }
          }
        }
         else
        {
         Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.FileNotFound).Replace("<NewFontFile>",$NewFontFile)
         Return $False
        }
      }
     
     Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry " "  
     Return $True   
   }

  Function UserDetails
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       03-Jan-21 / Modified on 14-Jan-21 / Modified on 22-Apr-22 / Modified on 01-May-2022
    Created by:       Willem-Jan Vroom
    Functionname:     UserDetails
    =============================================================================================================================================
    .SYNOPSIS

    This function returns 4 details of the Current Logged In Usser
    1. The username of the current logged in user
    2. User\Domain of the current logged in user
    3. User SID fo the User\Domain
    4. Account name that is using the script 

    Initially, the scriptAccount was found with the command 
    [System.Security.Principal.WindowsIdentity]::GetCurrent().Name

    But that command throws up an error due to the PowerShell ConstrainedMode.

    Also, the output of whoami /user depends on the language. In the German language the output is different.
    The header information is in the German language, so the header should be read from the CSV file.
    That can be done with PSObject.Properties.Name

    C:\Users\Test>whoami /user

    BENUTZERINFORMATIONEN
     ---------------------

    Benutzername         SID
    ==================== ==============================================
    windows10wkg_02\test S-1-5-21-1753037473-1212035439-1379364385-1001

    #>

    $Explorer      = (Get-WMIObject -Query "Select * From Win32_Process Where Name='explorer.exe'")
    if($Explorer.Count -gt 1)
     {
      $UserName      = ($Explorer[-1]).GetOwner()
      $SID           = (($Explorer[-1]).GetOwnerSID()).SID
     }
      else
     {
      $UserName      = ($Explorer).GetOwner()
      $SID           = (($Explorer).GetOwnerSID()).SID
     }

    $UserAndDomain      = "$($Username.Domain )\$($Username.User)".ToUpper()
    $tmpScriptAccount   = (whoami /user /FO csv | convertfrom-csv)
    $TranslatedUserName = $tmpScriptAccount.PSObject.Properties.Name[0]
    $ScriptAccount      = $($tmpScriptAccount.$TranslatedUserName).ToUpper()
    
    Return $($Username.User),$UserAndDomain,$SID,$ScriptAccount

   }

  Function Find-Language
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       30-Dec-20
    Created by:       Willem-Jan Vroom 
    Organisation:                      
    Functionname:     Find-Language
    =============================================================================================================================================
    .SYNOPSIS

    This function works the best, even running under system context.
    Get-UiCulture returns the UICulture details from the account that is used. So when the system account is used, incorrect
    details might be shown.

    #>

    [CmdletBinding()]
    Param
     (
      [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $CurrentUserSID
     )

    $Result           = "en-US"
    $ThisFunctionName = $PSCmdlet.MyInvocation.MyCommand.Name


    $RegKey = "REGISTRY::HKEY_USERS\$CurrentUserSID\Control Panel\Desktop"
    $Value  = "PreferredUILanguages"
    if (Test-RegistryKeyValue -Path $RegKey -Name $Value)
     {
      $Result = (get-itemproperty $RegKey | Select -ExpandProperty $Value).Split()[0]
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Regkey '$RegKey' value '$Value' exists. The data is '$Result'."
      Return $Result
     }

    $RegKey = "REGISTRY::HKEY_USERS\.DEFAULT\Control Panel\Desktop\MuiCached"
    $Value  = "MachinePreferredUILanguages"
    if (Test-RegistryKeyValue -Path $RegKey -Name $Value)
     {
      $Result = (get-itemproperty $RegKey | Select -ExpandProperty $Value).Split()[0]
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Regkey '$RegKey' value '$Value' exists. The data is '$Result'."
      Return $Result
     }

    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "There was a problem reading the registry..."
    Return $Result
   }

  Function Get-FolderFromButton
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       26-January-2021 / Updated 09-February-2021
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-FolderFromButton
    =============================================================================================================================================
    .SYNOPSIS

    You can browse to a folder when clicked on the 'Browse' button.
    #>

    Param
    (
      [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $InitialDirectory,
      [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $Description,
      [Parameter(Mandatory = $False)]                         [Switch] $AllowNewFolder
    )

    $ThisFunctionName               = $PSCmdlet.MyInvocation.MyCommand.Name
    $Folder                         = New-Object System.Windows.Forms.FolderBrowserDialog
    $Folder.ShowNewFolderButton     = $AllowNewFolder
    $Folder.SelectedPath            = $InitialDirectory
    $Folder.Description             = $Description
    [void]$Folder.ShowDialog()
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.ReturnedFontsFolder).Replace("<FolderName>",$($Folder.SelectedPath))
    Return $Folder.SelectedPath
   }

# =============================================================================================================================================
# End function block
# =============================================================================================================================================

# =============================================================================================================================================
# Functions, used in the forms blocks
# =============================================================================================================================================

  Function PopulateFonts
   {
    
    $FolderWithFonts = Get-FolderFromButton -InitialDirectory $($Global:gblFolderWithFonts) -Description $FontActions.$Language.SpecifyFontsFolder
    [System.Windows.Forms.Application]::EnableVisualStyles()
    $frmProcessing   = New-Object 'System.Windows.Forms.Form'
    $txtMessage      = New-Object 'System.Windows.Forms.TextBox'

    $frmProcessing.Controls.Add($txtMessage)
    $frmProcessing.AutoScaleDimensions     = New-Object System.Drawing.SizeF(6, 13)
    $frmProcessing.AutoScaleMode           = 'Font'
    $frmProcessing.BackColor               = [System.Drawing.SystemColors]::ControlDark 
    $frmProcessing.ClientSize              = New-Object System.Drawing.Size(430, 44)
    $frmProcessing.FormBorderStyle         = 'None'
    $frmProcessing.Name                    = 'frmProcessing'
    $frmProcessing.ShowIcon                = $False
    $frmProcessing.StartPosition           = 'CenterParent'
    $frmProcessing.Text = "Form"
    $frmProcessing.add_Shown({    
            $FilesToProcess  = @(Get-ChildItem -Path $FolderWithFonts -Recurse:$($chkRecurse.Checked) | where {$_.Extension -in ".fon.",".fnt",".ttf",".otf",".ttc"})
            $Global:Table    = @()
            $Record = [ordered] @{"Filename" ="";
                                  "Fontname" =""}

            ForEach ($FileToProcess in $FilesToProcess)
             {
              $tmpFile           = $FileToProcess.FullName
              $tmpFont           = Read-Font -FileNameFromFont $($FileToProcess.FullName)
              $Record."Filename" = $tmpFile
              $Record."Fontname" = $tmpFont
              $objRecord         = New-Object PSObject -Property $Record
              $Global:Table            += $objRecord
             }
            [void]$frmProcessing.Close()
})
    #
    # txtMessage
    #
    $txtMessage.ReadOnly                   = $True
    $txtMessage.Location                   = New-Object System.Drawing.Point(12, 12)
    $txtMessage.MaxLength                  = 50
    $txtMessage.Name                       = 'txtMessage'
    $txtMessage.Size                       = New-Object System.Drawing.Size(400, 20)
    $txtMessage.TabIndex                   = 0
    $txtMessage.Text                       = $($FontActions.$Language.txtMessage).Replace("<FolderName>",$FolderWithFonts)
    $txtMessage.TextAlign                  = 'Center'
    $txtMessage.WordWrap                   = $False

    [void]$frmProcessing.ShowDialog()
    
    $dgvFontFiles.DataSource = $null
    $dgvFontFiles.Rows.Clear()
    if($Global:Table.Count -gt 0)
     {
      $dgvFontFiles.Enabled = $True
      $dgvFontFiles.DataSource = [System.Collections.ArrayList]$Global:Table
      $btnInstallFonts.Enabled = $True
      $btnUninstallSelectedFonts.Enabled = $True
      $chkAllItems.Enabled = $True
     }
   }

  Function SelectOrUnselectAllItemsInDGV
   {
    if($chkAllItems.Checked)
     {
      $dgvFontFiles.SelectAll()
     }
      else
     {
      $dgvFontFiles.ClearSelection()
     }
   }
   
  Function InstallOrUninstallAllTheSelectedFonts
   {
    
    Param
     (
      [String] $ActionToDo
     )
    $Rows = $dgvFontFiles.SelectedRows
    $Total = $Rows.Count
    $numSuccess = 0
    $numFailures = 0
    $ProgressBar.Visible = $True
    
    $Counter = 1
    ForEach ($Row in $Rows)
     {
      $File = $dgvFontFiles.Rows[$Row.Index].Cells['Filename'].Value
      $ProgressBar.TextOverlay = $($FontActions.$Language.ProgressbarText).Replace("<File>",$File).Replace("<ActionToDo>",$($FontActions.$Language.$ActionToDo))
      $ProgressBar.Value = $Counter / $Total * 100
      $Return = Set-Font -Action $ActionToDo -FullFileName $File -PerMachine:$($chkMachineWide.Checked)
      $Counter++
      if($Return) {$numSuccess++} else {$numFailures++}
     }
    $ProgressBar.Visible = $False
            
    $Text  = $($FontActions.$Language.ProgressbarCompleted)
    $Text += "`n" + $($FontActions.$Language.ProgressbarSuccess).Replace("<numSuccess>",$numSuccess)
    $Text += "`n" + $($FontActions.$Language.ProgressbarFailures).Replace("<numFailures>",$numFailures)
    Display-MessageBox -Text $Text -GUI
   }   

# =============================================================================================================================================
# End functions, used in the forms blocks.
# =============================================================================================================================================

# =============================================================================================================================================
# Define the variables
# =============================================================================================================================================
  
  Clear-Host

  $Global:gblDetailedLogging                     = $DetailedLogging
  $arrUsers                                      = @()
  $strCurrentDir                                 = Split-Path -parent $MyInvocation.MyCommand.Definition
  $ApplicationName                               = "Install or Uninstall Fonts"
  $ApplicationVersion                            = "0.2"
  $PowerShellLanguageMode                        = $ExecutionContext.SessionState.LanguageMode
  $Global:bolFullLanguage                        = $PowerShellLanguageMode -eq "FullLanguage"
  $Global:bolConstrainedLanguage                 = $PowerShellLanguageMode -eq "ConstrainedLanguage"
  $Global:gblFolderWithFonts                     = $strCurrentDir
  $Global:bolElevatedRights                      = Check-HasElevatedRights
  $MinimumWidth                                  = 1400
  $MinimumHeight                                 = 800
  
  if($FolderWithFonts)
   {
    $Global:gblFolderWithFonts                     = $FolderWithFonts
   }

# =============================================================================================================================================
# Find the logpath.
# It is the key 'HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders' with the value 'Local AppData'.
# And then '\temp' is added. 
# =============================================================================================================================================

  $OnlyUserName,               `
  $LoggedOnUserInDomainFormat, `
  $UseridSID,                  `
  $InstallAccount                 = UserDetails
  $RegKey                         = "REGISTRY::HKEY_USERS\$UseridSID\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
  $ValueName                      = "Local AppData"
  $Value                          = (Get-ItemProperty $RegKey).$ValueName
  $LogPath                        = $Value + "\temp"    
  $Global:gblLogPath              = $LogPath

# =============================================================================================================================================
# Define the results file. This file contains all the results.
# =============================================================================================================================================
 
  $strLastPartOfFileName                         = " ($((Get-Date).ToString('G')))"
  $strLastPartOfFileName                         = $strLastPartOfFileName -replace ":","-"
  $strLastPartOfFileName                         = $strLastPartOfFileName -replace "/","-"
  $PreFixLogFile                                 = $ApplicationName -replace(" ","")
  $ThisFunctionName                              = "[Main - Resultsfile]"
   
  if($Global:gblDetailedLogging)
   {
    $Global:gblLogFile = $Global:gblLogPath + "\"+ $PreFixLogFile + $($strLastPartOfFileName + ".log")
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Logfile: $Global:gblLogFile"
   }

# =============================================================================================================================================
# Find all the arguments and put them in the log file
# Source: https://ss64.com/ps/psboundparameters.html
# =============================================================================================================================================

  $ThisFunctionName                              = "[Main - Arguments]"
  if($Global:bolFullLanguage)
   {
    $TableWithParameters  = @()
    $RecordWithParameters = [ordered] @{"Key"     = "";
                                      "Value"   = ""}
  
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** Parameters part           *****"
  
    ForEach($boundparam in $PSBoundParameters.GetEnumerator()) 
     {
      $tmpValue                     = $($boundparam.Value)
      $Value                        = ""
      if($tmpValue -is [array])
       {
        ForEach ($object in $tmpValue)
         {
          if(-not($value))
           {
            $Value = $object
           }
            else
           {
            $Value +=",$($object)"
           }
         }
       }
        else
       {
        $Value = $tmpValue
       }
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Key: $($boundparam.Key) Value: $Value" 
     }
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** End Parameters part       *****`r`n" 
   }
    else
   {
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The parameters are not written to the logfile due to PowerShell ConstrainedMode."
   }

# =============================================================================================================================================
# Write the logged in user details to the log file. 
# =============================================================================================================================================
  
  $ThisFunctionName                              = "[Main - User Details]"
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** User details part         *****" 
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Logged on user:                  $LoggedOnUserInDomainFormat"
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Logged on user (SID):            $UseridSID"
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Has (elevated) admin rights:     $Global:bolElevatedRights"
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Installation account:            $InstallAccount"
  if($Global:gblDetailedLogging)
   {
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Logfile:                         $Global:gblLogFile"
   }
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** End User details part     *****`r`n"

# =============================================================================================================================================
# Read the JSON file with the translations.
# If the JSON file does not contain the detected language, then fallback to English.
# =============================================================================================================================================

  $ThisFunctionName                              = "[Main - Language]"
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** Language part             *****"

  if ($LanguageOverride)
   {
    $Language = $LanguageOverride
    Add-EntryToLogFile -FunctionName $ThisFunctionName  -Entry "The parameter -LanguageOverride is used. The language is '$Language'."
   }
    else
   {
    $Language = (Find-Language -CurrentUserSID $UseridSID).SubString(0, 2)
   }

  $JSONFile = $strCurrentDir + "\"+ $($MyInvocation.MyCommand.Name -replace ".ps1",".json")

  if (-not (Test-Path($JSONFile)))
   {
    $Message = "The language file '$JSONFile' does not exists. Leaving the script."
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $Message
    Write-Host $Message
    Exit 1
   }

  $FontActions = Get-Content $JSONFile -Encoding UTF8 | ConvertFrom-Json

  if (-not ($FontActions.$Language))
   {
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The language '$Language' is not found in the json file '$JSONFile'."
    $Language = "en"
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Falling back to the default language '$Language'."
   }

  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The language '$Language' is used."

  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** End language part         *****`r`n" 

# =============================================================================================================================================
# End reading the JSON file with the translations.
# =============================================================================================================================================

# =============================================================================================================================================
# Add assembly (only FullLanguage Mode and not silent)
# =============================================================================================================================================
  
  if($Global:bolFullLanguage -and -not $Silent)
   {    
    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing
    Add-Type -AssemblyName System.Runtime
    Add-Type -AssemblyName PresentationFramework 
    [System.Windows.Forms.Application]::EnableVisualStyles()
   }
   
# =============================================================================================================================================
# Error handling.
# =============================================================================================================================================

  $ErrorNumber = 0
  $ErrorText   = ""
  if (-not $Silent)             
   {
    if ($Global:bolConstrainedLanguage)         {$ErrorNumber = $ErrorNumber +  2}
    if ($Global:bolFullLanguage)
     {
      $DetectedWidth  = ([System.Windows.Forms.SystemInformation]::PrimaryMonitorSize).Width
      $DetectedHeight = ([System.Windows.Forms.SystemInformation]::PrimaryMonitorSize).Height
      if($DetectedWidth -le $MinimumWidth)   {$ErrorNumber = $ErrorNumber + 16}
      if($DetectedHeight -le $MinimumHeight) {$ErrorNumber = $ErrorNumber + 32}
     }
   }
  
  if ($FolderWithFonts -and -not (test-path($FolderWithFonts))) {$ErrorNumber = $ErrorNumber + 4} 

  if ($ForcePerMachine -and -not ($Global:bolElevatedRights))   {$ErrorNumber = $ErrorNumber + 8} 

  if (($ErrorNumber -band 2) -eq 2) {$ErrorText = $FontActions.$Language.Error02Text; $Silent = $True}
  if (($ErrorNumber -band 4) -eq 4)
   {
    $tmpText = $($FontActions.$Language.Error04Text).Replace("<FolderName>",$FolderWithFonts).Trim()
    if($ErrorText) {$ErrorText += "`n$tmpText"} else {$ErrorText = $tmpText}
   }

  if (($ErrorNumber -band 8) -eq 8)
   {
    if($ErrorText) {$ErrorText += "`n$($FontActions.$Language.Error08Text)"} else {$ErrorText = $($FontActions.$Language.Error08Text)}
   }

  if (($ErrorNumber -band 16) -eq 16)
   {
    $tmpText = $($FontActions.$Language.Error16Text).Replace("<DetectedWidth>",$DetectedWidth).Replace("<AllowedWidth>",$MinimumWidth)
    if($ErrorText) {$ErrorText += "`n$tmpText"} else {$ErrorText = $tmpText}
   }
  if (($ErrorNumber -band 32) -eq 32)
   {
    $tmpText = $($FontActions.$Language.Error32Text).Replace("<DetectedHeight>",$DetectedHeight).Replace("<AllowedHeight>",$MinimumHeight)
    if($ErrorText) {$ErrorText += "`n$tmpText"} else {$ErrorText = $tmpText}
   }

  if($ErrorNumber -gt 0)
   {
    Display-MessageBox -Text $ErrorText -GUI:(-not($Silent)) -Error
    Exit $ErrorNumber
   }

# =============================================================================================================================================
# End Error handling.
# =============================================================================================================================================

# =============================================================================================================================================
# Define the C++ code to Add or Remove fonts
# =============================================================================================================================================

$fontCSharpCode = @'
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
namespace FontResource
{
    public class AddRemoveFonts
    {
        [DllImport("gdi32.dll")]
        static extern int AddFontResource(string lpFilename);
        public static int AddFont(string fontFilePath) {
            try 
            {
                return AddFontResource(fontFilePath);
            }
            catch
            {
                return 0;
            }
        }
        [DllImport("gdi32.dll")]
        static extern int RemoveFontResource(string lpFilename);
        public static int RemoveFont(string fontFilePath) {
            try 
            {
                return RemoveFontResource(fontFilePath);
            }
            catch
            {
                return 0;
            }
        }

    }
}
'@

# =============================================================================================================================================
# Add C Sharp code to add fonts.
# =============================================================================================================================================
          
  Add-Type $fontCSharpCode -ErrorAction SilentlyContinue

  if($Silent)
   {

  # =============================================================================================================================================
  # Process all the files
  # =============================================================================================================================================

    $FilesToProcess  = @(Get-ChildItem -Path $FolderWithFonts -Recurse:$IncludeSubFolders | where {$_.Extension -in ".fon.",".fnt",".ttf",".otf",".ttc"})
      
    $NumberOfFiles = $FilesToProcess.Count
    $Counter       = 1 

    $numSuccess  = 0
    $numFailures = 0

    ForEach ($FileToProcess in $FilesToProcess)
     {
      Write-Progress -Id 1 -Activity ($FontActions.$Language.ProgressbarText).Replace("<File>",$($FileToProcess.FullName)).Replace("<ActionToDo>",$($FontActions.$Language.$ActionToPerform)) -PercentComplete ($Counter / $NumberOfFiles * 100)
      $Return = Set-Font -Action $ActionToPerform -FullFileName $($FileToProcess.FullName) -PerMachine:$ForcePerMachine
      if($Return) {$numSuccess++} else {$numFailures++}
      $Counter++
     }

     Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry " "
     Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.ProgressbarCompleted)
     Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.ProgressbarSuccess).Replace("<numSuccess>",$numSuccess)
     Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $($FontActions.$Language.ProgressbarFailures).Replace("<numFailures>",$numFailures)
   }
  else # Not Silent
   {

  # =============================================================================================================================================
  # Sapient Progress bar. (C) by SAPIEN Technologies, Inc.   
  # =============================================================================================================================================

$SAPIENTypes = @'
using System;
using System.Windows.Forms;
using System.Drawing;
        namespace SAPIENTypes
        {
    public class ProgressBarOverlay : System.Windows.Forms.ProgressBar
        {
                public ProgressBarOverlay() : base() { SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); }
            protected override void WndProc(ref Message m)
            { 
                base.WndProc(ref m);
                if (m.Msg == 0x000F)// WM_PAINT
                {
                    if (Style != System.Windows.Forms.ProgressBarStyle.Marquee || !string.IsNullOrEmpty(this.Text))
                        {
                            using (Graphics g = this.CreateGraphics())
                            {
                                using (StringFormat stringFormat = new StringFormat(StringFormatFlags.NoWrap))
                                {
                                    stringFormat.Alignment = StringAlignment.Center;
                                    stringFormat.LineAlignment = StringAlignment.Center;
                                    if (!string.IsNullOrEmpty(this.Text))
                                        g.DrawString(this.Text, this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                    else
                                    {
                                        int percent = (int)(((double)Value / (double)Maximum) * 100);
                                        g.DrawString(percent.ToString() + "%", this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                    }
                                }
                            }
                        }
                }
            }
              
                public string TextOverlay
                {
                    get
                    {
                        return base.Text;
                    }
                    set
                    {
                        base.Text = value;
                        Invalidate();
                    }
                }
        }
        }
'@

     Add-Type $SAPIENTypes -ReferencedAssemblies 'System.Windows.Forms', 'System.Drawing' -ErrorAction SilentlyContinue

  # =============================================================================================================================================
  # Make the form
  # =============================================================================================================================================
  
     $frmFonts                  = New-Object 'System.Windows.Forms.Form'
     $chkRecurse                = New-Object 'System.Windows.Forms.CheckBox'
     $btnExit                   = New-Object 'System.Windows.Forms.Button'
     $chkMachineWide            = New-Object 'System.Windows.Forms.CheckBox'
     $chkAdmin                  = New-Object 'System.Windows.Forms.CheckBox'
     $grpFolder                 = New-Object 'System.Windows.Forms.GroupBox'
     $ProgressBar               = New-Object 'SAPIENTypes.ProgressBarOverlay'
     $chkAllItems               = New-Object 'System.Windows.Forms.CheckBox'
     $btnUninstallSelectedFonts = New-Object 'System.Windows.Forms.Button'
     $btnInstallFonts           = New-Object 'System.Windows.Forms.Button'
     $btnGetFolderContent       = New-Object 'System.Windows.Forms.Button'
     $dgvFontFiles              = New-Object 'System.Windows.Forms.DataGridView'
     $frmFonts.Controls.Add($ProgressBar)
     $frmFonts.Controls.Add($chkRecurse)
     $frmFonts.Controls.Add($btnExit)
     $frmFonts.Controls.Add($chkMachineWide)
     $frmFonts.Controls.Add($chkAdmin)
     $frmFonts.Controls.Add($grpFolder)
     $frmFonts.AutoScaleDimensions                        = New-Object System.Drawing.SizeF(6, 13)
     $frmFonts.AutoScaleMode                              = 'Font'
     $frmFonts.ClientSize                                 = New-Object System.Drawing.Size(1029, 667)
     $frmFonts.FormBorderStyle                            = 'Fixed3D'
     $frmFonts.Name                                       = 'frmFonts'
     $frmFonts.StartPosition                              = 'CenterScreen'
     #
     # chkRecurse
     #
     $chkRecurse.Location                                 = New-Object System.Drawing.Point(680, 73)
     $chkRecurse.Name                                     = 'chkRecurse'
     $chkRecurse.Size                                     = New-Object System.Drawing.Size(337, 24)
     $chkRecurse.TabIndex                                 = 8
     $chkRecurse.UseVisualStyleBackColor                  = $True
     #
     # btnExit
     #
     $btnExit.Location                                    = New-Object System.Drawing.Point(870, 621)
     $btnExit.Name                                        = 'btnExit'
     $btnExit.Size                                        = New-Object System.Drawing.Size(147, 40)
     $btnExit.TabIndex                                    = 7
     $btnExit.UseVisualStyleBackColor                     = $True
     #
     # chkMachineWide
     #
     $chkMachineWide.Location                             = New-Object System.Drawing.Point(680, 43)
     $chkMachineWide.Name                                 = 'chkMachineWide'
     $chkMachineWide.Size                                 = New-Object System.Drawing.Size(337, 24)
     $chkMachineWide.TabIndex                             = 5
     $chkMachineWide.UseVisualStyleBackColor              = $True
     #
     # chkAdmin
     #
     $chkAdmin.Location                                   = New-Object System.Drawing.Point(680, 13)
     $chkAdmin.Name                                       = 'chkAdmin'
     $chkAdmin.Size                                       = New-Object System.Drawing.Size(337, 24)
     $chkAdmin.TabIndex                                   = 4
     $chkAdmin.UseVisualStyleBackColor                    = $True
     #
     # grpFolder
     #
     $grpFolder.Controls.Add($chkAllItems)
     $grpFolder.Controls.Add($btnUninstallSelectedFonts)
     $grpFolder.Controls.Add($btnInstallFonts)
     $grpFolder.Controls.Add($btnGetFolderContent)
     $grpFolder.Controls.Add($dgvFontFiles)
     $grpFolder.Location                                  = New-Object System.Drawing.Point(12, 12)
     $grpFolder.Name                                      = 'grpFolder'
     $grpFolder.Size                                      = New-Object System.Drawing.Size(650, 649)
     $grpFolder.TabIndex                                  = 3
     $grpFolder.TabStop                                   = $False
     #
     # ProgressBar
     #
     $ProgressBar.BackColor                               = [System.Drawing.SystemColors]::ControlLightLight 
     $ProgressBar.Location                                = New-Object System.Drawing.Point(20, 275)
     $ProgressBar.Margin                                  = '5, 5, 5, 5'
     $ProgressBar.Name                                    = 'ProgressBar'
     $ProgressBar.Size                                    = New-Object System.Drawing.Size(970, 75)
     $ProgressBar.Style                                   = 'Continuous'
     $ProgressBar.TabIndex                                = 5
     #
     # chkAllItems
     #
     $chkAllItems.Location                                = New-Object System.Drawing.Point(7, 20)
     $chkAllItems.Name                                    = 'chkAllItems'
     $chkAllItems.Size                                    = New-Object System.Drawing.Size(432, 24)
     $chkAllItems.TabIndex                                = 4
     $chkAllItems.UseVisualStyleBackColor                 = $True
     #
     # btnUninstallSelectedFonts
     #
     $btnUninstallSelectedFonts.Location                  = New-Object System.Drawing.Point(432, 599)
     $btnUninstallSelectedFonts.Name                      = 'btnUninstallSelectedFonts'
     $btnUninstallSelectedFonts.Size                      = New-Object System.Drawing.Size(200, 40)
     $btnUninstallSelectedFonts.TabIndex                  = 3
     $btnUninstallSelectedFonts.UseVisualStyleBackColor   = $True
     #
     # btnInstallFonts
     #
     $btnInstallFonts.Location                            = New-Object System.Drawing.Point(226, 599)
     $btnInstallFonts.Name                                = 'btnInstallFonts'
     $btnInstallFonts.Size                                = New-Object System.Drawing.Size(200, 40)
     $btnInstallFonts.TabIndex                            = 2
     $btnInstallFonts.UseVisualStyleBackColor             = $True
     #
     # btnGetFolderContent
     #
     $btnGetFolderContent.Location                        = New-Object System.Drawing.Point(20, 599)
     $btnGetFolderContent.Name                            = 'btnGetFolderContent'
     $btnGetFolderContent.Size                            = New-Object System.Drawing.Size(200, 40)
     $btnGetFolderContent.TabIndex                        = 1
     $btnGetFolderContent.UseVisualStyleBackColor         = $True
     #
     # dgvFontFiles
     #
     $dgvFontFiles.AutoSizeColumnsMode                    = 'AllCells'
     $dgvFontFiles.AutoSizeRowsMode                       = 'AllCells'
     $dgvFontFiles.ColumnHeadersHeightSizeMode            = 'AutoSize'
     $System_Windows_Forms_DataGridViewCellStyle_1                     = New-Object 'System.Windows.Forms.DataGridViewCellStyle'
     $System_Windows_Forms_DataGridViewCellStyle_1.Alignment           = 'MiddleLeft'
     $System_Windows_Forms_DataGridViewCellStyle_1.BackColor           = [System.Drawing.SystemColors]::Window 
     $System_Windows_Forms_DataGridViewCellStyle_1.Font                = [System.Drawing.Font]::new('Microsoft Sans Serif', '8.25')
     $System_Windows_Forms_DataGridViewCellStyle_1.ForeColor           = [System.Drawing.SystemColors]::ControlText 
     $System_Windows_Forms_DataGridViewCellStyle_1.SelectionBackColor  = [System.Drawing.SystemColors]::Highlight 
     $System_Windows_Forms_DataGridViewCellStyle_1.SelectionForeColor  = [System.Drawing.SystemColors]::HighlightText 
     $System_Windows_Forms_DataGridViewCellStyle_1.WrapMode            = 'True'
     $dgvFontFiles.DefaultCellStyle                       = $System_Windows_Forms_DataGridViewCellStyle_1
     $dgvFontFiles.Location                               = New-Object System.Drawing.Point(6, 50)
     $dgvFontFiles.Name                                   = 'dgvFontFiles'
     $dgvFontFiles.Size                                   = New-Object System.Drawing.Size(626, 543)
     $dgvFontFiles.TabIndex                               = 0
   
     $frmFonts.Text                                       = $FontActions.$Language.frmFonts + " " + $ApplicationVersion
     $chkRecurse.Text                                     = $FontActions.$Language.chkRecurse
     $btnExit.Text                                        = $FontActions.$Language.btnExit
     $chkMachineWide.Text                                 = $FontActions.$Language.chkMachineWide
     $chkAdmin.Text                                       = $FontActions.$Language.chkAdmin
     $grpFolder.Text                                      = $FontActions.$Language.grpFolder
     $chkAllItems.Text                                    = $FontActions.$Language.chkAllItems
     $btnInstallFonts.Text                                = $FontActions.$Language.btnInstallFonts
     $btnUninstallSelectedFonts.Text                      = $FontActions.$Language.btnUninstallSelectedFonts
     $btnGetFolderContent.Text                            = $FontActions.$Language.btnGetFolderContent
   
     $chkRecurse.Checked                                  = $IncludeSubFolders
     $chkAdmin.Checked                                    = $Global:bolElevatedRights
     $chkAdmin.Enabled                                    = $False
     $btnInstallFonts.Enabled                             = $False
     $btnUninstallSelectedFonts.Enabled                   = $False
     $chkAllItems.Enabled                                 = $False
     $ProgressBar.Visible                                 = $False

     if(-not($chkAdmin.Checked))
      { 
       $chkMachineWide.Enabled = $False
      }
   
     $btnGetFolderContent.add_Click({PopulateFonts})
     $btnInstallFonts.add_Click({InstallOrUninstallAllTheSelectedFonts -ActionToDo "Install"})
     $btnUninstallSelectedFonts.add_Click({InstallOrUninstallAllTheSelectedFonts -ActionToDo "Uninstall"})
   
     $btnExit.add_Click({$frmFonts.Close()})

     $chkAllItems.Add_CheckedChanged({SelectOrUnselectAllItemsInDGV})
      
     [void]$frmFonts.ShowDialog()
      
    # =============================================================================================================================================
    # End of making the form
    # =============================================================================================================================================

   }

Download the zip file InstallorUninstallFonts_v02.zip

Before download...

Continue... ×

Download the zip file InstallorUninstallFonts_v01.zip

Before download...

Continue... ×