Refresh Excel connections using Powershell - excel

I have the below code that opens a spreadsheet, deletes all connections and "Saves" a new file.
$a = New-Object -COM "Excel.Application"
$a.Visible = $false
$b = $a.Workbooks.Open("F:\Scripts\All Users.xlsx")
do
{
$b.Connections.Item(1).Delete()
$Count = $b.Connections.Count()
} until($Count -eq 0)
$b.SaveAs("F:\Scripts\Users Home Drive Search.xlsx")
$b.Close()
I would like to know two things:
How do I get the sheet to RefreshAll connections? I've tried "$b.Connections.refreshall()" but the refreshall() doesn't exist.
How do I quit Excel application? I ran "New-Object -COM "Excel.Application" | Get-Member -MemberType Methods" and I don't see a quit or exit method.

RefreshAll() is a method of the WorkBook object, so use that to refresh the external connections:
$b.RefreshAll()
To exit Excel and remove the used COM objects from memory, use
$a.Quit()
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($b)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($a)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
BTW, I would use more descriptive variable names, so $excel instead of $a and $workbook instead of $b to avoid confusion later on.

Related

PowerShell script to open excel, wait for power query to refresh, and then save the file

I have an Excel that is using Power Query to get data from a API. What I would like to do is have this data update every day without having to open the excel myself. So I enabled the setting within excel to Refresh data when opening the file.
So I am trying to create a PowerShell script which open the excel, waits for the query to refresh, and then saves the excel. However I cant get it to wait update the query has refreshed before saving and closing.
code:
$Excel = New-Object -COM "Excel.Application"
$Excel.Visible = $true
$Workbook = $Excel.Workbooks.Open("G:\...\jmp-main-2020-07-17.xlsx")
While (($Workbook.Sheets | ForEach-Object {$_.QueryTables | ForEach-Object {if($_.QueryTable.Refreshing){$true}}}))
{
Start-Sleep -Seconds 1
}
$Excel.Save()
$Excel.Close()
I think your while loop is wrong. You should probably loop over the worksheets in the workbook and for each of them loop over the QueryTables. Then enter a while loop to wait until the Refreshing property turns $false
foreach ($sheet in $Workbook.Sheets) {
$sheet.QueryTables | ForEach-Object {
while ($_.QueryTable.Refreshing) {
Start-Sleep -Seconds 1
}
}
}
As aside: you should clear the COM object you have created after finishing with them to free memory:
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
I am using Windows Task Scheduler to open Excel file and VBA inside that Excel file to close it after data is refreshed.
No PowerShell script needed.
I was able to get #Theo's example working with some small tweaks. Not sure why but the QueryTables property did not have my collection of queries for the sheet however using the ListObjects property did.
$file = "path\to\file.xlsx"
$Excel = New-Object -COM "Excel.Application"
$Excel.Visible = $false
$Workbook = $Excel.Workbooks.Open($file)
foreach ($sheet in ($Workbook.Sheets)) {
$sheet.ListObjects | ForEach-Object{$_.QueryTable.Refresh() | out-null}
$sheet.ListObjects | ForEach-Object{
while ($_.QueryTable.Refreshing) {
Start-Sleep -Seconds 1
}
}
}
And to properly save and exit out of the excel you would use
$Workbook.Save()
$Workbook.Close()
$Excel.Quit()

CSV to Excel with pivot table

