PowerShell opens excel while running script and crashes - excel

I have made some code to run a Macro on 560 Excel files.
There is a small issue with the code, it seems it doesn't save excel file, and opens every excel file, cause excel to crash.
is there a way to have the macro be ran on these 560 files in the backround, and to automatically save once macro is ran, rather than saving it manually?
Thanks
Here is my Code:
# start excel
$excel = New-Object -comobject Excel.Application
# get files
$files = Get-ChildItem 'C:\Users\ME\Desktop\TEST'
# loop through all files in the directory
ForEach ($file in $files){
# open the file
$workbook = $excel.Workbooks.Open($file.FullName)
# make file visible
$excel.Visible = $true
# run macro
$app = $excel.Application
$app.run("PERSONAL.xlsb!Module6.MyMacro")
}

By setting $excel.Visible = $true, the code will become much slower because of all the screen updates involved.
Also, you do not save the workbook after running the code, and because you never quit Excel and remove the COM objects from memory, eventually it will crash because of running out of resources.
Try:
# start excel
$excel = New-Object -comobject Excel.Application
$excel.Visible = $false
$excel.DisplayAlerts = $false
# get files and loop through the list
# the usual extension for macro-enabled Excel files is `*.xlsm`.
# if your files have this extension, add -Filter '*.xlsm' to the
# Get-ChildItem command below.
Get-ChildItem -Path 'C:\Users\ME\Desktop\TEST' -File | ForEach-Object {
# open the file
$workbook = $excel.Workbooks.Open($_.FullName)
# run macro
$app = $excel.Application
$app.run("PERSONAL.xlsb!Module6.MyMacro")
$workbook.Close($true) # $true --> save changes
}
$excel.Quit()
# cleanup COM objects
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

Related

Import .odc file into Excel via PowerShell

I created a PowerQuery in excel which gets data from an API, modifies it, and presents it in a table. I have exported the PowerQuery to a .odc file called Query - Singles - Template.odc.
I also have a PowerShell script which is dynamically creating excels where needed, as well as taking the .odc template, replacing the API URL and creating a new .odc for each excel created.
I have tried to mainly import the .odc into the created excels and it is working as expected. However I cant find a way to import these .odc files in to excel via PowerShell script.
PowerShell Script:
$Excel = New-Object -ComObject excel.application
$Excel.DisplayAlerts = $False
$Excel.visible = $False
$WorkBook = $Excel.Workbooks.Add()
$WorkSheet = $WorkBook.worksheets.item(1)
$WorkSheet.name = "data"
((Get-Content -path "$Path\Query - Singles - Template.odc" -Raw) -replace "TempSetCode",$Set) | Set-Content -Path "$Path\$Set\Query - Singles - $Set.odc"
### Code to import .odc file into excel
$WorkBook.SaveAs("$SetPath\$Set-main-$ReleaseDate.xlsx", 51)
$WorkBook.Close($True)
$Excel.Quit()
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($WorkSheet)
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($WorkBook)
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
I solved this, not by importing the .odc file into excel, but by opening the .odc file into excel and then saving that file.
$InFile = Get-Item("$Path\$Set\Query - Singles - $Set.odc")
$Excel = New-Object -ComObject excel.application
$Excel.DisplayAlerts = $False
$Excel.visible = $False
$WorkBook = $Excel.Workbooks.Open($InFile.FullName)
$WorkSheet = $WorkBook.worksheets.item(1)
$WorkSheet.name = "data"
$WorkBook.RefreshAll()
$WorkBook.SaveAs("$SetPath\$Set-main-$ReleaseDate.xlsx", 51)
$WorkBook.Close($True)
$Excel.Quit()
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($WorkSheet)
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($WorkBook)
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

Powershell to refresh Excel with OLAP Query. Credential issues

