Excel Macro file locked after script completion - excel

I have a peculiar issue. I've written a script that pulls some properties of files in a destination, generates three 'reports' and comines then i a single xlsx file. After that a macro is applied to two of worksheets present and it works like a charm. The issue is that in order to use my macro I had to open personal.xlsb as a second workbook and even though I colse it with .Close() it remains locked for editing. Any ideas?
Code used
$excel = New-Object -com excel.application
$excel.SheetsInNewWorkbook = 2
$excel.displayalerts = $false
$workbook = $excel.workbooks.add()
$worksheet = $workbook.worksheets
$first = $worksheet.item(1)
$first.name = "First"
$second = $worksheet.item(2)
$second.name = "Second"
$wbPersonalXLSB = $excel.workbooks.open("$env:USERPROFILE\Application Data\Microsoft\Excel\XLSTART\PERSONAL.XLSB")
$path = "PATH"
$GCI = GCI $path -file -Recurse -ErrorAction SilentlyContinue
$hashes =
$GCI|
Get-FileHash -Algorithm MD5 -ErrorAction SilentlyContinue|
select Algorithm, Hash, #{l="File";e={$_.Path.split("\")|select -Last 1}},#{l="Path";e={$_.Path.Substring(0,$_.Path.LastIndexof('\'))}}, #{l="Link";e={$_.Path}}|
Group -property "Hash"|
Where {$_.Count -ge 2}|
select -Expand Group
$hashes|ConvertTo-Csv -NoTypeInformation -Delimiter "`t"|clip
$first.Cells.Item(1).pastespecial()|out-null
$first.activate()
$excel.run("PERSONAL.XLSB!Empty_Row_Dupes")
$filenames =
$GCI|
Select #{l='File';e={$_.PSChildName}}, #{l='Compare Filename';e={$_.BaseName.replace('_','*').replace(' ','*').replace('-','*')}}, Directory, FullName, #{l="Extension";e={$_.Extension}}|
group -Property 'Compare Filename'|
Where {#($_.Group.Extension |Sort -Unique).Count -ge 2}|
select -expand Group
$filenames|ConvertTo-Csv -NoTypeInformation -Delimiter "`t"|clip
$second.Cells.Item(1).pastespecial()|out-null
$second.Activate()
$excel.run("PERSONAL.XLSB!Empty_Row_Dupes")
$third = $worksheet.add([System.Reflection.Missing]::Value,$worksheet.Item($worksheet.count))
$third.Name = "Third"
$zero_length =
$GCI|
? {$_.Length -eq 0}|
Select #{l='File';e={$_.PSChildName}}, Length, Directory, FullName
$zero_length|ConvertTo-Csv -NoTypeInformation -Delimiter "`t"|clip
$third.cells.item(1).pastespecial()|out-null
$third.range("A1:D1").Interior.Color = 8454080
$wbPersonalXLSB.Close()
$save = "XLSX_PATH"
$workbook.saveas($save)
$workbook.close()
$excel.quit()
while( [System.Runtime.Interopservices.Marshal]::ReleaseComObject($first)){}
while( [System.Runtime.Interopservices.Marshal]::ReleaseComObject($second)){}
while( [System.Runtime.Interopservices.Marshal]::ReleaseComObject($third)){}
while( [System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet)){}
while( [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)){}
while( [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)){}

The problem is that your code to release the COM object is missing to call to [System.GC]::Collect(), so these objects may linger for longer than needed in memory, keeping the files locked.
Also, don't forget to release $wbPersonalXLSB as well..
End your code with:
$excel.quit()
# release the COM objects from memory, so the files lose their lock
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wbPersonalXLSB)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($first)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($second)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($third)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

As ridiculous as it may sound, changing personal.xlsb to personal_2.xlsb
and updating code accordingly seems to have resolved the issue.

Related

Clipboard access denied, csv to xlsx conversion