So I am trying to write a easy code to convert a csv to excel, insert a pivot table and then save it. the below code runs fine but the excel file is being saved with the name $name instead of the content of $name... and also can I use VBA code in powershell script ? I want to insert a pivot table and I can only find vba code for it....
Add-Type -AssemblyName System.Windows.Forms
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{
InitialDirectory = [Environment]::GetFolderPath('Desktop')
Filter = 'SpreadSheet (*.csv)|*.csv'
}
$FileBrowser.ShowDialog()
$file = $FileBrowser.FileName
$name= $FileBrowser.SafeFileName
Write-Host $name
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$excel.DisplayAlerts = $false
$excel.Workbooks.Open($file)
$excel.Workbooks.item(1).SaveAs('C:\Users\User\Desktop\$name.xlsx',51)
$excel.Quit()
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel)
so after fixing the code here is the end product....
Add-Type -AssemblyName System.Windows.Forms
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{
InitialDirectory = [Environment]::GetFolderPath('Desktop')
Filter = 'SpreadSheet (*.csv)|*.csv'
}
$FileBrowser.ShowDialog()
$file = $FileBrowser.FileName
$name = $FileBrowser.SafeFileName -replace ".{4}$"
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$excel.DisplayAlerts = $false
$excel.Workbooks.Open($file)
#$excel.Workbooks.Item(1).activate()
#$excel.ActiveSheet.Cells.Select()
$excel.Workbooks.item(1).SaveAs("C:\Users\User\Desktop\$name.xlsx",51)
$excel.Quit()
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel)
Now I want to add a Pivot table to this. I added the lines to activate the workbook and select all the cells... now I have the VBA code but can someone help me translate it to the powershell ?
Sheets.Add
ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, SourceData:= _
"2!R1C1:R1048576C27", Version:=6).CreatePivotTable TableDestination:= _
"Sheet1!R3C1", TableName:="PivotTable3", DefaultVersion:=6
Sheets("Sheet1").Select
Cells(3, 1).Select
With ActiveSheet.PivotTables("PivotTable3").PivotFields("Domain")
.Orientation = xlRowField
.Position = 1
The above VBA is a macro that I recorded that gives us the steps... now we just have to translate it to power shell... I am lost as figuring the code till now itself was hours of tinkering...
It is because this is exactly what you told it to do.
$excel.Workbooks.item(1).SaveAs('C:\Users\User\Desktop\$name.xlsx',51)
Quotes are important. Single quotes mean to pass the string exactly as-is.
As defined in the help files.
about_Quoting_Rules - PowerShell | Microsoft Docs
Variables must be expanded, and in a string, you must use double-quotes.
$excel.Workbooks.item(1).SaveAs("C:\Users\User\Desktop\$name.xlsx",51)
Here are a couple of good articles for your edification on the topic as well:
A Story of PowerShell Quoting Rules
Windows PowerShell Quotes
Point of note:
That Write-Host is not needed if all you are doing is outputting text to the screen. In Powe3rShell output to the screen is the default. Also, depending on what version of PowerShell you are on, Write-Host empties the buffer, and anything after that is not usable elsewhere. Later versions of PowerShell does not have this issue, but it's still best to avoid Write-Host, unless you are sending a colorized text to the screen, or other custom formatting scenarios.
Additional reading:
Write-Host Considered Harmful
... Jeffrey Snover changes his stance on this as of May 2016.
With PowerShell v5 Write-Host no longer "kills puppies". data is
captured into info stream ...
https://twitter.com/jsnover/status/727902887183966208 ...
https://learn.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Utility/Write-Information?view=powershell-5.1
Lastly, you are no completely cleaning up behind yourself. Though you are quitting Excel, you are not exiting the Excel process, thus releasing those resources and the COM stuff. So, stopping that process and garbage collection is warranted.
For example:
Get-Process -Name 'EXCEL' | Stop-Process
Function Clear-ResourceEnvironment
{
# Clear any PowerShell sessions created
Get-PSSession | Remove-PSSession
# Release any COM object created
$null = [System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$Shell)
# Perform garbage collection on session resources
[System.GC]::Collect()
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
<#
Remove any custom variables created
some list of your variable, or a wildcard when prefix was used.
#>
Get-Variable -Name MyShell -ErrorAction SilentlyContinue | Remove-Variable
}

How to select Excel Add-ins with a script?

I'm trying to get a solution to select specific Excel Add-ins with a script.
I was able to catch all the information about Excel.Application and change "Installed" property to $true, but it comes back to $false when the code stops running. Anybody knows a different solution or a way to save the Excel property?
$excel = New-Object -ComObject Excel.Application
$excel.Addins | Where-Object { $_.Title -eq "NameOfAdd-ins" } | ForEach-Object {
$_.Installed = $true
}
$_.Installed property should stay $true after Run-Time

Use PowerShell to get Excel Data Connection's SQL command text

