Powershell Internet Explorer Automation - powershell

Trying to get powershell to start different websites at some time intervals.
Here is a script that works:
function IEWeb {
$ie = New-Object -Comobject 'InternetExplorer.Application'
$ie.visible=$true
Do
{
$ie.navigate('http://p-captas02.int.addom.dk/cap-tas-views/Queue.aspx')
start-sleep 15
$ie.navigate('https://oneview.int.addom.dk/dashboard?dashboard_id=1')
start-sleep 15
$ie.navigate('https://oneview.int.addom.dk/dashboard?time=0&scroll_value=15&dashboard_id=10')
start-sleep 15
}
While ($ie.name -contains 'Internet Explorer')
}#Function
The problem is that it does not work every time
Is there anyone who knows another way of doing it?
It is important that the websites are started in the same tab

I think it would be better to check if IE has not been closed by the user at some point before trying to navigate to the next url. Also, $ie.name is a String, so $ie.name -contains 'Internet Explorer' would be wrong.
Maybe this works better for you.
function IEWeb {
# create an array with the urls you want to revolve
$urls = 'http://p-captas02.int.addom.dk/cap-tas-views/Queue.aspx',
'https://oneview.int.addom.dk/dashboard?dashboard_id=1',
'https://oneview.int.addom.dk/dashboard?time=0&scroll_value=15&dashboard_id=10'
$ie = New-Object -Comobject 'InternetExplorer.Application'
$ie.visible=$true
$index = 0
while ($ie.HWND) { # for as long as the user does not close IE
$ie.navigate($urls[$index])
Start-Sleep 15
# increment the array counter, and revert to index 0 if $urls length is reached
$index = ($index + 1) % $urls.Count
}
try {
# close and release the Com object from memory
$ie.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ie) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
catch {}
}
IEWeb

Related

IE Automation with Powershell send ENTER Key