I'm rather new with PowerShell scripting so please be patient with me :)
I have multiple excel file with OLAP Query connections connecting to Power BI Datasets, following are the script;
$libraryPath = "C:\Repos\AUSD\3.0\Test"
$excel = new-object -comobject Excel.Application
$excel.Visible = $false
# Give delay to open
Start-Sleep -s 3
$allExcelfiles = Get-ChildItem $libraryPath -recurse -include “*.xls*”
foreach ($file in $allExcelfiles)
{
$workbookpath = $file.fullname
Write-Host "Updating " $workbookpath
# Open the Excel file
$excelworkbook = $excel.workbooks.Open($workbookpath)
$connections = $excelworkbook.Connections
# This will Refresh All the pivot tables data.
$excelworkbook.RefreshAll()
# The following script lines will Save the file.
$excelworkbook.Save()
$excelworkbook.Close()
Write-Host "Update Complete " $workbookpath
}
$excel.quit()
It is working fine if following options;
$excel.Visible is true
However this is going to be scheduled in the server and hopefully this could be done in the background, hence the $excel.Visible = $false
This causing the following error;
I suspect this is due to the Automatic sign in which happen when the Excel are open, due to its not being open, its failing the sign in process.
How do I bypass or rather set the credentials/permission right?

Open, save and close Excel

I have an issue opening a spreadsheet via PowerShell, renaming a worksheet, saving and closing Excel. The issue is when run the first time the $WorkBook variable is null. If I run it a second time the script works fine. Also if I add $ExcelDoc.Visible = $true the script works fine. Does anyone have an idea why the script fails on it first run in the form it is below?
$Path = "C:\ScriptRepository\CQC\DataToLoad\"
$FileName = (Get-ChildItem $Path).FullName
$FileName2 = (Get-ChildItem $Path).Name
Start-Sleep 2
$ExcelDoc = New-Object -ComObject Excel.Application
$WorkBook = $ExcelDoc.Workbooks.Open($FileName)
$WorkSheet = $WorkBook.Worksheets.Item(2)
$WorkSheet.Name = "CQCProviders"
$WorkBook.Save()
$WorkBook.Close()
$ExcelDoc.Quit()
While([System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelDoc)) {}

How to use powershell to run personal macro with different paths

