How to open an Excel file directly from a Powershell script? - excel

I'm using Powershell version 7.2.1
When I run the command below on Powershell, it works fine, i.e. Excel is launched and the target Excel file is opened.
& 'PATH_TO_EXCEL_EXE' 'PATH_TO_TARGET_FILE'
Howewer, when I pass this command to a scheduled task, Excel is launched but the target file isn't opened. More details about my Powershell script:
$action = New-ScheduledTaskAction -Execute "& 'PATH_TO_EXCEL_EXE' 'PATH_TO_TARGET_FILE'"
...
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "myTask"
# The line below is for testing purpose, to see if my scheduled task can launch correctly.
Start-ScheduledTask -TaskName "myTask"
Can someone tell me the reason why Powershell fails to replicate the action, when it's passed to a scheduled task as above? Any workarounds/solutions would be appreciated.

Based on ZivkoK's comment, I'm adding an answer which solves the issue:
$excelExePath = ...
$excelFile = ...
$action = New-ScheduledTaskAction -Execute $excelExePath -Argument """$excelFile"""
A scheduled task based on the action above does what it intends to do.
P.S. Triple quotes surrounding excelFile parameter are needed when the excel file path contains space characters. For example, if the excel file path is "C:\MyUser\some random file.xlsx", then Excel tries to open 3 files named "some", "random", and "file.xlsx". Using triple quotes solves this problem.

Related

Excel Crashes when trying to run powershell command

I am having an issue trying to run a complex HTTP Request through a powershell command from a VBA function.
VBA Code Below: The powershell script runs fine from inside powershell
Public Function PS_GetOutput(id As String) As String
'Setup the powershell command properly
psCommand = "powershell -command " & "C:\Users\promney\temp1\curlCommand.ps1 " & id
'Execute the command
CreateObject("WScript.Shell").Run psCommand, 3, True
'Get an instance of the clipboard to capture the save value
End Function
The PS1 file should grab the html and put a string on the clipboard. Instead, the PS window opens as a blue field, then it and excel crash completely and immediately. What am I doing wrong here?
Note: I would include the PS script but that is a lot of possibly sensitive info to scrub and I'm pretty sure the issue is in the VBA, since it works fine unless called from excel.

can you use IF statement in inline Azure Powershell script in Azure Devops?

I have a task in Azure Devops which builds a VM
Task 1 builds a VM
Task 2 adds datadisks if required
so in task 2 I have an inline powershell script but when i run it it gives an error
" The term 'n' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. Not sure why it is giving the error?
"
###Azure Devops pipeline variable is $(datadisk1required) this has the value 'n'
$datadisk1required = $(datadisk1required)
if ($datadisk1required -eq "n")
{
write-host "datadisk1 not required"
write-host $datadisk1required
}
The substitution of AzDO variables in the script happens before PowerShell runs, and that means that if the inline script definition is:
$powerShellVariable = $(azVariable)
and the value of the azVariable variable in the task is "string", the resulting script passed to PowerShell will be:
$powerShellVariable = string
PowerShell will interpret the bare word string as a command name, hence the error.
Put quotation marks in place around the AzDO variable macro and it'll work:
$datadisk1required = '$(datadisk1required)'

Create and Writing into a .tempo file using PowerShell Script with variables passed, a specific format

I would like to create a abcd.tempo file using powershell and want to add the content in the below format :
Startdate:12/06/2020
ParamVar2:RISPA
ParamVar3:MLPL
CNTRY=USA
the above is the targeted format , i need to insert into the abcd.tempo file.
Startdate is a variable used and similar to this, other variables as well.
what i meant,Startdate, ParamVar2 , ParamVar3, CNTRY are hardcoded labels and these parameters would be filled with the dynamic values fetched from other code block in my same powershell file.
At present, am able to create a new abcd.tempo file using
new-item -filepath "$myfilepath2createFile" -itemtype File
and i added some dummy content inside that. but
am not able understand how can i create the similar structure , written above.
Another requirement is to call a EXE file from this powershell script and pass anotehr 4 parameters to the EXE
Though i used
Start-Process -FilePath $script:testingexeFile
am stuck at how to pass the 4 parameters to this EXE

Powershell multithreading

