Search code examples
powershellcom

How do I pass option flags to Folder.CopyHere in PowerShell?


I am trying to write a script that automatically and silently moves a bunch of fonts into the Fonts special folder so they are available as if you had "installed" them from Explorer (by dragging and dropping, copying, or right-click and choosing Install). I have the Shell.Application part down all the way to the copy.

$FONTS = 0x14
$shell = New-Object -ComObject Shell.Application
$source = $shell.Namespace($downloaded_path)
$target = $shell.Namespace($FONTS)
$target.CopyHere($source.Items())

However, some systems may already have the fonts installed and I want the progress dialog to be hidden and any prompts to be silently accepted.

Installing Fonts The font is already installed

So, I'm investigating the Folder.CopyHere option flags.

  • 4 Do not display a progress dialog box
  • 16 Respond with "Yes to All" for any dialog box that is displayed.

I hope they are supported in this folder (some options are ignored by design). And I think these are in decimal, right? Do they need to be converted? However I pass them in, I still see both dialogs. I have tried

$options = 4           <-- don't expect int to work
$options = 0x4         <-- thought hexidecimal would be ok, the VB documentation shows &H4&
$options = "4"         <-- string's the thing?
$options = [byte]4     <-- no luck with bytes
$options = [variant]4  <-- this isn't even a type accelerator!

And, if I can get one option working, how do I get both working? Do I bor them together? What about the formatting?

$options = 4 -bor 16

Or do I add them or convert them to hex?

$options = "{0:X}" -f (4 + 16)

Solution

  • The Folder.CopyHere option flags may simply not work. This makes me sad. I'll have to investigate one of these other methods, all of which leave me in a bit of a bind.

    Separate Process

    Invoke the copy in a new process and hide the window using the ProcessStartInfo properties. I haven't implemented this yet, but I wonder if it will address the user-prompting for overwriting existing files?

    Dim iProcess As New System.Diagnostics.ProcessStartInfo(AppDomain.CurrentDomain.BaseDirectory + “unzip.exe”)
    
    iProcess.CreateNoWindow = True
    Dim sArgs As String = ZippedFile
    iProcess.Arguments = sArgs
    iProcess.WindowStyle = ProcessWindowStyle.Hidden
    Dim p As New System.Diagnostics.Process
    iProcess.UseShellExecute = False
    p = System.Diagnostics.Process.Start(iProcess)
    p.WaitForExit(30000)
    Dim s As Integer = p.ExitCode
    iProcess.UseShellExecute = True
    
    p.Dispose()
    iProcess = Nothing
    

    For Loop

    Only copy non-existing items. This seems to fall down when I actually want to update an existing font with a new font file of the same name.

    foreach($File in $Fontdir) {
        $fontName = $File.Name.Replace(".ttf", " Regular")
        $objFolderItem = $objFolder.ParseName($fontName);
        if (!$objFolderItem) {
          $objFolder.CopyHere($File.fullname,0x14)
        }
    }
    

    Remove Existing

    I'm thinking of removing all fonts of the same name as the ones I'm copying, then copying the set. Although that's kind of brutal. And I believe that there's another prompt if that font cannot be deleted because it's in use. sigh