I am trying to create automatically a report out of an excel file. I already analyzed one specific file and the report is saved local.
Now I want to use this macro to run it on other files. Therefore I have to change the path in the powershell.
Now I want to run the macro automatically (let`s say at 1am) with powershell.
$excel = New-Object -comobject Excel.Application
$wbPersonalXLSB = $excel.workbooks.open("C:\Users\fami\AppData\Roaming\Microsoft\Excel\XLSTART\PERSONAL.XLSB")
$FilePath = "C:\Users\fami\Desktop\example.xls"
$workbook = $excel.Workbooks.Open($FilePath)
$excel.Visible = $false
$worksheet = $workbook.worksheets.item(1)
$excel.Run("PERSONAL.XLSB!run")
$wbPersonalXLSB.Close()
$workbook.save()
$workbook.close()
$excel.quit()
Only the $FilePath needs to be variable.
You just need to use a Parameter for the $FilePath variable instead of hard coding it. Like this:
param([string]$FilePath)
$excel = New-Object -comobject Excel.Application
$wbPersonalXLSB = $excel.workbooks.open("C:\Users\fami\AppData\Roaming\Microsoft\Excel\XLSTART\PERSONAL.XLSB")
$workbook = $excel.Workbooks.Open($FilePath)
$excel.Visible = $false
$worksheet = $workbook.worksheets.item(1)
$excel.Run("PERSONAL.XLSB!run")
$wbPersonalXLSB.Close()
$workbook.save()
$workbook.close()
$excel.quit()
Then you would schedule the script and specify the -FilePath paramater like so:
powershell.exe -file C:\folder\yourscript.ps1 -FilePath "C:\Users\fami\Desktop\example.xls"
EDIT: To read a list of files from a text file (with one file on each line) would be this.
param(
[string]$FileList,
[string]$PersonalXLSB="C:\Users\fami\AppData\Roaming\Microsoft\Excel\XLSTART\PERSONAL.XLSB",
[string]$RunMacro="PERSONAL.XLSB!run"
)
$Files = Get-Content $FileList
foreach ($FilePath in $Files) {
$excel = New-Object -comobject Excel.Application
$wbPersonalXLSB = $excel.workbooks.open($PersonalXLSB)
$workbook = $excel.Workbooks.Open($FilePath)
$excel.Visible = $false
$worksheet = $workbook.worksheets.item(1)
$excel.Run($RunMacro)
$wbPersonalXLSB.Close()
$workbook.save()
$workbook.close()
$excel.quit()
}
I've also moved the PersonalXLSB and Macro to Params, as they have a value set this will be used as default if you don't specify anything else. It's most basic form is like this:
powershell.exe -file C:\folder\yourscript.ps1 -FileList "C:\folder\name.text"
You can change the other params like this:
powershell.exe -file C:\folder\yourscript.ps1 -FileList "C:\folder\name.text" -RunMacro="PERSONAL.XLSB!macroname"

Powershell COM Excel visible not working

I have written a short Powershell script to copy some Excel data. The script works fine, but it opens Excel every time I run it, even thought the visible property is set to $false. Anyone have any ideas why? I have searched for answers, but can't find any solutions. I have also displayed the property and it is set correctly.
Excel opens when the workbook is opened.
Thanks,
Chris J.
Code:
## function to close all com objects
function Release-Ref ($ref{
([System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
#Read-Host -Prompt "Press Enter to exit"
#####################################################################
## Load excel com objects attach to file
#####################################################################
$ExcelPath = 'C:\Chris_Test.xls'
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $False
write-host "Visible 1"
write-host $Excel.Visible
$ExcelWorkBook = $Excel.Workbooks.Open($ExcelPath)
$Excel.Visible = $False
write-host "Visible 2"
write-host $Excel.Visible
$ExcelWorkSheet = $Excel.WorkSheets.item("Sheet1")
$Excel.Visible = $False
write-host "Visible 3"
write-host $Excel.Visible
$row = 2
$South_HX_Avg = "This"
$Middle_HX_Avg = " is a "
$North_HX_Avg = "test."
##
## Cells.Item(Row,Column)
#$ExcelWorkSheet.Cells.Item($row,1).Value2 = $South_HX_Avg
#$ExcelWorkSheet.Cells.Item(2,2).Value2 = $Middle_HX_Avg
#$ExcelWorkSheet.Cells.Item(2,3).Value2 = $North_HX_Avg
#####################################################################
# Close connections to Excel
# set interactive to false so no save buttons are shown
#####################################################################
$Excel.DisplayAlerts = $false
$Excel.ScreenUpdating = $false
$Excel.Visible = $False
write-host "Visible 4"
write-host $Excel.Visible
$Excel.Visible = $false
$Excel.Visible = $False
write-host "Visible 5"
write-host $Excel.Visible
$Excel.UserControl = $false
$Excel.Interactive = $false
## save the workbook
$ExcelWorkBook.Save()
## quit the workbook
$Excel.Quit()
## close all object references
Release-Ref($ExcelWorkSheet)
Release-Ref($ExcelWorkBook)
Release-Ref($Excel)
# Move-Item c:\scripts\test.zip c:\test -force
Read-Host -Prompt "Press Enter to exit"
When I uncommented the lines 30,31 and 32, it worked ok, but I had to comment out your calls to Release-Ref (including the definition) for it to parse.
Also, when you create a new Excel object using:
$Excel = New-Object -ComObject Excel.Application
the visibility of the window is set by default to false in the object's constructor, so there's no need to set it explicitly...
Yes I did try rebooting and also tried it on a different computer with the same results. Good catch Steve Rathbone, my procedure was missing a parenthesis. After I fixed it, it still opens Excel.
I also tried commenting out the calls to Release-Ref with the same results.
I am doing something similar in a script I've been working with lately. MS Com objects have tons of options in several places.
This works for me.
$xlsFile = "\\your\file.xls"
$Excel = New-Object -ComObject Excel.Application
$excel.DisplayAlerts = $false;
$excel.AskToUpdateLinks = $false;
$Workbook = $Excel.Workbooks.Open($xlsFile, 0, 0, 5, "password")
#do some stuff in excel
$Excel.Quit()
PS, you can also set some of the parameters using the workbooks.open() function.
Here's just a few:
Workbooks.Open(FileName, UpdateLinks, ReadOnly, Format, Password)
You can read more about com object parameters here:
https://learn.microsoft.com/en-us/office/vba/api/excel.workbooks.open

Resources