如何在PowerShell中将选项标志传递给Folder.CopyHere?

时间:2012-08-28 19:07:36

标签: powershell com

我正在尝试编写一个脚本,自动并静默地将一堆字体移动到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())

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

Installing Fonts The font is already installed

所以,我正在研究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)

8 个答案:

答案 0 :(得分:4)

您可以使用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中撰写了有关此主题的一些信息。

答案 1 :(得分:3)

我遇到了同样的问题,并在另一个帖子中找到了这个问题,对我来说非常合适。

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

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

答案 2 :(得分:2)

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 Loop

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

删除现有

我正在考虑删除与我正在复制的字体同名的所有字体,然后复制该集。虽然那是残酷的。如果该字体无法删除,我相信还有另一个提示,因为它正在使用中。 叹息

答案 3 :(得分:2)

副本标志对我不起作用。我在安装字体脚本中设置了一个工作,用于检测&#34;安装字体&#34;窗口并发送{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。

答案 4 :(得分:1)

我看到了许多Unzip文件夹操作,但实际上没有人编写适合Fonts文件夹情况的解决方案。所以我写了自己的!事实证明,Fonts文件夹确实实现了Shell.Folder.CopyHere方法,但不承认为方法的第二个参数传递的任何重载。为什么?谁知道!我怀疑'The Old new Thing' Windows开发人员博客的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"

答案 5 :(得分: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)    
}

答案 6 :(得分:0)

@FoxDeploy的答案存在多个问题,这就是为什么它不起作用的原因。第一个问题是您还希望检查%USERPROFILE%中的Fonts文件夹,否则将出现确认对话框。第二个问题是您要避免在字体名称中使用'-'。

下面是固定版本,该示例从CodeFonts存储库中安装字体,例如:

$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"
}

答案 7 :(得分:-1)

我只是简单地使用+即

来实现这一点
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)
    }
}