【问题标题】:Powershell Test for Excel Password ProtectionExcel 密码保护的 Powershell 测试
【发布时间】:2016-04-11 17:14:51
【问题描述】:

我有一个脚本,用于转换和密码保护许多 Excel 文档。较旧的文档是 Excel 2003 格式,我可以很好地转换/密码保护它们。当我看到更新的文档时,这些文件是 Excel 2010 格式,因此只需要密码保护。我正在尝试找到一种方法来检查 xlsx 文件是否已经受密码保护,因此可以跳过(可能是已经处理过的 2003 文件)。如果我打开文件,因为它有密码,那么即使我将可见属性设置为 false,excel 也会弹出并在继续之前询问该密码。我需要一种自动检查的方法,因为有很多文件要检查。这是我到目前为止的代码:

[cmdletbinding()]
param (
    [parameter(mandatory=$true)][string]$Path,
    [parameter(mandatory=$false)][switch]$Visible,
    [parameter(mandatory=$false)][string]$ToFolder,
    [parameter(mandatory=$false)][string]$Password,
    [parameter(mandatory=$false)][switch]$Force
)
begin {
    Add-Type -AssemblyName Microsoft.Office.Interop.Excel
    $xlFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlWorkbookDefault
    Write-Verbose 'Opening Excel COM object.'
    $Excel = New-Object -ComObject excel.application
    if ($Visible -eq $true) {
      $Excel.visible = $true
    } else {
      $Excel.visible = $false
      $Excel.DisplayAlerts = $false
      $Excel.ScreenUpdating = $false
      $Excel.UserControl = $false
      $Excel.Interactive = $false
    }
    $filetype = "*xls"
} process {
    if (Test-Path -Path $Path) {
        Get-ChildItem -Path $Path -Include '*.xls' -recurse | ForEach-Object {
            Write-Verbose "Processing $($_.Basename)"
            if ($ToFolder -ne '') {
                $FilePath = Join-Path $ToFolder $_.BaseName
                $FilePath += ".xlsx"
            } else {
                $FilePath = ($_.fullname).substring(0, ($_.FullName).lastindexOf("."))
                $FilePath += ".xlsx"
            }
            if (!(Test-Path $FilePath) -Or $Force) {
              Write-Verbose "Opening $($_.Basename)"
              $WorkBook = $Excel.workbooks.open($_.fullname)
              Write-Verbose "Saving $($_.Basename) to $FilePath with password $Password"
              $WorkBook.saveas($FilePath, $xlFixedFormat, $Password)
              Write-Verbose "Closing $($_.Basename)"
              $WorkBook.close()
            } else {
              Write-Verbose "$($_.Basename) already converted."
            }
        }
    } else {
        return 'No path provided or access has been denied.'
    }
} end {
    Write-Verbose 'Closing Excel'
    $Excel.Quit()
    $Excel = $null
    [gc]::collect()
    [gc]::WaitForPendingFinalizers()
}

【问题讨论】:

  • $Excel.workbooks.open 呼叫停止并要求输入密码?
  • @JoachimIsaksson:是的,这就是它停止的地方。
  • 好的,这里只是抛出一些想法。如果您将虚拟密码传递给open,会发生什么?还会提示还是抛出异常?
  • @JoachimIsaksson:它会引发异常。所以我想我可以尝试使用无效密码打开它,然后捕获异常。
  • 我看不到任何其他明显的方法可以使用 API 进行检查,但我并不完全是专家。

标签: excel powershell


【解决方案1】:

您可以使用另一个脚本在作业中执行转换。如果 Excel 提示转换脚本将阻塞,直到提示消失。您可以等待工作一段时间(例如 10 秒),如果它没有返回,请杀死它。

如果您同时启动多个作业(假设这可以使用 Excel),脚本将始终转换一些文件,同时等待其他文件。


串行解决方案也可以使用等待 Excel 提示符的 Autohotkey 脚本。当提示显示时,您可以终止将控制权返回给 powershell 脚本的 excel(您可以忽略以这种方式出现的任何错误),然后可以继续迭代。 AHK 脚本只有几行,例如:

ExcelMonitor.ahk

SetTitleMatchMode, 2   # see [1]
Loop {
    WinWaitActive, <Set Excel Prompt Window Title Here>
    Run, taskkill /IM excel.exe /f
}

解释

在永远循环中,脚本等待 Excel 显示,然后将其终止并继续等待另一个提示。这会将控件返回给您的豪华脚本。

在你的 powershell 脚本之前运行这个 ahk 脚本,并在转换完成后关闭它。您还可以安排您的 powershell 脚本在转换之前启动 ahk 脚本并在之后关闭它。

[1]:https://autohotkey.com/docs/commands/SetTitleMatchMode.htm#Parameters

【讨论】:

  • 感谢您的想法。这样做的问题是,我有成千上万的电子表格要处理,即使并行运行它,如果我不得不等待每个文件的超时,也将花费很长时间。此外,通过脚本打开具有密码保护的文档将打开 Excel 窗口并提示输入密码,即使可见选项为 false。因此,我会不断弹出一堆屏幕。我想我可以使用虚拟机,但同样不太理想。我认为捕获错误密码的异常可能会更好。
  • 查看我编辑的帖子。一旦出现提示,它将杀死excel。老实说,如果它是 1 次运行操作需要多长时间并不重要;你可以在一夜之间运行它。
【解决方案2】:

抱歉,我忘了来更新这个。对于任何展望未来的人,我最终要做的是将文件作为数据流打开并检查文件签名(文件的前 4 个字节)。标准 2010 Office 文档(.xlsx、.docx 等)实际上是 Zip 文件并具有标准 zip 文件签名。这个 Powershell 代码给了我一个带有那个 sig 的字节数组:

$sig = [Byte[]] (0x50,0x4b,0x03,0x04)

另一方面,加密的 Office 文档具有所有 Office 文档通用的标准文件签名,例如最近 5 个版本。因此,这段代码 sn-p 显示了我如何从每个文档中读取 sig 以检查然后测试它是否未加密(上面有 sig):

$bytes = get-content $_.fullname -encoding byte -total 4
if (@(compare-object $sig $bytes -sync 0).length -eq 0) {
  # process unencrypted file
}

majkinetor 上面的回答也可以。

【讨论】:

  • 非常好。感谢分享。
  • TBH,我的解决方案还允许您在处理之前实际输入密码:)
【解决方案3】:

为了增加精度,提供的答案仅适用于 2007+ 文件。 要为 97-2003 文件实现这一点,您可以试试这个:

# Look for 97-2003 files
If ($excelSheet.Extension.Length -eq 4)
{
    # Get Excel sheet file header
    $header = Get-Content $excelSheet.FullName -Encoding Unicode -Total 1

    # Check if Excel file is password protected
    If (($header -ne $null) -and ($header -notmatch "Microsoft Enhanced Cryptographic Provider"))
    {
        Write-Host = "Excel sheet : $excelSheet is unencrypted."
    }
    Else
    {
        Write-Host "Excel sheet : $excelSheet is password protected !"
    }
}

【讨论】:

    猜你喜欢
    • 2021-02-10
    • 1970-01-01
    • 2010-11-18
    • 2019-09-18
    • 2011-02-06
    • 2017-11-09
    • 2010-10-29
    • 2013-11-16
    • 1970-01-01
    相关资源
    最近更新 更多