I have a most peculiar issue with clipboard. Below is the code I've written that in essence gathers info about many many thousands of files, compares hashes, compares filnames and lists zero length files and then writes them all to an xlsx file in separate worksheets.
Everything works fine if the scope is relatively small (i.e. ~20k files), but if the scope becomes greater (i.e. ~200k files) I get an error Clipboard access denied. Initially I believed that the issue was clipboard capacity, as I use clipboard and .pastespecial method. But when intermediate csv files are created and their contents copied, everything seems to work fine. Ant thoughts?
Original script
$excel = New-Object -com excel.application
$excel.SheetsInNewWorkbook = 2
$excel.displayalerts = $false
$workbook = $excel.workbooks.add()
$worksheet = $workbook.worksheets
$True_Dups = $worksheet.item(1)
$True_Dups.name = "True Duplicates"
$Dupes = $worksheet.item(2)
$Dupes.name = "Name Duplicates"
$wbPersonalXLSB = $excel.workbooks.open("$env:USERPROFILE\Application Data\Microsoft\Excel\XLSTART\PERSONAL_2.XLSB")
$path = "PATH"
$GCI = GCI $path -file -Recurse -Ea 0
$hashes =
$GCI|
Get-FileHash -Algorithm MD5 -Ea 0|
select Algorithm, Hash, #{l="File";e={$_.Path.split("\")|select -Last 1}},#{l="Path";e={$_.Path.Substring(0,$_.Path.LastIndexof('\'))}}, #{l="Link";e={$_.Path}}|
Group -property "Hash"|
Where {$_.Count -ge 2}|
select -Expand Group
$hashes|ConvertTo-Csv -NoTypeInformation -Delimiter "`t"|scb
$True_Dups.Cells.Item(1).pastespecial()|out-null
$True_Dups.activate()
$excel.run("PERSONAL_2.XLSB!Empty_Row_Dupes")
$filenames =
$GCI|
Select #{l='File';e={$_.PSChildName}}, #{l='Compare Filename';e={$_.BaseName.replace('_','*').replace(' ','*').replace('-','*')}}, Directory, FullName, #{l="Extension";e={$_.Extension}}|
group -Property 'Compare Filename'|
Where {#($_.Group.Extension |Sort -Unique).Count -ge 2}|
select -expand Group
$filenames|ConvertTo-Csv -NoTypeInformation -Delimiter "`t"|scb
$Dupes.Cells.Item(1).pastespecial()|out-null
$Dupes.Activate()
$excel.run("PERSONAL_2.XLSB!Empty_Row_Dupes")
$wbPersonalXLSB.Close()
$Empty = $worksheet.add([System.Reflection.Missing]::Value,$worksheet.Item($worksheet.count))
$Empty.Name = "Zero Lenght"
$zero_length =
$GCI|
? {$_.Length -eq 0}|
Select #{l='File';e={$_.PSChildName}}, Length, Directory, FullName
$zero_length|ConvertTo-Csv -NoTypeInformation -Delimiter "`t"|scb
$zero_length.cells.item(1).pastespecial()|out-null
$zero_length.range("A1:D1").Interior.Color = 8454080
$save = "CSV_PATH"
$workbook.saveas($save)
$workbook.close()
$excel.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)|out-null
Remove-Variable excel
Interdemiate CSVs added
$start = Get-Date
$path = "Path"
$GCI = GCI $path -file -Recurse -Ea 0
$hashes =
$GCI|
Get-FileHash -Algorithm MD5 -Ea 0|
select Algorithm, Hash, #{l="File";e={$_.Path.split("\")|select -Last 1}},#{l="Path";e={$_.Path.Substring(0,$_.Path.LastIndexof('\'))}}, #{l="Link";e={$_.Path}}|
Group -property "Hash"|
Where {$_.Count -ge 2}|
select -Expand Group
$hashes|export-Csv "PATH_1 csv" -NoTypeInformation
$filenames =
$GCI|
Select #{l='File';e={$_.PSChildName}}, #{l='Compare Filename';e={$_.BaseName.replace('_','*').replace(' ','*').replace('-','*')}}, Directory, FullName, #{l="Extension";e={$_.Extension}}|
group -Property 'Compare Filename'|
Where {#($_.Group.Extension |Sort -Unique).Count -ge 2}|
select -expand Group
$filenames|export-Csv "PATH_2.csv" -NoTypeInformation
$zero_length =
$GCI|
? {$_.Length -eq 0}|
Select #{l='File';e={$_.PSChildName}}, Length, Directory, FullName
$zero_length|export-Csv "PATH_3.csv" -NoTypeInformation
$span = ((get-date) - $start).ToString("hh\:mm\:ss")
Write "Span lasted $span"
and conversion script (credit goes to the original creator linked at the end)
$path = "CSV_FOLDER"
$csvs = Get-ChildItem $path -filter *.csv
$y = $csvs.Count
Write-Host "Detected the following CSV files: ($y)"
Write-Host " "$csvs.Name"`n"
$outputfilename = "Final Registry Results"
Write-Host Creating: $outputfilename
$excelapp = New-Object -ComObject Excel.Application
$excelapp.SheetsInNewWorkbook = $csvs.Count
$xlsx = $excelapp.Workbooks.Add()
for($i=1;$i -le $y;$i++) {
$worksheet = $xlsx.Worksheets.Item($i)
$worksheet.Name = $csvs[$i-1].Name
$file = (Import-Csv $csvs[$i-1].FullName)
$file | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip
$worksheet.Cells.Item(1).PasteSpecial()|out-null
}
$output = "XLSX_OUTPUT"
$xlsx.SaveAs($output)
$excelapp.Quit()
Merging CSV Files into a XLSX with Tabs

Powershell - Creating Excel Workbook - Getting "Insufficient memory to continue the execution of the program"

I'm trying to create an Excel workbook, then populate the cells with data found from searching many txt files.
I read a file and extract all comments AFTER I find "IDENTIFICATION DIVISION" and BEFORE I find "ENVIRONMENT DIVISION"
I then populate two cells in my excel workbook. cell one if the file and cell two is the comments extracted.
I have 256GB of memory on the work server. less than %5 is being used before Powershell throws the memory error.
Can anyone see where I'm going wrong?
Thanks,
-Ron
$excel = New-Object -ComObject excel.application
$excel.visible = $False
$workbook = $excel.Workbooks.Add()
$diskSpacewksht= $workbook.Worksheets.Item(1)
$diskSpacewksht.Name = "XXXXX_Desc"
$col1=1
$diskSpacewksht.Cells.Item(1,1) = 'Program'
$diskSpacewksht.Cells.Item(1,2) = 'Description'
$CBLFileList = Get-ChildItem -Path 'C:\XXXXX\XXXXX' -Filter '*.cbl' -File -Recurse
$Flowerbox = #()
ForEach($CBLFile in $CBLFileList) {
$treat = $false
Write-Host "Processing ... $CBLFile" -foregroundcolor green
Get-content -Path $CBLFile.FullName |
ForEach-Object {
if ($_ -match 'IDENTIFICATION DIVISION') {
# Write-Host "Match IDENTIFICATION DIVISION" -foregroundcolor green
$treat = $true
}
if ($_ -match 'ENVIRONMENT DIVISION') {
# Write-Host "Match ENVIRONMENT DIVISION" -foregroundcolor green
$col1++
$diskSpacewksht.Cells.Item($col1,1) = $CBLFile.Name
$diskSpacewksht.Cells.Item($col1,2) = [String]$Flowerbox
$Flowerbox = #()
continue
}
if ($treat) {
if ($_ -match '\*(.{62})') {
Foreach-Object {$Flowerbox += $matches[1] + "`r`n"}
$treat = $false
}
}
}
}
$excel.DisplayAlerts = 'False'
$ext=".xlsx"
$path="C:\Desc.txt"
$workbook.SaveAs($path)
$workbook.Close
$excel.DisplayAlerts = 'False'
$excel.Quit()
Not knowing what the contents of the .CBL files could be, I would suggest not to try and do all of this using an Excel COM object, but create a CSV file instead to make things a lot easier.
When finished, you can simply open that csv file in Excel.
# create a List object to collect the 'flowerbox' strings in
$Flowerbox = [System.Collections.Generic.List[string]]::new()
$treat = $false
# get a list of the .cbl files and loop through. Collect all output in variable $result
$CBLFileList = Get-ChildItem -Path 'C:\XXXXX\XXXXX' -Filter '*.cbl' -File -Recurse
$result = foreach ($CBLFile in $CBLFileList) {
Write-Host "Processing ... $($CBLFile.FullName)" -ForegroundColor Green
# using switch -File is an extremely fast way of testing a file line by line.
# instead of '-Regex' you can also do '-WildCard', but then add asterikses around the strings
switch -Regex -File $CBLFile.FullName {
'IDENTIFICATION DIVISION' {
# start collecting Flowerbox lines from here
$treat = $true
}
'ENVIRONMENT DIVISION' {
# stop colecting Flowerbox lines and output what we already have
# output an object with the two properties you need
[PsCustomObject]#{
Program = $CBLFile.Name # or $CBLFile.FullName
Description = $Flowerbox -join [environment]::NewLine
}
$Flowerbox.Clear() # empty the list for the next run
$treat = $false
}
default {
# as I have no idea what these lines may look like, I have to
# assume your regex '\*(.{62})' is correct..
if ($treat -and ($_ -match '\*(.{62})')) {
$Flowerbox.Add($Matches[1])
}
}
}
}
# now you have everything in an array of PSObjects so you can save that as Csv
$result | Export-Csv -Path 'C:\Desc.csv' -UseCulture -NoTypeInformation
Parameter -UseCulture ensures you can double-click the file so it will open correctly in your Excel
You can also create an Excel file from this csv programmatically like:
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$workbook = $excel.Workbooks.Open('C:\Desc.csv')
$worksheet = $workbook.Worksheets.Item(1)
$worksheet.Name = "XXXXX_Desc"
# save as .xlsx
# 51 ==> [Microsoft.Office.Interop.Excel.XlFileFormat]::xlWorkbookDefault
# see: https://learn.microsoft.com/en-us/office/vba/api/excel.xlfileformat
$workbook.SaveAs('C:\Desc.xlsx', 51)
# quit Excel and remove all used COM objects from memory
$excel.Quit()
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

