【问题标题】:Shell.Application & errorsShell.Application & 错误
【发布时间】:2020-09-09 03:54:34
【问题描述】:

为了澄清,我特别询问如何在使用 Shell.Application 时处理错误。或者更重要的是,CAN 一个处理错误,如果是这样,如何处理。字体示例就是这样,我正在尝试解决的当前情况的示例。但问题仍然存在,在使用 Shell.Application 时是否可以处理(而不是避免、处理)错误?答案可能有一些微妙之处,即一般你可以,但特别是在字体示例中不是。情况似乎是这样,我倾向于认为 Shell.Application 是陈旧的、破碎的技术,根本不应该使用,因为它不能在所有用例中都健壮,对我来说,它在任何情况下都可能很脆弱用例。

我正在尝试改进我对 Shell.Application.CopyHere() 的使用,特别是用于安装字体。我希望做的是解决字体文件损坏或不是有效字体文件的偶尔错误。 据我所知,this 确实没有办法解决这个问题。对于显示的任何对话框,我可以使用参数值 16 以“全部是”来响应。也许可以克服错误,但没有返回代码,我无法记录由此产生的错误。在带有 Try/Catch 的 PowerShell 中使用 .CopyHere() 也不起作用。这只是微软刚刚接受失败失败的旧技术吗?还是我错过了解决问题的技术?

编辑:根据我提供的链接,我尝试了 1024 参数,如果发生错误,请不要显示用户界面。 像这样

$fontFolder.CopyHere($fontFilePath, 1024)

似乎没有按照它说的去做,因为我看到一个对话框说

Cannot install bogus.ttf
The file ... does not appear to be a valid font.

因此,不仅无法返回有意义的错误,而且错误的存在会中断脚本的执行并需要用户交互。呃。

编辑 2: 不是一个真正的最小代码示例,因为我的问题是可以做到这一点,甚至在如何之前。但这是我刚刚尝试过的。

fontFilePath = '\\px\Rollouts\Misc\Fonts\bogus.ttf'
$fontFolderPath = "$env:windir\Fonts"
$fontFolder = $(New-Object -ComObject:Shell.Application).Namespace($fontFolderPath)
$fontFolder.CopyHere($fontFilePath, 1024)

根据 1024 参数声称要做的事情,我希望这会失败,但也不会在对话框等待用户交互时停止处理。

另外,值得注意的是 bogus.ttf 只是一个以 TTF 扩展名重命名的空文本文件。所以,保证不会成功安装字体。

【问题讨论】:

  • “我希望做的是解决字体文件损坏或不是有效字体文件的偶尔错误” - 好吧,你想做什么?重试复制操作?跳过它?休息?
  • 您的问题需要改进,不仅是为了解决上述评论中提出的问题,还要提供您正在运行的代码的minimal reproducible example,并显示您正在尝试解决的问题地址,以及我们复制您的问题所需的调试信息。如果我们自己无法准确地解决问题,我们将无法帮助您解决问题。
  • @mathias-r-jessen 我想知道发生的错误,这样我就可以记录发生的错误,并跳过一些只有在正确安装字体时才会发生的后续任务。如果我可以像使用 try/catch 那样记录详细的错误,那就更好了,因为它有助于故障排除。但仅仅能够识别错误将是一个好的开始。并且无需用户确定抛出的错误对话框即可继续。
  • @Gordon 请提供您已经尝试处理此案例的内容,否则该问题被视为 StackOverflow 的题外话。这就是提供minimal reproducible example 的意思。
  • @bender-the-greatest 我添加了我目前正在尝试的代码,但 MRE 示例向我提出了一个问题。提出“X 能做到吗?”这样的问题被认为是不恰当的。我希望 Code Review 需要代码,但我认为 Stack Overflow 对更高级别的问题开放,例如“依赖注入的优缺点是什么”,没有代码,甚至没有指定的语言,但(至少对我而言)在更高层次上进行有用的讨论仍有空间。当前的帖子不是一个例子,但这是我想到的一个问题。

标签: powershell


【解决方案1】:

Fonts 文件夹虽然实现了Folder.CopyHere,但似乎没有评估第二个参数中传递的标志。似乎没有任何其他标准实用程序可以以编程方式安装字体,或以非交互方式验证 TTF 的完整性。

因此,我们可以选择使用 Win32 API 滚动我们自己的字体注册。基本上,您必须将字体复制到 Fonts 文件夹:

# For .NET versions earlier than 4.0, hardcode to C:\Windows\Fonts
# Fonts special folder is new in 4.0
$fontDir = [System.Environment]::GetFolderPath([System.Environment.SpecialFolder]::Fonts)
Copy-Item C:\Path\To\Font.ttf "${fontDir}\Font.ttf"

下一个调用并不是绝对必要的,因为它只是将字体临时添加到您的用户会话中,但它确实起到了完整性检查的作用,以查看字体是否有效并且可以导入。您需要从 Win32 API P/Invoke AddFontResource

Add-Type -Name Gdi32 -Namespace Win32 -MemberDefinition @"
  [System.Runtime.InteropServices.DllImport("gdi32.dll")]
  public static extern int AddFontResource(string lpszFilename);
"@

# Returns 1 on success or 0 on failure, if you want your error checking here
[Win32.Gdi32]::AddFontResource("${fontDir}\FontFileName.ttf")

然后将其注册到以下注册表项。这块是持久化你复制到$fontDir的字体所必需的:

New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' -Name "FontName" -Value "FontFileName.ttf"

您可以使用PrivateFontCollection 类和Families[x].Name 属性以编程方式获取FontName,其中x 是您想要为其命名的集合中字体的索引。


为了解决您的编辑问题,在处理Shell.Application 中的错误时,不会有“一刀切”的方法。避免使用Shell.Application 的原因有多种,例如:

  • API 采用遗留行为,例如使用模式对话框报告错误
  • Windows 标准的实现不一致,例如字体忽略了记录的 CopyFile 标志
  • 在非交互式会话中不可用

前两点是您问题中的示例所遇到的情况。第三个在许多自动化场景中发挥作用,尤其是在使用非交互式服务帐户执行命令时。在极少数情况下,只能通过Shell.Application 完成某些事情,因此,如果有替代 API,几乎总是最好避免使用它。您的字体安装方案就是一个很好的例子。

【讨论】:

  • 嗯。我一直在走这样的道路,使用$fontFamilies = $fontCollection.Families | Foreach-Object {$_.Name} 来构建我的字体系列列表。但这始终是按字母顺序排列的,而手动安装并不总是如此。是时候看看我是否可以按索引排序并获得更好的匹配。我不知道这很重要,但如果可能的话,我更喜欢让我的自动化结果与手动过程完全相同。
  • Sitka.ttc 是预安装的名称与我上面的代码生成的名称不匹配的示例。
  • 我不明白这个问题。您担心字体可能会按照字体系列名称的字母顺序安装?
  • 我也不确定订单。我找不到任何确定的方式。在测试中我没有看到它的重要性,但它是 Windows,谁知道以后会出现什么。这一点,以及关于非交互式会话的部分达成了交易,因为这是我以后想添加的内容。
  • 我的市场是架构公司,在那里很难获得组策略专业知识,并且需要完成大量其他安装/自定义任务,而这些任务通常是 GPO 无法完成的因为它必须在用户上下文中完成。基于 Powershell 和 XML 的数据文件让我可以在一个地方处理所有事情。
猜你喜欢
  • 2014-04-27
  • 1970-01-01
  • 2018-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多