Import .odc file into Excel via PowerShell - excel

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()

Related

PowerShell opens excel while running script and crashes

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()

Copying multiple excel columns one after another with powershell

I'm using powershell to manipulate a group of excel files. I'm copying one column from each file into another file that will contain all the data. The problem is I need to copy the columns one after another.
I have managed to write this to copy a column and paste it to another excel file.
ForEach ($item in $files) {
$FullName = [System.IO.Path]::GetFileName("$item")
$Excel = New-Object -ComObject Excel.Application
$Excel.visible = $true
$Workbook = $Excel.workbooks.open($Path + [System.IO.Path]::GetFileName("$item"))
$Worksheets = $Workbooks.worksheets
$Worksheet = $Workbook.Worksheets.Item(1)
$range = $WorkSheet.Range($range1).EntireColumn
$range.Copy() | out-null
$Worksheet = $Workbook.Worksheets.item($merge)
$Range = $Worksheet.Range($range2)
$Worksheet.Paste($range)
$WorkBook.Save()
$WorkBook.Close()
$Excel.quit()
}

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)) {}

Update multiple connection strings in Excel using PowerShell

I currently have a PS script that refreshes Excel files that have 1 data connection and it works perfectly. The issue is that I've built other Excel files that have 3 data connections. When I try to use the below code for the files with 3 data connection strings, the data gets processed, but only one of the data connections gets updated. Can anyone tell me what I need to do to get all data connections updated? I tried repeating the "refresh all"/"Save" part of the code, but that gave me error messages. Any help would be appreciated.
$excel = new-object -comobject excel.application
$excel.DisplayAlerts = $false
$excelFiles = Get-ChildItem -Path "File Folder Location (ex. C:\Documents)" -Include *.xls, *.xlsm,*.xlsx, *.lnk -Recurse
Foreach($file in $excelFiles)
{
$workbook = $excel.workbooks.open($file.fullname)
$worksheet = $workbook.worksheets.item(1)
$workBook.RefreshAll()
$workbook.save()
$workbook.close()
}
$excel.quit()
Depending on your connections, one the methods might help you (untested)
$excel = new-object -comobject excel.application
$excel.DisplayAlerts = $false
$excelFiles = Get-ChildItem -Path "$($env:userprofile)\Documents)" -Include *.xls, *.xlsm,*.xlsx, *.lnk -Recurse
Foreach($file in $excelFiles) {
$workbook = $excel.workbooks.open($file.fullname)
# ---- this method ----
foreach ($Conn in $workbook.Connections){
$Conn.OLEDBConnection.BackgroundQuery = $false
$Conn.refresh()
}
# ---- and/or this method ----
foreach ($Sheet in $workbook.Worksheets) {
foreach ($QTable in $Sheet.QueryTables) {
$QTable.BackgroundQuery = $false
}
}
# ----- might get you further, depneding on your connections ----
$workBook.RefreshAll()
$workbook.save()
$workbook.close()
}
$excel.quit()

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"

Resources