I am doing an IE Automation with ServiceNow where there is an option to fill the search data but there is no search button available to use the CLICK method. So I am looking for the method to enter key like {ENTER} or {~} once I filled the search data. But I am in a middle stage of PowerShell scripting and not sure how to use that.
If someone could help me with the method that would be greatly appreciate.
$IE = New-Object -ComObject InternetExplorer.application
$IE.FullScreen = $false
$IE.Visible = $true
$IE.Navigate($ServiceNowURL)
While ($IE.Busy -eq $true)
{
Start-Sleep -Milliseconds 50
}
$Enter = Read-Host 'To continue press ENTER'
#Enter
$Search = $IE.Document.IHTMLDocument3_getElementsByTagName('input') | ? {$_.id -eq 'sysparm_search'}
$EnterValue = $Search.value() = $TicketNumber
First, you need to active IE window and bring it to front using AppActivate, then set focus to the search area using focus(). After that, you can send Enter key using SendKeys.
I use https://www.google.com to search as an example and you can refer to my code sample below. I test it and it works well:
[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
$ie = New-Object -ComObject 'InternetExplorer.Application'
$ie.Visible=$true
$ie.Navigate("https://www.google.com") #change it to your own url
while($ie.ReadyState -ne 4 -or $ie.Busy) {Start-Sleep -m 100}
$search=$ie.Document.getElementsByName("q")[0] #change it to your own selector
$search.value="PowerShell" #change it to your own search value
Sleep 5
$ieProc = Get-Process | ? { $_.MainWindowHandle -eq $ie.HWND }
[Microsoft.VisualBasic.Interaction]::AppActivate($ieProc.Id)
$search.focus()
[System.Windows.Forms.SendKeys]::Sendwait("{ENTER}");

powershell click on internet explorer popup

I need to use PowerShell to hit close on this pop up window which appears when I open internet explorer. Hitting enter key also closes the pop up.
What I've tried
[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
$ie = new-object -com internetexplorer.application
$ie.visible = $true
$ie.navigate('http://website/')
while ($ie.busy) { Start-Sleep 3 }
[Microsoft.VisualBasic.Interaction]::AppActivate("internet explorer")
[System.Windows.Forms.SendKeys]::Sendwait("{ENTER}");
Start-Sleep 3
$link = $ie.Document.getElementsByTagName('Button') | where-object { $_.innerText -eq 'Simple Setup' }
$link.click()
Start-Sleep 2
$ie.quit()
Continuing from my comment.
Others have run into this dialog and others, and, as stated, used Selenium, AutoIT, et., to deal with that; while others have tried different means.
For Example:
# using the process handle of that dialog
$ws = New-Object -ComObject WScript.Shell
$ld = (gps iex* | where {$_.MainWindowTitle }).id
if($ld.Count -gt 1)
{
$ws.AppActivate($ld[1])
$ws.sendkeys("{ENTER}")
}
# Using the WASP module
# (note - though the code for this module is still available, the DLL is not. So, you have to compile that yourself.)
Import-Module WASP
while ($true) {
[System.Threading.Thread]::Sleep(200)
$confirmation = Select-Window iexplore
if ($confirmation -ne $null)
{
Select-ChildWindow -Window $confirmation |
Select-Control -title "OK" -recurse |
Send-Click
}
}
btw..
"Hitting enter key also closes the pop up"
... that is because modal dialogs always take focus until they are dismissed.

Power Shell updating property

I play around with Power Shell... And I have a newbie question to navigate with ie.
Have this Code:
# IE window
$ie = New-Object -com "InternetExplorer.Application"
$ie.visible = $true
function waitforpageload {
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 1000; }
}
# navigate to the first page
$ie.Navigate("http://ss64.com/bash/")
waitforpageload
$ie.Document.Url # return http://ss64.com/bash/
# navigate to the second page
$ie.Navigate("http://ss64.com/ps/")
waitforpageload
$ie.Document.Url # return also http://ss64.com/bash/
and I'm wondering why $ie.Document.Url in both times return http://ss64.com/bash/
should is it possible to get http://ss64.com/ps/ in the second call?
Thanks a lot
You'll get the current location using
$ie.LocationURL
To get a list of all methods and properties available, use $ie | gm

Powershell Scripting -- Docx to PDF

I have a script (below) that converts docx to PDF. However, after it gets to file 204 or 205, I get a memory exceeded message and the process stops. I have about 40,000 docx that need to be converted. Can someone help with making this more efficient or possibly add a loop that closes the application after every 150 documents then re-opens the application and continues? Any help would be appreciated.
$documents_path = 'C:\Users\jgentile\Desktop\Purdue\DocX\All'
$word_app = New-Object -ComObject Word.Application
$i=0
Get-ChildItem -Path $documents_path -Filter *.doc? | ForEach-Object {
If( $i%150 ) { $word_app.Quit(); $word_app = New-Object -ComObject Word.Application }
$document = $word_app.Documents.Open($_.FullName)
$pdf_filename = "C:\Users\jgentile\Desktop\Purdue\PDFs\$($_.BaseName)_Discipline.pdf"
$document.SaveAs([ref] $pdf_filename, [ref] 17)
$document.Close()
$i++
}
$word_app.Quit()
The way your if statement is set up, it would execute every iteration of the loop except for 150, which I'm guessing is not what you intended. Also, you should release the comobject in order to avoid out of memory exception
If(!($i%150)) {
$word_app.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word_app)
Remove-Variable word_app
$word_app = New-Object -ComObject Word.Application
}
http://technet.microsoft.com/en-us/library/ff730962.aspx
An if-test needs a true or false value. 1%150 will return 1, 2%150 will return 2 etc. -> value is not null. You're if-test will always be true, except for 0 which is converted to false. So your script actually works the opposite way of how you want it.
You should test if $i%150 -eq 0. That will only happend when $i is 0, 150, 300, 450 etc. If you don't want it to run on 0, start your $i on 1. :)
Try
If($i%150 -eq 0) { $word_app.Quit(); $word_app = New-Object -ComObject Word.Application }
You could also just flip the if-test(turning false to true) using if(-not($i%150)), but I think -eq 0 is more readable.
UPDATE: As #Cole9350 pointed out, you should also release the Comobject after calling Quit() to free up the handle on the resource(process) so that Word closes properly. Like:
If($i%150 -eq 0) {
$word_app.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word_app)
$word_app = New-Object -ComObject Word.Application
}

How to Open; SaveAs; then Close an Excel 2013 (macro-enabled) workbook from PowerShell4