How to use powershell to select range and dump that to csv file

Actually, this is a version of question here:
How to use powershell to select and copy columns and rows in which data is present in new workbook.
The goal is to grab certain columns from multiple Excel workbooks and dump everything to one csv file. Columns are always the same.
I'm doing that manually:
$xl = New-Object -ComObject Excel.Application
$xl.Visible = $false
$xl.DisplayAlerts = $false
$counter = 0
$input_folder = "C:\Users\user\Documents\excelfiles"
$output_folder = "C:\Users\user\Documents\csvdump"
Get-ChildItem $input_folder -File |
Foreach-Object {
$counter++
$wb = $xl.Workbooks.Open($_.FullName, 0, 1, 5, "")
try {
$ws = $wb.Worksheets.item('Calls') # => This specific worksheet
$rowMax = ($ws.UsedRange.Rows).count
for ($i=1; $i -le $rowMax-1; $i++) {
$newRow = New-Object -Type PSObject -Property #{
'Type' = $ws.Cells.Item(1+$i,1).text
'Direction' = $ws.Cells.Item(1+$i,2).text
'From' = $ws.Cells.Item(1+$i,3).text
'To' = $ws.Cells.Item(1+$i,4).text
}
$newRow | Export-Csv -Path $("$output_folder\$ESO_Output") -Append -noType -Force
}
}
} catch {
Write-host "No such workbook" -ForegroundColor Red
# Return
}
}
Question:
This works, but is extremely slow because Excel has to select every cell, copy that, then Powershell has to create array and save row by row in output csv file.
Is there a method to select a range in Excel (number of columns times ($ws.UsedRange.Rows).count), cut header line and just append this range (array?) to csv file to make everything much faster?
So that's the final solution
Script is 22 times faster!!! than original solution.
Hope somebody will find that useful :)
PasteSpecial is to filter out empty rows. There is no need to save them into csv
$xl = New-Object -ComObject Excel.Application
$xl.Visible = $false
$xl.DisplayAlerts = $false
$counter = 0
$input_folder = "C:\Users\user\Documents\excelfiles"
$output_folder = "C:\Users\user\Documents\csvdump"
Get-ChildItem $input_folder -File |
Foreach-Object {
$counter++
try {
$new_ws1 = $wb.Worksheets.add()
$ws = $wb.Worksheets.item('Calls')
$rowMax = ($ws.UsedRange.Rows).count
$range = $ws.Range("A1:O$rowMax")
$x = $range.copy()
$y = $new_ws1.Range("A1:O$rowMax").PasteSpecial([System.Type]::Missing,[System.Type]::Missing,$true,$false)
$wb.SaveAs("$($output_folder)\$($_.Basename)",[Microsoft.Office.Interop.Excel.XlFileFormat]::xlCSVWindows)
} catch {
Write-host "No such workbook" -ForegroundColor Red
# Return
}
}
$xl.Quit()
Part above will generate a bunch of csv files.
Part below will read these files in separate loop and combine them together into one.
-exclude is an array of something I want to omit
Remove-Item to remove temporary files
Answer below is based on this post: https://stackoverflow.com/a/27893253/6190661
$getFirstLine = $true
Get-ChildItem "$output_folder\*.csv" -exclude $excluded | foreach {
$filePath = $_
$lines = Get-Content $filePath
$linesToWrite = switch($getFirstLine) {
$true {$lines}
$false {$lines | Select -Skip 1}
}
$getFirstLine = $false
Add-Content "$($output_folder)\MERGED_CSV_FILE.csv" $linesToWrite
Remove-Item $_.FullName
}

