【问题标题】:How do I pass option flags to Folder.CopyHere in PowerShell?如何在 PowerShell 中将选项标志传递给 Folder.CopyHere?
【发布时间】:2012-08-23 08:24:33
【问题描述】:

我正在尝试编写一个脚本,该脚本会自动将一堆字体以静默方式移动到 Fonts special folder 中,以便它们像您从资源管理器“安装”它们一样可用(通过拖放、复制或右键单击并选择安装)。我有Shell.Application 部分一直到副本。

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

但是,某些系统可能已经安装了字体,我希望隐藏进度对话框并静默接受任何提示。

所以,我正在调查Folder.CopyHere 选项标志。

  • 4不显示进度对话框
  • 16 对显示的任何对话框都回复“全部是”。

我希望它们在这个文件夹中得到支持(一些选项被设计忽略了)。我认为这些都是十进制的,对吧?他们需要转换吗?但是我将它们传递进去,我仍然看到两个对话框。我试过了

$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!

而且,如果我可以让一个选项起作用,我如何让两个选项都起作用?我bor他们在一起吗?格式呢?

$options = 4 -bor 16

或者我是否添加它们或将它们转换为十六进制?

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

【问题讨论】:

  • 管理员/常规 PowerShell 会话似乎没有效果。

标签: powershell com


【解决方案1】:

您可以使用4 -bor 16。因为类型是 VARIANT,所以很难说这个方法期望什么。我原以为它会取一个整数值。如果这不起作用,MSDN topic on Folder.CopyHere 的这条评论暗示字符串应该起作用:

function CopyFileProgress
{
    param( $Source, $DstFolder, $CopyType = 0 )

    # Convert the decimal to hex
    $copyFlag = [String]::Format("{0:x}", $CopyType)

    $objShell = New-Object -ComObject "Shell.Application"
    $objFolder = $objShell.NameSpace($DestLocation) 
    $objFolder.CopyHere($Source, $copyFlag)
}

虽然我想知道格式字符串是否应该是"0x{0:x}"

请注意,对于普通的 .NET 标志样式枚举,您可以将多个标志传递给一个强类型为枚举的 .NET(或命令参数),如下所示:

$srv.ReplicationServer.Script('Creation,SomeOtherValue')

Oisin 在blog post 中写了一些关于这个主题的信息。

【讨论】:

【解决方案2】:

我遇到了同样的问题,在另一个帖子中发现了这个问题,对我来说效果很好。

如果您希望它覆盖并保持静默,请将 0x10 更改为 0x14(文档)。

$destinationFolder.CopyHere($zipPackage.Items(), 0x14)