I have a bunch of Excel files each of which has a number of Workbook Connections. Each of these workbook connections has a properties with a Definition, which contains a "Connection String" and also a "Command text"
I would like to retrieve the connection string and command text values through PowerShell but cannot see the function to do this
I have got as far as the following snippet, any advice appreciated...
$excelObj = New-Object -ComObject Excel.Application
$excelObj.Visible = $false
$workbook = $excelObj.Workbooks.Open($xlsxLocation)
foreach ($connect in $workbook.Connections)
{
Write-Host $connect.Name
# This is where I need the connection string and the command text, for this connection.
}
I ran a search for "vba connections command text" which returned Extracting Excel Data Connection Command Text. Then I was able to adapt your code to:
$xlsxLocation="C:\Temp\MyFile.xlsx"
$excelObj = New-Object -ComObject Excel.Application
$excelObj.Visible = $False
$excelObj.DisplayAlerts = $False
$workbook = $excelObj.Workbooks.Open($xlsxLocation)
foreach ($worksheet in $workbook.Worksheets){
foreach ($listobject in $worksheet.ListObjects) {
$commandtext = $listobject.QueryTable.CommandText
if (-not ([string]::IsNullOrWhiteSpace($commandtext))) {
Write-Host $commandtext
}
}
}
$workbook.Close($False) # closed do not save
$excelObj.DisplayAlerts = $True
$excelObj.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excelObj) | Out-Null
Remove-Variable excelObj | Out-Null
The last two lines of code will dispose the Excel resourse when you terminate the script.

Powershell Excel automatic Bloomberg add-in

I have a problem with automatic run of Excel with Bloomberg add-in included.
When I manually open Excel worksheet, data functions from Bloomberg add-in automatically run.
But when I open same sheet with Powershell and save, there aren't any loaded data
Code - Start Excel and open Workbook:
$xls = New-Object -ComObject Excel.Application
$xls_workbook = $xls.Workbooks.Open("Market_data.xlsx")
$xls_workbook.Activate()
I have tried to force recalculation by these methods:
$xls.Calculate()
$xls.CalculateFull()
$xls.CalculateFullRebuild()
$xls.Workbooks.Application.CalculateFullRebuild()
$xls_workbook.Worksheets(1).Calculate()
But nothing worked. Strange, because, as I have mentioned, manual opening of Excel sheet causes that data from Bloomberg are loaded automatically.
Do you have some experience with this Bloomberg add-in automation? I wanted to check also .xla macros that are included (BloombergUI.xla, BloombergHistory.xla) but they are protected by passwords.
Maybe, is there any option to force running of all Add-ins in Excel?
Or is there any call like $xls.Application.Run() that can run this add-in?
Thank you
Whole code:
$xls = New-Object -ComObject Excel.Application
$xls_workbook = $xls.Workbooks.Open("MarketData.xlsx")
$xls_workbook.Activate()
#calculation
$xls.Calculate()
#$xls_workbook.Aplication.Run("RefreshAllStaticData") - THIS RETURNS ERROR, THAT MACRO IS NOT AVAILABLE OR MACROS ARE DISABLED
#my current option of waiting
$internal_timeout = new-timespan -Seconds $timeout
$sw = [diagnostics.stopwatch]::StartNew()
while ($sw.elapsed -lt $internal_timeout){
}
#maybe next option, how to wait until job finished
#$job = Start-Job -ScriptBlock {
#docalculation
#}
#Wait-Job $job -Timeout $timeout | out-null
$date = Get-Date -Format M_dd_yyyy
$file_to_save = "MarketData_$date.xlsx"
$xls_workbook.SaveAs($file_to_save)
$xls_workbook.Close();
$xls.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xls)
This code worked for me:
$xls = New-Object -ComObject Excel.Application
$xls.Workbooks.Open("C:\blp\API\Office Tools\BloombergUI.xla")
Write-Debug "$file_path$file_name$file_ext"
$xls_workbook = $xls.Workbooks.Open("$file_path$file_name$file_ext")
$xls_workbook.Activate()
$xls_workbook.Application.Run("RefreshAllWorkbooks")
$xls_workbook.Application.Run("RefreshAllStaticData")
$xls.CalculateFull()
Start-Sleep -s $timeout
$date = Get-Date -Format M_dd_yyyy
$file_to_upload = "$file_path$file_name$date$file_ext"
Write-Debug $file_to_upload
$xls_sheet = $xls_workbook.Sheets.Item(1)
$bl_value = $xls_sheet.Cells.Item(2,9).Text
Write-Host $bl_value
$xls_workbook.SaveAs($file_to_upload)
$xls_workbook.Close()
the bloomberg addin is not loaded at all when you use "New-Object -ComObject Excel.Application" to open excel. you can double check by alt+F11 in vba environment

Resources