用于Excel密码保护的Powershell测试

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

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

[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提示,转换脚本将被阻止,直到提示消失。你们可以等工作一段时间(比如10秒),若它并没有回来,就把它干掉

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


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

ExcelMonitor.ahk

SetTitleMatchMode, 2   # see [1]
Loop {
    WinWaitActive, <Set Excel Prompt Window Title Here>
    Run, taskkill /IM excel.exe /f
}
SetTitleMatchMode,2#参见[1]
环路{
WinWaitActive,
运行,taskkill/IM excel.exe/f
}
说明

在forever循环中,脚本等待Excel显示,然后终止它并继续等待另一个提示。这将使控件返回到您的高级脚本

在powershell脚本之前运行此ahk脚本,并在转换完成后将其关闭。您还可以安排powershell脚本在转换之前启动ahk脚本,然后将其关闭


[1] :

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

$sig = [Byte[]] (0x50,0x4b,0x03,0x04)
另一方面,加密的Office文档具有与所有Office文档相同的标准文件签名,如前5个版本。因此,这个代码片段显示了我如何从每个文档中读取sig,以检查并测试它是否未加密(上面有sig):


不过,Majkinator上面的答案也同样适用。

为了提高精度,提供的答案仅适用于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 !"
    }
}

$Excel.workbooks.open
呼叫停止并请求密码?@JoachimIsaksson:是的,它停止了。好的,在这里抛出想法。如果将虚拟密码传递给
open
,会发生什么情况?它还会提示还是抛出异常?@JoachimIsaksson:它抛出异常。所以我想我可以尝试用一个无效的密码打开它,然后捕获异常。我看不到任何其他明显的方法来使用API进行检查,但是我不是一个专家。谢谢你的想法。问题是,我有成千上万的电子表格要处理,即使并行运行,如果我必须为每个文件等待一个超时时间,也会花费很长时间。此外,从脚本打开具有密码保护的文档将打开Excel窗口并提示输入密码,即使使用visible选项false也是如此。因此,我会得到一堆屏幕不断弹出。我想我可以用一个虚拟机,但还是不太理想。我认为用错误的密码捕获异常可能会更好。请参阅我编辑的文章。当提示出现时,它将立即终止excel。老实说,如果是单次运行,需要多长时间并不重要;你可以通宵运行,非常好。感谢您共享此。TBH,我的解决方案还允许您在处理之前实际输入密码:)
# 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 !"
    }
}