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