Excel.Application: Microsoft Excel 无法访问文件 '[<filename>]' 可能有以下几种原因:
Excel.Application: Microsoft Excel cannot access the file '[<filename>]' There are several possible reasons:
我有一个有效的 PowerShell 脚本,它帮助我 运行 针对多个服务器的多个查询,并将每个输出保存在不同的 CSV 中,然后将它们合并到一个 Excel 文件中。
$Servers = get-content -Path "Servers.txt"
$DatabaseName ="master"
#$credential = Get-Credential #Prompt for user credentials
$secpasswd = ConvertTo-SecureString "MyPassword" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ("sa", $secpasswd)
$QueriesFolder = "Queries\"
$ResultFolder = "Results\"
ForEach($Server in $Servers)
{
$DateTime = (Get-Date).tostring("yyyy-MM-dd")
ForEach ($filename in get-childitem -path $QueriesFolder -filter "*.sql" | sort-object {if (($i = $_.BaseName -as [int])) {$i} else {$_}} )
{
$oresults = invoke-sqlcmd -ServerInstance $Server -Database $DatabaseName -Credential $credential -InputFile $filename.fullname
write-host "Executing $filename on $Server"
$BaseNameOnly = Get-Item $filename.fullname | Select-Object -ExpandProperty BaseName
$oresults | export-csv $ResultFolder$BaseNameOnly.csv -NoTypeInformation -Force
}
$All_CSVs = get-childitem -path $ResultFolder -filter "*.csv" | sort-object {if (($i = $_.BaseName -as [int])) {$i} else {$_}}
$Count_CSVs = $All_CSVs.Count
Write-Host "Detected the following CSV files: ($Count_CSVs)"
Write-Host " "$All_CSVs.Name"`n"
$ExcelApp = New-Object -ComObject Excel.Application
$ExcelApp.SheetsInNewWorkbook = $All_CSVs.Count
$output = "C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
if (Test-Path $output)
{
Remove-Item $output
Write-Host Removing: $output because it exists already
}
$xlsx = $ExcelApp.Workbooks.Add()
for($i=1;$i -le $Count_CSVs;$i++)
{
$worksheet = $xlsx.Worksheets.Item($i)
$worksheet.Name = $All_CSVs[$i-1].Name
$file = (Import-Csv $All_CSVs[$i-1].FullName)
$file | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip
$worksheet.Cells.Item(1).PasteSpecial()|out-null
}
$xlsx.SaveAs($output)
Write-Host Creating: $output
$ExcelApp.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsx) | Out-Null;
Write-Host "Closing all worksheet"
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelApp) | Out-Null;
Write-Host "Closing Excel"
[System.GC]::Collect();
[System.GC]::WaitForPendingFinalizers()
Remove-Item "$ResultFolder\*" -Include *.csv
Write-Host "Cleaning all *.csv"
Start-Sleep -Seconds 3
}
为了使这个脚本更具可移植性,我希望其中提到的所有路径都存储到一个变量中,然后连接起来。
但是一旦我改变:
$output = "C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
进入:
$output = $ResultFolder + $Server + " $DateTime.xlsx"
事情变得糟糕,我收到错误:
Microsoft Excel cannot access the file 'C:\Users\FrancescoM\Documents\Results[=16=]DC80000'.
There are several possible reasons:
• The file name or path does not exist.
• The file is being used by another program.
• The workbook you are trying to save has the same name as a currently open workbook.
At C:\Users\FrancescoM\Desktop\CSV\QueryLauncher.ps1:50 char:2
+ $xlsx.SaveAs($output)
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
我不明白,我想我把事情串联起来是对的。
我也按照这个Whosebug post添加了"C:\Windows\SysWOW64\config\systemprofile\desktop"后重启了电脑,但是问题还是没有解决。
可变路径怎么会把 Excel 搞得一团糟?
因为您没有在 $ResultFolder
变量中定义完整路径,它将使用当前工作目录进行扩展。
只要看看你想要的路径:
"C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
以及使用部分 $ResultFolder 变量生成的路径:
C:\Users\FrancescoM\Documents\Results[=11=]DC80000
由于您希望将输出文件放在桌面上的文件夹中,因此请将 $output
设置为
$output = Join-Path $([Environment]::GetFolderPath("Desktop")) "CSV\Results$Server $DateTime.xlsx"
编辑
根据您最后的评论,我了解到您希望输出位于名为 "Results" 的子文件夹中,该子文件夹位于脚本本身所在的文件夹内。
在那种情况下这样做:
# get the folder this script is running from
$ScriptFolder = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path $MyInvocation.MyCommand.Path }
# the following paths are relative to the path this script is in
$QueriesFolder = Join-Path -Path $ScriptFolder -ChildPath 'Queries'
$ResultFolder = Join-Path -Path $ScriptFolder -ChildPath 'Results'
# make sure the 'Results' folder exists; create if not
if (!(Test-Path -Path $ResultFolder -PathType Container)) {
New-Item -Path $ResultFolder -ItemType Directory | Out-Null
}
然后,当需要保存 xlsx 文件时,使用以下方法创建完整路径和文件名:
$output = Join-Path -Path $ResultFolder -ChildPath "$Server $DateTime.xlsx"
$xlsx.SaveAs($output)
P.S。我建议使用 Join-Path
cmdlet 组合文件路径或使用 [System.IO.Path]::Combine()
而不是像您使用此行那样将路径连接在一起:$oresults | export-csv $ResultFolder$BaseNameOnly.csv
。如果您忘记在第一个路径部分后缀反斜杠,使用后者可能会导致无法预料的路径名。
P.S.2 Excel 在 Tools->Options->General->Default File Location[=43] 中设置了自己的默认输出路径=] 并且不知道脚本的相对路径。这就是您应该使用完整路径和文件名保存的原因。
我有一个有效的 PowerShell 脚本,它帮助我 运行 针对多个服务器的多个查询,并将每个输出保存在不同的 CSV 中,然后将它们合并到一个 Excel 文件中。
$Servers = get-content -Path "Servers.txt"
$DatabaseName ="master"
#$credential = Get-Credential #Prompt for user credentials
$secpasswd = ConvertTo-SecureString "MyPassword" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ("sa", $secpasswd)
$QueriesFolder = "Queries\"
$ResultFolder = "Results\"
ForEach($Server in $Servers)
{
$DateTime = (Get-Date).tostring("yyyy-MM-dd")
ForEach ($filename in get-childitem -path $QueriesFolder -filter "*.sql" | sort-object {if (($i = $_.BaseName -as [int])) {$i} else {$_}} )
{
$oresults = invoke-sqlcmd -ServerInstance $Server -Database $DatabaseName -Credential $credential -InputFile $filename.fullname
write-host "Executing $filename on $Server"
$BaseNameOnly = Get-Item $filename.fullname | Select-Object -ExpandProperty BaseName
$oresults | export-csv $ResultFolder$BaseNameOnly.csv -NoTypeInformation -Force
}
$All_CSVs = get-childitem -path $ResultFolder -filter "*.csv" | sort-object {if (($i = $_.BaseName -as [int])) {$i} else {$_}}
$Count_CSVs = $All_CSVs.Count
Write-Host "Detected the following CSV files: ($Count_CSVs)"
Write-Host " "$All_CSVs.Name"`n"
$ExcelApp = New-Object -ComObject Excel.Application
$ExcelApp.SheetsInNewWorkbook = $All_CSVs.Count
$output = "C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
if (Test-Path $output)
{
Remove-Item $output
Write-Host Removing: $output because it exists already
}
$xlsx = $ExcelApp.Workbooks.Add()
for($i=1;$i -le $Count_CSVs;$i++)
{
$worksheet = $xlsx.Worksheets.Item($i)
$worksheet.Name = $All_CSVs[$i-1].Name
$file = (Import-Csv $All_CSVs[$i-1].FullName)
$file | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip
$worksheet.Cells.Item(1).PasteSpecial()|out-null
}
$xlsx.SaveAs($output)
Write-Host Creating: $output
$ExcelApp.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsx) | Out-Null;
Write-Host "Closing all worksheet"
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelApp) | Out-Null;
Write-Host "Closing Excel"
[System.GC]::Collect();
[System.GC]::WaitForPendingFinalizers()
Remove-Item "$ResultFolder\*" -Include *.csv
Write-Host "Cleaning all *.csv"
Start-Sleep -Seconds 3
}
为了使这个脚本更具可移植性,我希望其中提到的所有路径都存储到一个变量中,然后连接起来。 但是一旦我改变:
$output = "C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
进入:
$output = $ResultFolder + $Server + " $DateTime.xlsx"
事情变得糟糕,我收到错误:
Microsoft Excel cannot access the file 'C:\Users\FrancescoM\Documents\Results[=16=]DC80000'.
There are several possible reasons:
• The file name or path does not exist.
• The file is being used by another program.
• The workbook you are trying to save has the same name as a currently open workbook.
At C:\Users\FrancescoM\Desktop\CSV\QueryLauncher.ps1:50 char:2
+ $xlsx.SaveAs($output)
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
我不明白,我想我把事情串联起来是对的。
我也按照这个Whosebug post添加了"C:\Windows\SysWOW64\config\systemprofile\desktop"后重启了电脑,但是问题还是没有解决。
可变路径怎么会把 Excel 搞得一团糟?
因为您没有在 $ResultFolder
变量中定义完整路径,它将使用当前工作目录进行扩展。
只要看看你想要的路径:
"C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
以及使用部分 $ResultFolder 变量生成的路径:
C:\Users\FrancescoM\Documents\Results[=11=]DC80000
由于您希望将输出文件放在桌面上的文件夹中,因此请将 $output
设置为
$output = Join-Path $([Environment]::GetFolderPath("Desktop")) "CSV\Results$Server $DateTime.xlsx"
编辑
根据您最后的评论,我了解到您希望输出位于名为 "Results" 的子文件夹中,该子文件夹位于脚本本身所在的文件夹内。 在那种情况下这样做:
# get the folder this script is running from
$ScriptFolder = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path $MyInvocation.MyCommand.Path }
# the following paths are relative to the path this script is in
$QueriesFolder = Join-Path -Path $ScriptFolder -ChildPath 'Queries'
$ResultFolder = Join-Path -Path $ScriptFolder -ChildPath 'Results'
# make sure the 'Results' folder exists; create if not
if (!(Test-Path -Path $ResultFolder -PathType Container)) {
New-Item -Path $ResultFolder -ItemType Directory | Out-Null
}
然后,当需要保存 xlsx 文件时,使用以下方法创建完整路径和文件名:
$output = Join-Path -Path $ResultFolder -ChildPath "$Server $DateTime.xlsx"
$xlsx.SaveAs($output)
P.S。我建议使用 Join-Path
cmdlet 组合文件路径或使用 [System.IO.Path]::Combine()
而不是像您使用此行那样将路径连接在一起:$oresults | export-csv $ResultFolder$BaseNameOnly.csv
。如果您忘记在第一个路径部分后缀反斜杠,使用后者可能会导致无法预料的路径名。
P.S.2 Excel 在 Tools->Options->General->Default File Location[=43] 中设置了自己的默认输出路径=] 并且不知道脚本的相对路径。这就是您应该使用完整路径和文件名保存的原因。