Powershell, close Excel

I have the following code and it works perfectly except it's not closing Excel properly. It's leaving an Excel process running.
Is there a way to close Excel properly without killing the process?
Since i'm using other Excel files while running this script i can not kill all active Excel processes.
I think i tried everything i found online.
$WorkDir = "D:\Test\QR_ES\RG_Temp"
$BGDir = "D:\Test\QR_ES\3_BG"
$File = "D:\Test\QR_ES\4_Adr_Excel\KD_eMail.xlsx"
$SentDir = "D:\Test\QR_ES\RG_Temp\Sent\Dunning"
chdir $WorkDir
$firstPageList = Get-ChildItem "$WorkDir\1*.pdf" -File -Name
ForEach ($firstPage in $firstPageList)
{
$secondPage = "$BGDir\BG_RG.pdf"
$output = "Dunn-$firstPage"
invoke-command {pdftk $firstPage background $secondPage output $output}}
del 1*.pdf
gci $WorkDir\Dunn-*.pdf | rename-item -newname {$_.Name.Substring(5)} -Force
$Excel = New-Object -ComObject Excel.Application
$Excel.visible = $false
$Workbook = $Excel.workbooks.open($file)
$DunnList = Get-ChildItem "$WorkDir\1*.pdf" -File -Name
ForEach ($Dunn in $DunnList)
{
$Worksheets = $Workbooks.worksheets
$Worksheet = $Workbook.Worksheets.Item("KD_eMail")
$Range = $Worksheet.Range("A1").EntireColumn
$DunnSearch = $Dunn.Substring(0,5)
$SearchString = $DunnSearch
$Search = $Range.find($SearchString)
$Recipient = $Worksheet.Cells.Item($Search.Row, $Search.Column + 1)
$Msg = "<span style='font-family:Calibri;font-size:12pt;'>Test</span>"
$Outlook = New-Object -ComObject Outlook.Application
$namespace = $Outlook.GetNameSpace("MAPI")
$namespace.Logon($null, $null, $false, $true)
$EmailFrom = ('test#test.com')
$account = $outlook.Session.Accounts.Item($EmailFrom)
$Mail = $Outlook.CreateItem(0)
$Mail.HTMLBody = $Msg
$Mail.Subject = "OP - $SearchString"
$Mail.To = $Recipient
function Invoke-SetProperty {
param(
[__ComObject] $Object,
[String] $Property,
$Value
)
[Void] $Object.GetType().InvokeMember($Property,"SetProperty",$NULL,$Object,$Value)
}
Invoke-SetProperty -Object $mail -Property "SendUsingAccount" -Value $account
$Mail.Attachments.Add("$WorkDir\$Dunn")
$Mail.Save()
$Mail.close(1)
$Mail.Send()}}
$workbook.close($false)
$Excel.Quit()
chdir $WorkDir
del 1*.pdf
See this post:
https://stackoverflow.com/a/35955339/5329137
which is not accepted as an answer, but I believe is the full, correct way to close Excel.
This is what did it for me:
$FilePID = (Get-Process -name Excel | Where-Object { $_.MainWindowTitle -like 'FileName.xlsx*' }).Id
$Workbook.Save()
$Workbook.close($false)
Stop-Process $FilePID
Elaborating on #ASD's answer, since the MainWindowTitle doesn't (always) include the file suffix (.xlsx) you may have to strip that when comparing it to the filename. I'm using -replace to use a Regex match of everything before the last dot.
$excelPID = (Get-Process -name Excel | Where-Object { $_.MainWindowTitle -eq $fileName -replace '\.[^.]*$', '' }).Id
$workbook.Close()
Stop-Process $excelPID

