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"
Related
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()
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
}
I tried this code as follows:
param(
$inputPath = "N:\Disease_Prevention\....\Samplename.xlsx",
$outputPath = "N:\Disease_Prevention\...\Output.csv"
)
$xlCSV=6
$inputPath = (Resolve-path $inputPath).Path
$outputpath = (Resolve-path $outputpath).Path
get-childitem $inputPath -File | foreach {
write-host "processing $_ "
$Excelfilename = $_.fullname
if(!$outputPath)
{
$outputPath = $_.DirectoryName
}
$CSVfilename =join-path $outputpath $_.BaseName
$CSVfilename+=".csv";
#open excel and save
$Excel = New-Object -comobject Excel.Application
$Excel.Visible = $False
$Excel.displayalerts=$False
$Workbook = $Excel.Workbooks.Open($ExcelFileName)
$Workbook.SaveAs($CSVfilename,$xlCSV)
$Excel.Quit()
If(ps excel){
kill -name excel
}
}
And received the following error as follows:
The error says "Unable to get the SaveAs property of the Workbook class"
Would you know why it could be?
And if it's a permission issue, then would you recommend an alternative way to convert excel into csv? Should I use cmdlet to read each line at the excel file and convert one by one?
The issues I encountered with your code are
$outputpath = (Resolve-path $outputpath).Path
Presumably this CSV file doesn't exist, so when you try to resolve the path it gives you nothing.
if(!$outputPath)
{
$outputPath = $_.DirectoryName
}
$CSVfilename =join-path $outputpath $_.BaseName
$CSVfilename+=".csv";
If you don't pass in an outputpath argument, then this will build the file properly. However, if you pass in an outputpath as a csvfile, then you build out a path that looks like
"N:\Disease_Prevention\...\Output.csv\inputpath basename.csv"
as you're appending the current file's basename and .csv to an already full path.
The adjusted code below worked fine.
param(
$inputPath = "N:\Disease_Prevention\....\Samplename.xlsx",
$outputPath = "N:\Disease_Prevention\...\Output.csv"
)
$xlCSV=6
$inputPath = (Resolve-path $inputPath).Path
get-childitem $inputPath -File | foreach {
write-host "processing $_ "
$Excelfilename = $_.fullname
if(!$outputPath)
{
$outputPath = $_.DirectoryName
$CSVfilename = join-path $outputpath $_.BaseName
$CSVfilename+=".csv"
}
else{
$CSVfilename = $outputPath
}
#open excel and save
$Excel = New-Object -comobject Excel.Application
$Excel.Visible = $False
$Excel.displayalerts=$False
$Workbook = $Excel.Workbooks.Open($ExcelFileName)
$Workbook.SaveAs($CSVfilename,$xlCSV)
$Excel.Quit()
If(ps excel){
kill -name excel
}
}
I also encourage you to check out Doug Finke's ImportExcel powershell module. Makes working with Excel files so much easier and it doesn't require Excel be installed on the machine that runs the script.
I have 20 csv files. Each are unrelated. How do I combine them together into one xlsx file with 20 sheets, each named after the csv files.
$root = "C:\Users\abc\Desktop\testcsv"
$CSVfiles = Get-ChildItem -Path $root -Filter *.csv
$xlsx = "C:\Users\abc\Desktop\testxl.xlsx" #output location
$delimiter = "," #delimiter
#Create a excel
$xl=New-Object -ComObject Excel.Application
$xl.Visible=$true
#add a workbook
$wb=$xl.WorkBooks.add(1)
ForEach ($csv in $CSVfiles){
#name the worksheet
$ws=$wb.WorkSheets.item(1)
$ws.Name = [io.path]::GetFileNameWithoutExtension($csv)
$TxtConnector = ("TEXT;" + $csv)
$Connector = $ws.QueryTables.add($TxtConnector,$ws.Range("A1"))
$query = $ws.QueryTables.item($Connector.name)
$query.TextFileOtherDelimiter = $delimiter
$query.TextFileParseType = 1
$query.TextFileColumnDataTypes = ,1 * $ws.Cells.Columns.Count
$query.AdjustColumnWidth = 1
# Execute & delete the import query
$query.Refresh()
$query.Delete()
$wb.SaveAs($xlsx,51)
}
# Save & close the Workbook as XLSX.
$xl.Quit()
This way, change the first line to the folder where you store those 20 CSV files and then
$path="c:\path\to\folder" #target folder
cd $path;
$csvs = Get-ChildItem .\* -Include *.csv
$y=$csvs.Count
Write-Host "Detected the following CSV files: ($y)"
foreach ($csv in $csvs)
{
Write-Host " "$csv.Name
}
$outputfilename = $(get-date -f yyyyMMdd) + "_" + $env:USERNAME + "_combined-data.xlsx" #creates file name with date/username
Write-Host Creating: $outputfilename
$excelapp = new-object -comobject Excel.Application
$excelapp.sheetsInNewWorkbook = $csvs.Count
$xlsx = $excelapp.Workbooks.Add()
$sheet=1
foreach ($csv in $csvs)
{
$row=1
$column=1
$worksheet = $xlsx.Worksheets.Item($sheet)
$worksheet.Name = $csv.Name
$file = (Get-Content $csv)
foreach($line in $file)
{
$linecontents=$line -split ',(?!\s*\w+")'
foreach($cell in $linecontents)
{
$worksheet.Cells.Item($row,$column) = $cell
$column++
}
$column=1
$row++
}
$sheet++
}
$output = $path + "\" + $outputfilename
$xlsx.SaveAs($output)
$excelapp.quit()
cd \ #returns to drive root
https://stackoverflow.com/a/51094040/5995160 answer is too slow when dealing with csv's with a ton of data, I modified this solution to use https://github.com/dfinke/ImportExcel. This has greatly improved the performance of this task, at least for me.
Install-Module ImportExcel -scope CurrentUser
$csvs = Get-ChildItem .\* -Include *.csv
$csvCount = $csvs.Count
Write-Host "Detected the following CSV files: ($csvCount)"
foreach ($csv in $csvs) {
Write-Host " -"$csv.Name
}
$excelFileName = $(get-date -f yyyyMMdd) + "_" + $env:USERNAME + "_combined-data.xlsx"
Write-Host "Creating: $excelFileName"
foreach ($csv in $csvs) {
$csvPath = ".\" + $csv.Name
$worksheetName = $csv.Name.Replace(".csv","")
Write-Host " - Adding $worksheetName to $excelFileName"
Import-Csv -Path $csvPath | Export-Excel -Path $excelFileName -WorkSheetname $worksheetName
}
This solution assumes that the user has already changed directories to where all the csv's live.
See below for a solution with uses the OpenText method.
At least two things to note:
I'm assuming your workbook creates a single sheet by default. if creates more than that, you will need to modify the script so that these additional sheets are deleted from the end result.
The way you specify TextFileColumnDataTypes is quite clever. You will need to modify it and feed the array to the FieldInfo argument below. See the documentation linked above for the kind of array it is expecting.
$CSVfiles = Get-ChildItem -Path $root -Filter *.csv
$xlsx = "C:\Users\abc\Desktop\testxl.xlsx" #output location
#Create a excel
$xl = New-Object -ComObject Excel.Application
$xl.Visible=$true
#add a workbook
$wb = $xl.WorkBooks.add(1)
# how many worksheets do you have in your original workbook? Assuming one:
$ws = $wb.Worksheets.Item(1)
ForEach ($csv in $CSVfiles){
# OpenText method does not work well with csv files
Copy-Item -Path $csv.FullName -Destination ($csv.FullName).Replace(".csv",".txt") -Force
# Use OpenText method. FieldInfo will need to be amended to suit your needs
$xl.WorkBooks.OpenText(`
($file.FullName).Replace(".csv",".txt"), # Filename
2, # Origin
1, # StartRow
1, # DataType
1, # TextQualifier
$false, # ConsecutiveDelimiter
$false, # Tab
$false, # Semicolon
$true, # Comma
$false, # Space
$false, # Other
$false, # OtherChar
#() # FieldInfo
)
$tempBook = $xl.ActiveWorkbook
$tempBook.worksheets.Item(1).Range("A1").Select() | Out-Null
$tempBook.worksheets.Item(1).Move($wb.Worksheets.Item(1)) | Out-Null
# name the worksheet
$xl.ActiveSheet.Name = $csv.BaseName
Remove-Item -Path ($csv.FullName).Replace(".csv",".txt") -Force
}
$ws.Delete()
# Save & close the Workbook as XLSX.
$wb.SaveAs($xlsx,51)
$wb.Close()
$xl.Quit()
I'm trying to write a powershell script that will loop through each excel file in the given directory, check the file for a specifically named worksheet, and then copy that file to another location if it's a match.
Please see below for what I've already tried:
[void][reflection.assembly]::Loadwithpartialname("microsoft.office.excel")
$Excel = New-Object -ComObject Excel.Application
$tempLocation = "C:\Test\" # Path to read files
$files = Get-ChildItem C:\Test
ForEach ($file in $files)
{
#Check for Worksheet named TestSheet
$WorkBook = $Excel.Workbooks.Open($file)
$WorkSheets = $WorkBook.WorkSheets
foreach ($WorkSheet in $Workbook.Worksheets) {
If ($WorkSheet.Name -eq "TestSheet")
{$path = $tempLocation + "\" + $file
Write "Saving $path"
Copy-Item c:\Test\$file c:\Confirmed}
Else {Write "$path does not contain TestSheet"}
$WorkBook.Close()
}
}
This script returns no errors in PowerShell, but just sits there without writing anything or copying any files. Any ideas?
EDIT: Here's my final script that is now running successfully
$ErrorActionPreference= 'silentlycontinue'
$tempLocation = "C:\Source" # Path to read files
$targetlocation = "C:\Target"
Write "Loading Files..."
$files = Get-ChildItem C:\Source
Write "Files Loaded."
ForEach ($file in $files)
{
#Check for Worksheet named TestSheet
$Excel = New-Object -ComObject Excel.Application
$Excel.visible = $false
$Excel.DisplayAlerts = $false
$WorkBook = $Excel.Workbooks.Open($file.Fullname)
$WorkSheets = $WorkBook.WorkSheets | where {$_.name -eq "TestSheet"}
if($WorkSheets) {
$path = $tempLocation + "\" + $file
$dest = $targetlocation + "\" + $file
Write "Saving $path"
$WorkBook.SaveAs($dest)
}
$Excel.Quit()
Stop-Process -processname EXCEL
}
Read-host -prompt "The Scan has completed. Press ENTER to close..."
clear-host;
There were several issues with my script's logic. The following script ran successfully! It took hours of research...
$ErrorActionPreference= 'silentlycontinue'
$tempLocation = "C:\Source" # Path to read files
$targetlocation = "C:\Target"
Write "Loading Files..."
$files = Get-ChildItem C:\Source
Write "Files Loaded."
ForEach ($file in $files)
{
#Check for Worksheet named TestSheet
$Excel = New-Object -ComObject Excel.Application
$Excel.visible = $false
$Excel.DisplayAlerts = $false
$WorkBook = $Excel.Workbooks.Open($file.Fullname)
$WorkSheets = $WorkBook.WorkSheets | where {$_.name -eq "TestSheet"}
if($WorkSheets) {
$path = $tempLocation + "\" + $file
$dest = $targetlocation + "\" + $file
Write "Saving $path"
$WorkBook.SaveAs($dest)
}
$Excel.Quit()
Stop-Process -processname EXCEL
}
Read-host -prompt "The Scan has completed. Press ENTER to close..."
clear-host;
You don't need this line:
[void][reflection.assembly]::Loadwithpartialname("microsoft.office.excel")
($Excel = New-Object -ComObject Excel.Application is sufficient here)
I don't think you're referencing the full path to your Excel files. Try modifying this line:
$WorkBook = $Excel.Workbooks.Open($file)
Amend to:
$WorkBook = $Excel.Workbooks.Open($file.Fullname)
Additionally, consider adding a filter to your Get-ChildItem command, if there are sub-directories or non-Excel files, they will cause errors:
$files = Get-ChildItem C:\Test -filter "*.xls"