I have a Powershell script that converts Office documents to PDF. I would like to multithread it, but cannot figure out how based on other examples I have seen. The main script (OfficeToPDF.ps1) scans through a list of files and calls separate scripts for each file type/office application (ex. for .doc files WordToPDF.ps1 is called to convert). The main script passes 1 file name at a time to the child script ( I did this for a couple of reasons).
Here is an example of the main script:
$documents_path = "C:\Documents\Test_Docs"
$pdf_out_path = "C:\Documents\Converted_PDFs"
$failed_path = "C:\Documents\Failed_to_Convert"
# Sets the root directory of this script
$PSScriptRoot = Split-Path -parent $MyInvocation.MyCommand.Definition
$date = Get-Date -Format "MM_dd_yyyy"
$Logfile = "$PSScriptRoot\logs\OfficeToTiff_$Date.log"
$word2PDF = "$PSScriptRoot\WordToPDF.ps1"
$arguments = "'$documents_path'", "'$pdf_out_path'", "'$Logfile'"
# Function to write to log file
Function LogWrite
{
Param ([string]$logstring)
$time = Get-Date -Format "hh:mm:ss:fff"
Add-content $Logfile -value "$date $time $logstring"
}
################################################################################
# Word to PDF #
################################################################################
LogWrite "*** BEGIN CONVERSION FROM DOC, DOCX, RTF, TXT, HTM, HTML TO PDF ***"
Get-ChildItem -Path $documents_path\* -Include *.docx, *.doc, *.rtf, *.txt, *.htm? -recurse | ForEach-Object {
$original_document = "$($_.FullName)"
# Verifies that a document exists before calling the convert script
If ($original_document -ne $null)
{
Invoke-Expression "$word2PDF $arguments"
#checks to see if document was successfully converted and deleted. If not, doc is moved to another directory
If(Test-Path -path $original_document)
{
Move-Item $original_document $failed_path
}
}
}
$original_document = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
Here is the script (WordToPDF.ps1) that is called by the main script:
Param($documents, $pdf_out_path, $Logfile)
# Function to write to the log file
Function LogWrite
{
Param ([string]$logstring)
$time = Get-Date -Format "hh:mm:ss:fff"
Add-content $Logfile -value "$date $time $logstring"
}
$word_app = New-Object -ComObject Word.Application
$document = $word_app.Documents.Open($_.FullName)
$original_document = "$($_.FullName)"
# Creates the output file name with path
$pdf_document = "$($pdf_out_path)\$($_.BaseName).pdf"
LogWrite "Converting: $original_document to $pdf_document"
$document.SaveAs([ref] $pdf_document, [ref] 17)
$document.Close()
# Deletes the original document after it has been converted
Remove-Item $original_document
LogWrite "Deleting: $original_document"
$word_app.Quit()
Any suggestions would be appreciated.
Thanks.
I was just going to comment and link you to this question: Can PowerShell run commands in Parallel. I then noted the date of that question and the answers, and with PowerShell v3.0 there are some new features that might work better for you.
The question goes over use of the PowerShell jobs. Which can work but require you to keep up with the job status, so can add a bit extra coding to manage.
PowerShell v3 opened up the door a bit more with workflow which is based on Windows Workflow Foundation. A good article on the basics of how this new command works can be found on Script Guy's blog here. You can basically adjust your code to run your conversion via workflow and it will perform this in parallel:
workflow foreachfile {
foreach -parallel ($f in $files) {
#Put your code here that does the work
}
}
Which from what I can find the thread limit this has is 5 threads at a time. I am not sure how accurate that is but blog post here noted the limitation. However, being that the Application com objects for Word and Excel can be very CPU intensive doing 5 threads at a time would probably work well.
I have a multithreaded powershell environment for indicator of compromise scanning on all AD devices- threaded 625 times with Gearman. http://gearman.org
It is open source and allows for an option to go cross platform. It threads with a server worker flow and runs via Python. Extremely recommended by yours truly- someone that has abused threading in powershell. This isn't so much an answer but something that I had never heard of but love and use daily. Pass it forward. Open source for the win :)
I have also used psjobs before and they are great until a certain point of magnitude. Maybe it is my lack of .net expertise but ps has some querky subtle memory nuances that in a large scale can create some nasty effects.

powershell: if excel already running, get instance, otherwise start it - with exception handling

Using Powershell, I want to import some tab-separated Ascii files into MS Excel. I use a loop for doing so, and right now I have a simple solution that works:
for each file: start Excel , import tsv file, close Excel.
..assuming Excel is in the Path, it's the right version of Excel, Excel 2010
Now I want to switch to a more efficient version: keep excel open.
for each file: grab running instance of excel if there is one, if not, try to start excel. Process file. Keep excel open. At the end, keep it open ( I want to look at the excel files while the script is running, which could take a while. Annoyingly, in the current version of the script excel is being closed while I am looking at the output).
I haven't found a comprehensive solution for this, neither here nor elsewhere on the internet. With "comprehensive" I mean "Exception Handling". In Powershell, it's a bit confusing. There are two ways of dealing with exceptions: using trap and a try-catch block.
Here is my code, thrown together from several internet sources , how can I improve it?
I want to wrap it in a function, but COM objects as return values are problematic. (
What I want is a combination of "simple factory" and "singleton".)
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Interop.Excel")
try {
$excelApp = [System.Runtime.InteropServices.Marshal]::GetActiveObject("Excel.Application")
} catch [System.Runtime.InteropServices.COMException], [System.Management.Automation.RuntimeException]{
write-host
write-host $("TRAPPED: " + $_.Exception.GetType().FullName);
write-host $("TRAPPED: " + $_.Exception.Message);
write-host "Excel is not running, trying to start it";
$excelApp = New-Object -ComObject "Excel.Application"
if (-not $excelApp){
# excel not installed, not in path
Write-Error "Excel not running, and cannot be started, exiting."
# Todo: test if excel version is correct, e.g. english Excel 2007 or 2010., if not set outfile extension xls.
exit;
}
}
catch [System.Exception]{
write-host $("EXCEPTION: " + $_.Exception.GetType().FullName);
write-host $("EXCEPTION: " + $_.Exception.Message);c
Write-Error 'Something went wrong during creation of "Excel.Application" object, => Exit.'
exit;
}
It's been a while since you asked this, but I have run into the same problem. Your code appears to be only good example of a reasonable solution. I do question your concern, however. Specifically, once Excel is running is there a need to call this routine again in a function? That is, once Excel is open, then you can treat it as a service to open/close workbooks, access worksheets within them, and eventually $excelApp.Quit() the application (although in Powershell v1.0 the .Quit() won't exit the app... but that is OK since I am grabbing a running instance anyways).
The link below discusses a way of starting up and grabbing the PID of Excel instance in order to explicitly kill it if needed.
Discussion on Quiting/Killing Excel from within Powershell
All variables must be null. Excel.Workbooks - all open book.
Check count of Workbooks. If count = 0, quit. After close of Powershell Excel will be close too.
$Workbook.Close()
$WorkSheet = $null;
$Workbook = $null;
If($excelApp.Workbooks.Count -eq 0)
{$excelApp.Quit()}
$excelApp = $null;

Resources