Rename multiple worksheet and multiple files with powershell

Sorry for my bad english I'm French.
I'm new at Powershell and I wanted to rename all of my worksheet in multiple files.
So far i got that code that rename all files in a directory
Function Rename()
{
$path = Get-Location
$files = Get-ChildItem
$counter = 1
foreach($file in $files)
{
Rename-Item $file.FullName "$counter" + ".xlsx"
$counter++
}
}
Then I tried on a specific file to rename all of the sheets that are inside but it doesn't work. The loop works only one time while it has 4 worksheets.
Function RenameTab ($ExcelFileName)
{
#Emplacement du fichier
$excelFile = "C:\Users\Donosaure\Documents\Magister\" + $excelFileName + ".xlsx"
#Ouverture d'excel
$xldoc = New-Object -ComObject "Excel.Application"
#Message de confirmation
$xldoc.Visible = $false
$xldoc.DisplayAlerts = $false
#Ouverture du fichier
$workbook = $xldoc.Workbooks.Open($excelFile)
$inc = 1
$i=1
foreach ($worksheet in $workbook.Worksheets.count)
{
$worksheet = $workbook.Sheets.Item($i)
$worksheet.Name = $inc
$inc++
$i++
$workbook.SaveAs("C:\Users\Donosaure\Documents\Magister\1.xlsx")
$workbook.Close()
}
$xldoc.Quit()
}
RenameTab("Magister")
Can somebody help me ?
Thanks for your time
A few remarks about your code:
Parameters sent to a function in PowerShell are separated by space, you should not use brackets around them as in RenameTab("Magister")
When using COM objects, always make sure you release them from memory when done, otherwise they will linger in memory and if you run this again and again, you will run out of resources otherwise
Please check for Excel Worksheets Naming Convention, so you do not create worksheet names with invalid characters. At the moment, that is not the case, but you never know how this evolves.
Please use the PowerShell function 'Verb-Noun' naming convention for functions you create.
Below should do what you want:
Function Rename-ExcelTabs ($ExcelFileName) {
#Emplacement du fichier
$excelFile = "C:\Users\Donosaure\Documents\Magister\" + $excelFileName + ".xlsx"
#Ouverture d'excel
$xldoc = New-Object -ComObject "Excel.Application"
#Message de confirmation
$xldoc.Visible = $false
$xldoc.DisplayAlerts = $false
#Ouverture du fichier
$workbook = $xldoc.Workbooks.Open($excelFile)
for ($i = 1; $i -le $workbook.Worksheets.Count; $i++) {
$workbook.Sheets.Item($i).Name = $i
}
$workbook.SaveAs("C:\Users\Donosaure\Documents\Magister\" + $excelFileName + "_1.xlsx")
$workbook.Close()
$xldoc.Quit()
# clean-up used COM objects
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($xldoc)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
Rename-ExcelTabs "Magister"
As per your comment, the function could be rewritten to not only change the tab names in one Excel file, but process all .xlsx files inside a folder and rename these files aswell.
One way would be do remove the original file after the tabs have been renamed and a new file is created with $workbook.SaveAs(), as in the code above.
The following function does this by renaming the file first and next change the tab names in it.
function Rename-ExcelTabsAndFiles {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[ValidateScript({Test-Path -Path $_ -PathType Container})]
[Alias('Path')]
[string]$SourceFolder
)
# get a list of xlsx files
$allFiles = Get-ChildItem -Path $SourceFolder -Filter '*.xlsx' -File
# create an Excel object
$xldoc = New-Object -ComObject Excel.Application
# Message de confirmation
$xldoc.Visible = $false
$xldoc.DisplayAlerts = $false
$fileCount = 1
foreach ($excelFile in $allFiles) {
# rename the file. use -PassThru to get the FileInfo object of the renamed file
# apparently you want the files to be called '1.xlsx', '2.xlsx' etc.
$newName = '{0}.xlsx' -f $fileCount++
Write-Host "Renaming file '$($excelFile.Name)' to '$newName'"
$excelFile = Rename-Item -Path $excelFile.FullName -NewName $newName -PassThru
# Ouverture du fichier
$workbook = $xldoc.Workbooks.Open($excelFile.FullName)
# rename all worksheets in the file
Write-Host "Renaming all worksheets in '$newName'"
for ($i = 1; $i -le $workbook.Worksheets.Count; $i++) {
$workbook.Sheets.Item($i).Name = $i
}
$workbook.Save()
$workbook.Close()
}
$xldoc.Quit()
# clean-up used COM objects
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($xldoc)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
Rename-ExcelTabsAndFiles "C:\Users\Donosaure\Documents\Magister"

Resources