【讨论】:

    【解决方案3】:

    Folder.CopyHere 选项标志可能只是 not work。这让我很难过。我将不得不研究其中一种其他方法,所有这些方法都让我有点束手无策。

    独立进程

    在新进程中调用副本并使用ProcessStartInfo 属性隐藏窗口。我还没有实现这个,但我想知道它是否会解决用户提示覆盖现有文件的问题?

    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 循环

    仅限copy non-existing items。当我真的想用同名的新字体文件更新现有字体时,这似乎失败了。

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

    删除现有

    我正在考虑删除与我正在复制的字体同名的所有字体,然后复制该集合。虽然这有点残忍。而且我相信如果该字体因为正在使用而无法删除,会有 another 提示。 叹息

    【讨论】:

    【解决方案4】:

    复制标志对我不起作用。我在安装字体脚本中设置了一个作业,该脚本检测“安装字体”窗口并将{Enter} 发送给它,这样我就不会覆盖现有字体。

    Start-Job –Name DetectAndClosePrompt –Scriptblock {
      $i=1
      [void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
      [void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
      while ($i -eq 1) { 
        $windowPrompt = Get-Process -ErrorAction SilentlyContinue |? {$_.MainWindowTitle -like "*Installing Fonts*"} 
        [Microsoft.VisualBasic.Interaction]::AppActivate($windowPrompt.ID)
        [System.Windows.Forms.SendKeys]::SendWait("{Enter}")
        sleep 2 
      }
    }
    

    复制/安装所有字体后...我按名称删除了该作业。

    Get-Job DetectAndClosePrompt | Remove-Job -Force
    

    这适用于 Windows 7、8.x 和 10。

    【讨论】:

    • 非常有趣的方法,但很遗憾你不能在 PC 运行时使用它,并且它不会在最后自动取消作业
    【解决方案5】:

    我看到了许多 Unzip 文件夹操作,但实际上没有人编写适合 Fonts 文件夹情况的解决方案。所以我自己写了!事实证明,Fonts 文件夹确实实现了 Shell.Folder.CopyHere 方法,但不支持为该方法的第二个参数传递的任何重载。为什么?谁知道!我怀疑'The Old new Thing'Windows Developer 博客的 Raymond Chen 可以解释它,但我不知道答案。因此,我们需要在尝试复制字体之前智能地查找我们的字体,否则我们会收到令人讨厌的消息。


    在我的代码中,我们通过使用通配符搜索检查字体名称的前四个字符是否匹配来检查字体是否存在。如果字体不存在,我们假设这是我们第一次在这个系统上安装字体并设置一个名为 $FirstInstall 的特殊标志。

    从那时起,在脚本中,如果 $FirstInstall 为真,我们将在源字体目录中安装所有字体。在随后的执行中,我们检查每个字体是否匹配,如果是,我们中止该副本。如果没有,我们继续复制。到目前为止,这似乎对我的大多数客户都有效。

    给你!

        <#
    .SYNOPSIS
        Script to quietly handle the installation of fonts from a network source to a system
    
    .DESCRIPTION
        We Can't just move files into the %windir%\Fonts directory with a script, as a simple copy paste from command line doesn't trigger windows to note the new font
    If we used that approach, the files would exist within the directory, but the font files woudln't be registered in windows, nor would applications 
    display the new font for use.  Instead, we can make a new object of the Shell.Application type (effectively an invisible Windows Explorer Windows) and use its Copy method
    Which is the functional equivalent of dragging an dropping font files into the Font folder, which does trigger the font to be installed the same as if you right clicked the font
    and choose install.
    
    .PARAMETER FontPath
        The path of a folder where fonts reside on the network
    
    .EXAMPLE
        .\Install-Fonts.ps1 -FontPath "\\corp\fileshare\Scripts\Fonts"
    
        Installing font...C:\temp\Noto\NotoSans-Bold.ttf
        Installing font...C:\temp\Noto\NotoSans-BoldItalic.ttf
        Installing font...C:\temp\Noto\NotoSans-Italic.ttf
        Installing font...C:\temp\Noto\NotoSans-Regular.ttf
    
        In this case, the fonts are copied from the network down to the system and installed silently, minus the logging seen here
        import files needed for step 1, step 2, and step 5 of the migration process.
    
    .EXAMPLE
        .\Install-Fonts.ps1 -FontPath "\\corp\fileshare\Scripts\Fonts"
    
        Font already exists, skipping
        Font already exists, skipping
        Font already exists, skipping
        Font already exists, skipping
        In this case, the fonts already existed on the system.  Rather than display an annoying 'Overwrite font' dialog, we simply abort the copy and try the next file
    
    
    .INPUTS
        String.
    
    .OUTPUTS
        Console output
    
    .NOTES
        CREATED: 06/11/2015
        Author: sowen@ivision.com
    
        MODIFIED:06/11/2015
        Author: sowen@ivision.com   -Reserved...
    
    #> 
    param
        ( 
            [Parameter(Mandatory)][string]$FontPath="C:\temp\Noto" 
        ) 
    
    #0x14 is a special system folder pointer to the path where fonts live, and is needed below. 
    $FONTS = 0x14
    
    #Make a refrence to Shell.Application
    $objShell = New-Object -ComObject Shell.Application
    $objFolder = $objShell.Namespace($FONTS)
    
    ForEach ($font in (dir $fontsPath -Recurse -Include *.ttf,*.otf)){
    
        #check for existing font (to suppress annoying 'do you want to overwrite' dialog box
        if ((($objShell.NameSpace($FONTS).Items() | where Name -like "$($font.BaseName.Split('-')[0].substring(0,4))*") | measure).Count -eq 0){
            $firstInstall = $true}
    
        if ($firstInstall -ne $true) {Write-Output "Font already exists, skipping"}
    
            else{    
            $objFolder.CopyHere($font.FullName)
            Write-Output "Installing font...$($font.FullName)"
            $firstInstall = $true
            }
    
        }
    
    .\Install-Fonts.ps1 -FontPath "\\corp\fileshare\Scripts\Fonts"
    

    【讨论】:

      【解决方案6】:

      你可以只取你的选项的总和。我需要使用两个选项运行 CopyHere - SILENT 和 NOCONFIRMATION。请看下面的示例:

      function Unzip-Archive($targetpath, $destination)
      {    
          $shell_app=new-object -com shell.application
      
          $FOF_SILENT_FLAG = 4
          $FOF_NOCONFIRMATION_FLAG = 16
      
          $zip_file = $shell_app.namespace("$targetpath")
      
          #Set the destination directory for the extracts
          $destination = $shell_app.namespace("$destination")
      
          #unzip the files
          $destination.Copyhere($zip_file.items(), $FOF_SILENT_FLAG + $FOF_NOCONFIRMATION_FLAG)    
      }
      

      【讨论】:

      • 复制到windows-fonts-directory时这两个选项不起作用...
      【解决方案7】:

      @FoxDeploy 的回答存在几个问题,这就是它不起作用的原因。第一个问题是您还想检查 %USERPROFILE% 中的 Fonts 文件夹,否则您会看到确认对话框。第二个问题是您要避免在字体名称中使用“-”。

      以下是以从 CodeFonts repo 安装字体的固定版本为例:

      $ErrorActionPreference = "Stop"
      Add-Type -AssemblyName System.Drawing
      
      # Clone chrissimpkins/codeface from which we will install fonts
      if (!(Test-Path /GitHubSrc/codeface)){
          git clone git://github.com/chrissimpkins/codeface.git /GitHubSrc/codeface
      }
      
      #0x14 is a special system folder pointer to the path where fonts live, and is needed below. 
      $FONTS = 0x14
      $fontCollection = new-object System.Drawing.Text.PrivateFontCollection
      
      #Make a refrence to Shell.Application
      $objShell = New-Object -ComObject Shell.Application
      $objFolder = $objShell.Namespace($FONTS)
      
      # local path
      
      $localSysPath = "$Env:USERPROFILE\AppData\Local\Microsoft\Windows\Fonts"
      $localSysFonts = Get-ChildItem -Path $localSysPath -Recurse -File -Name | ForEach-Object -Process {[System.IO.Path]::GetFileNameWithoutExtension($_)}
      
      $fontsPath="\GitHubSrc\codeface\fonts"
      ForEach ($font in (dir $fontsPath -Recurse -Include *.ttf,*.otf)){
          if ($localSysFonts -like $font.BaseName) {
              Write-Output "SKIP: Font ${font} already exists in ${localSysPath}"
          }
          else {
              $fontCollection.AddFontFile($font.FullName)
              $fontName = $fontCollection.Families[-1].Name
      
              #check for existing font (to suppress annoying 'do you want to overwrite' dialog box
              if ((($objShell.NameSpace($FONTS).Items() | where Name -ieq $fontName) | measure).Count -eq 0){
                  Write-Output "INST: Font ${font}"
                  $objFolder.CopyHere($font.FullName)
                  $firstInstall = $true
              }
              else {
                  Write-Output "SKIP: Font ${font} already exists in SYSTEM FONTS"
              }
          }
          # Read-Host -Prompt "Press Enter to continue"
      }
      

      【讨论】:

        【解决方案8】:

        我只是通过简单地使用 + 来实现这个功能,即

        function Expand-ZIPFile($file, $destination)
        {
            $shell = new-object -com shell.application
            $zip = $shell.NameSpace($file)
            foreach($item in $zip.items())
            {
                $shell.Namespace($destination).copyhere($item, 16+1024)
            }
        } 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-09-26
          • 2021-11-25
          • 2021-04-21
          • 2016-10-28
          • 1970-01-01
          • 1970-01-01
          • 2015-05-20
          • 2019-04-14
          相关资源
          最近更新 更多