Author Posts

January 18, 2018 at 10:03 pm

EDIT *** WORKING SOLUTION POSTED BELOW INITIAL QUESTION ***

Hello,

I think the issue with the script is my use of the -Scriptblock. It is confusing because the only variables I am using in the -Scriptblock are created there. They are not(?) local variables, but I get the error: You cannot call a method on a null-valued expression.

Here is my script:

#provide lab pc names
$labPCs = @("pc1","pc2")

#foreach lab $labPC, check for \tempFont directory. if false, create it and copy fonts. if true, copy new fonts to it
ForEach ($pc in $labPCs) {

    #define $localFolder
    $localFolder = "\\$($pc)\c$\tempFont\"

    #test path to $localFolder, create and copy fonts if false, else copy fonts
    if (!(Test-Path -Path $localFolder)) {

        New-Item -ItemType Directory -Path $localFolder -Force
        Copy-Item -Path $newFonts -Destination $localFolder

    }

    Else {
    
    Copy-Item -Path $newFonts -Destination $localFolder
    
    }
    
#for each font, check if it exists by name in the Fonts folder on the target machine. if not, attempt to install it...
    ForEach ($font in $newFonts) {
    
            if (!(Test-Path -Path ('\\'+$($pc)+'\C$\Windows\Fonts\'+$($font).Name))) {
#start scriptblock
                Invoke-Command -ComputerName $pc -ScriptBlock {
                #create a new com object Shell.Application
                    $shell = New-Object -ComObject Shell.Application
                #create fonts com object
                    $fontNS = $shell.NameSpace(0x14)
                #grab newly copied fonts
                    $copiedFonts = Get-ChildItem "C:\tempFont\"
#create com object from that directory
                    $copiedFontsNS = $shell.NameSpace($copiedFonts)
                #use CopyHere method to copy items in $copiedFonts to Fonts namespace
                    $fontNS.CopyHere($copiedFontsNS.Items(), 0x14)

                    }

            }
    
    }

}

*** WORKING SOLUTION *** – my issues were a lack of understand of Invoke-Command, Scriptblock, and the asynchronous CopyHere method. I read up and understand these much better (so I stopped using CopyHere altogether :)), and now the script works great...

#grab new fonts from share
$newFonts = Get-ChildItem -Path C:\path\to\newFonts -Recurse -Include *.ttf,*.otf,*.fon

#provide lab pc names
$labPCs = @("pc1","pc2")

#foreach lab $labPC, check for \tempFont directory. if false, create it and copy fonts. if true, copy new fonts to it
ForEach ($pc in $labPCs) {    

    #define $localFolder
    $localFolder = "\\$($pc)\c$\tempFont\"

    #test path to $localFolder, create and copy fonts if false, else copy fonts
    if (!(Test-Path -Path $localFolder)) {

        New-Item -ItemType Directory -Path $localFolder -Force
        Copy-Item -Path $newFonts -Destination $localFolder

    }

    Else {
    
    Copy-Item -Path $newFonts -Destination $localFolder
    
    }

    #run the following commands on target lab PC's
    Invoke-Command -ComputerName $pc -ScriptBlock {
        
        #set dest path
        $destPath = "C:\Windows\Fonts\"

        #grab existing dest objects
        $destFiles = Get-ChildItem -Path $destPath -Recurse         

        #set new font source path
        $sourcePath = 'C:\tempFont\'

        #use this to grab all new font source objects
        $sourceFiles = Get-ChildItem -Path $sourcePath -Include *.ttf,*.otf,*.fon -Recurse
        
        #set directory for placing fonts to install
        $fontInstall = New-Item -Path $sourcePath -ItemType Directory -Name 'Install' -Force

        #set path to \Fonts in registry
        $regPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"      

        #for each source file, see if it exists in \Fonts on the lab PC... 
        ForEach ($s in $sourceFiles) {

            $regName = "$($s.Name)"

            #if not...
            if (-not(Test-Path ($destPath + $s.Name))) {

                #copy to the Install directory...
                Copy-Item -Path $s.FullName -Destination $fontInstall.FullName -Force;

                #grab the full path to the font in it's new location...
                $install = $fontInstall.FullName + '\' + $s.Name;

                #for each font in the Install directory, copy it to \Fonts via shell object. This copy method is asynchronous...
                #so I used a While loop with Start-Sleep to make sure the copy finishes before the script moves on...
                #logic is 'While the font to install does not exist in \Fonts, sleep the script for 1 second until the asynch copy finishes...
                ForEach ($i in $install) {

                    #copy the font file to the \Fonts folder
                    Copy-Item $i -Destination $destPath -Force

                    #register the newly copied fonts in the registry
                    New-ItemProperty -Path $regPath -Name $regName -Value $regName -Force | Out-Null;

                    }

                #register the newly copied fonts in the registry
                New-ItemProperty -Path $regPath -Name $regName -Value $regName -Force | Out-Null
                
                }                

            }

            #remove the Install folder from the lab PC.
            #Get-Item -Path $fontInstall.FullName | Remove-Item -Recurse -Force

        }

    }

}

January 19, 2018 at 7:51 am

Step through your code one segment at a time to make sure you are getting what you'd expect.
Comment out that whole Invoke-Command segment, and put some other dummy code there and see what your results are.

January 19, 2018 at 1:26 pm

Hi there,

Thank you for the comments.

I have stepped through the code and everything works up to the scriptblock. If I do something like New-Item ... in the first line of the scriptblock, it works. The item is created on the remote computer.

Thanks again!