10

我正在尝试编写一个脚本,该脚本会自动将一堆字体以静默方式移动到Fonts 特殊文件夹中,这样它们就可以像从资源管理器中“安装”它们一样可用(通过拖放、复制或右键单击并选择安装)。我把这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)
4

8 回答 8

4

You can use 4 -bor 16. It is hard to tell what this method expects since the type is VARIANT. I would have thought that it would take an integer value. If that doesn't work, this comment from the MSDN topic on Folder.CopyHere implies that a string should work:

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)
}

Although I wonder if the format string should be "0x{0:x}"?

Just be aware that for normal .NET flags style enums, you can pass multiple flags to a .NET (or command parameter) that is strongly typed to the enum like so:

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

Oisin has written up some info on this subject in this blog post.

于 2012-08-28T19:16:31.860 回答
3

我遇到了同样的问题,并在另一个线程中发现了这个问题,对我来说非常有效。

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

$destinationFolder.CopyHere($zipPackage.Items(), 0x14)
于 2013-10-15T21:02:22.940 回答
2

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

分离过程

在新进程中调用副本并使用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

循环

复制不存在的项目。当我实际上想用同名的新字体文件更新现有字体时,这似乎失败了。

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

删除现有

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

于 2012-08-29T03:21:11.377 回答
2

复制标志对我不起作用。我在安装字体脚本中设置了一个作业,该脚本检测“安装字体”窗口并将其发送{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。

于 2015-05-13T10:24:27.910 回答
1

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


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

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

干得好!

    <#
.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"
于 2015-06-11T16:48:05.210 回答
0

你可以只取你的选项的总和。我需要使用两个选项运行 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)    
}
于 2013-06-27T08:16:33.397 回答
0

@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"
}
于 2019-12-06T00:46:44.100 回答
-1

我只是通过简单地使用 + ie 来实现它

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)
    }
} 
于 2014-11-21T20:24:24.880 回答