Doing a search on the above Com operations yields links dating to '09 and even earlier. Perhaps it hasn't changed but I find myself bumping up against errors where 'it is being used by another process.' - even though no Excel app is open on my desktop. I have to reboot to resume.
To be clear - I'm trying to open an existing file; immediately SaveAs() (that much works), add a sheet, Save(); Close() - and then, importantly, repeat that cycle. In effect, I'm creating a few dozen new sheets within a loop that executes the above 'Open Master; SaveAs(); Edit Stuff; Save; Close;
From the examples I've seen this is not a typical workflow for PowerShell. Pasted at the very bottom is my provisional script - pretty rough and incomplete but things are opening what they need to open and adding sheet also works - until I know I have the right way to cleanly close stuff out I'm not worried about the iterations.
I've found a couple examples that address closing:
From http://theolddogscriptingblog.wordpress.com/2012/06/07/get-rid-of-the-excel-com-object-once-and-for-all/
$x = New-Object -com Excel.Application
$x.Visible = $True
Start-Sleep 5
$x.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($x)
Remove-Variable x
And from http://social.technet.microsoft.com/Forums/windowsserver/en-US/24e57b61-e792-40c1-8aff-b0a8205f48ab/updated-opened-excel-using-powershell?forum=winserverpowershell
Set-ItemProperty $path -name IsReadOnly -value $false
$Excel.ActiveWorkBook.Save()
$openfile.Close()
$openfile = $null
$Excel.Quit()
$Excel = $null
[GC]::Collect()
<>
function MakeNewBook($theWeek, $AffID){
$ExcelFile = "C:\csv\InvoiceTemplate.xlsm"
$Excel = New-Object -Com Excel.Application
$Excel.Visible = $True
$Workbook = $Excel.Workbooks.Open($ExcelFile)
$theWeek = $theWeek -replace "C:\\csv\\", ""
$theWeek = $theWeek -replace "\.csv", ""
$theWeek = "c:\csv\Invoices\" +$AffID +"_" + $theWeek + ".xlsm"
$SummaryWorksheet = $Workbook.worksheets.Item(1)
$Workbook.SaveAs($theWeek)
return $Excel
}
function MakeNewSheet($myBook, $ClassID){
$SheetName = "w"+$ClassID
#$Excel = New-Object -Com Excel.Application
#$Excel.Visible = $True
$wSheet = $myBook.WorkSheets.Add()
}
function SaveSheet ($myExcel)
{
#$WorkBook.EntireColumn.AutoFit()
#Set-ItemProperty $path -name IsReadOnly -value $false
$myExcel.ActiveWorkBook.Save()
$openfile= $myExcel.ActiveWorkBook
$openfile.Close()
$openfile = $null
$myExcel.Quit()
$myExcel = $null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($myExcel)
Remove-Variable $myExcel
[GC]::Collect()
}
$theWeek = "C:\csv\wkStart2013-11-04.csv"
$x = Import-Csv $theWeek
foreach ($xLine in $x){
if ($x[0]){
$AffID = $x[1].idAffiliate
$myExcel = MakeNewBook $theWeek $x[1].idAffiliate
$ClassID = $x[1].idClass
MakeNewSheet $myExcel $ClassID
continue
}
SaveSheet($myExcel)
$AffID = $_.$AffID
$wID = $xLine.idClass
#MakeNewSheet($wID)
Echo "$wID"
}
As a follow up after playing around with this issue myself. I geared my solution around Ron Thompson's comment minus the function calls:
# collect excel process ids before and after new excel background process is started
$priorExcelProcesses = Get-Process -name "*Excel*" | % { $_.Id }
$Excel = New-Object -ComObject Excel.Application
$postExcelProcesses = Get-Process -name "*Excel*" | % { $_.Id }
# your code here (probably looping through the Excel document in some way
# try to gently quit
$Excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
# otherwise allow stop-process to clean up
$postExcelProcesses | ? { $priorExcelProcesses -eq $null -or $priorExcelProcesses -notcontains $_ } | % { Stop-Process -Id $_ }
My experience has been that the Quit method doesn't work well, especially when looping. When you get the error, instead of rebooting, open up Task Manager and look at the Processes tab. I'm willing to bet you'll see Excel still open -- maybe even multiple instances of it. I solved this problem by using Get-Process to find all instances of Excel and piping them to Stop-Process. Doesn't seem like that should be necessary, but it did the trick for me.
You should not have to keep track of processes and kill them off.
My experience has been that to properly and completely close Excel (including in loops), you also need to release COM references. In my own testing have found removing the variable for Excel also ensures no remaining references exist which will keep Excel.exe open (like if you are debugging in the ISE).
Without performing the above, if you look in Task Manager, you may see Excel still running...in some cases, many copies.
This has to do with how the COM object is wrapped in a “runtime callable wrapper".
Here is the skeleton code that should be used:
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$workbook = $excel.Workbooks.Add()
# or $workbook = $excel.Workbooks.Open($xlsxPath)
# do work with Excel...
$workbook.SaveAs($xlsxPath)
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
# no $ needed on variable name in Remove-Variable call
Remove-Variable excel
Try this
$filePath = "E:\TSMBackup\TSMDATAPull\ODCLXTSM01_VM.xlsx"
$excelObj = New-Object -ComObject Excel.Application
$excelObj.Visible = $true
$workBook = $excelObj.Workbooks.Open($filePath)
$workSheet = $workBook.Sheets.Item("Sheet1")
$workSheet.Select()
$workBook.RefreshAll()
$workBook.Save()
$excelObj.Quit()