Get powershell to control Internet Explorer and switch between 2 webpages - powershell

I want to have Internet Explorer to switch between 2 different webpages in an endless loop . The webpages is local files, that has to be viewed for 10 seconds before changing to next webpage. My code so far:
$ie = New-Object -Comobject 'InternetExplorer.Application'
$ie.Visible = $true
function IEWeb {
$ie.navigate(file://D:\web\index.html)
Start-sleep 10
$ie.navigate(file://D:\web\index2.html)
Start-sleep 10
}
while($true) {
IEWeb
}
Everything is working until the second webpage has to be loaded.
Then I get an error message:
Object is disconnected from it's clients. (Exception from HRESULT:0x800010108 (RPC_E_DISCONNECTED))
I have tried with global vars but still the same.
Can anyone give Me a hint of what I'm missing?

I wasn't able to produce your issue, but how about this?
function Navigate-Rotate {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[string[]]$Url
,
[Parameter()]
[int]$SleepSeconds = 10
)
Process {
$ie = New-Object -Comobject 'InternetExplorer.Application'
$ie.Visible = $true
while ($ie.Visible) {
foreach ($uri in $Url) {
if ($ie.Visible) {$ie.navigate2($uri)}
Start-Sleep -Seconds $SleepSeconds
if ($ie.Visible) {$ie.Stop()} #just incase anything's running which may interfere
}
}
}
}
#Navigate-Rotate 'https://stackexchange.com','https://google.com'
Navigate-Rotate 'file:///D:/web/index.html', 'file:///D:/web/index2.html'
Notes
I keep checking the value of $ie.Visible. Should a user exit IE this ensures that my code won't try to use any of $ie's methods. NB: This value doesn't get set to false once closed; rather it ceases to exist; but that evaluates as falsey, so has the same effect. There is a potential race condition, but it's minor / can't be avoided (or I don't know how to avoid it).
I use navigate2 instead of navigate since this method's a bit more flexible / there's no drawback.
I use $ie.Stop() after loading each page to ensure that IE's not busy with other tasks when I try to navigate away from the page. That should stop anything in the page from being able to block our attempt to navigate elsewhere.
Other Notes
The change from a function to a cmdlet doesn't make much difference; it's just my preferred approach.
Passing in a list of URLs to the function rather than hardcoding the 2 URLs means I can change the URLs easily (e.g. by reading in from a file), and I'm not restricted to 2 files/sites.
Details of the methods and properties available in IE are listed here: https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa752084(v=vs.85)
Apologies that I can't explain why you're seeing the issue you are; I just hope that this amended version avoids the issue you're seeing, and a few other potential issues you've not yet seen.

Related

Is there a way to run a popup window while a script runs?

I am creating a gui, and want a popup to let you know it's busy, but then close when it's completed that specific task. The only thing i can find is the following...
$popup = New-Object -ComObject wscript.shell
$popup.popup("Running Script, Please Wait....",0,"Running...",0x1)
But the issue is, this is waiting for a response, and then it will run the script. I am not asking for some to write me a script, but some guidelines on where to find this information.
I need powershell to popup a window, and then leave it up, while a script is ran, and then close it when the script is done running.
Would it be best just just have another windows form, that runs the script with a label on it? That seems like an excessive amount of work for a simple task. But it IS powershell...
Is there something like...
$popup = New-Object -ComObject wscript.shell
$popup.popup("Running Script, Please Wait....",0,"Running...",0x1)
###RUN SCRIPT HERE...
$popup.close()
EDIT:::
To the question "Why am I trying to have a popup, instead of writeprogress or whatnot" ... The reason is because I am doing this in a gui. Not in the command line. So i need the gui to basically inform the person it's busy, some of the tasks can take over 6 hours to complete, and i don't want them clicking around, and doing other things while the current task at hand is running.
EDIT 2:::
I will leave this open, as the original question was not answered, but I created a work around with the following code.
$LabelAlert = New-Object system.windows.forms.label
$LabelAlert.Text = "Working, Please wait."
$LabelAlert.location = New-Object System.Drawing.Point(0,180)
$LabelAlert.width = 590
$LabelAlert.height = 25
$LabelAlert.Visible = $false
$LabelAlert.TextAlign = "TopCenter"
$Form.Controls.Add($LabelAlert)
$FormGroupBox = New-Object System.Windows.Forms.GroupBox
$FormGroupBox.Location = New-Object System.Drawing.Size(0,0)
$FormGroupBox.width = 600
$FormGroupBox.height = 375
$Form.Controls.Add($FormGroupBox)
$startAlert = {
$LabelAlert.Visible = $true
$FormGroupBox.Visible = $false
}
$stopAlert = {
$LabelAlert.Visible = $false
$FormGroupBox.Visible = $true
}
Every form part was moved inside the group box. And the group box is the same size as my window.
And for every time consuming Script i run
&$startAlert
....script commands go here...
&$stopAlert
You could use Start-Job to run the popup within a background job, which would allow the script to continue after it has appeared:
$Job = Start-Job -ScriptBlock {
$popup = New-Object -ComObject wscript.shell
$popup.popup("Running Script, Please Wait....",0,"Running...",0x1)
}
#Run script here..
But I can't see any way to force the popup to close at the end of your script (tried Remove-Job -Force and even Stop-Process conhost -Force but neither seemed to work).
As others have said though, the better option would be write status to the PowerShell window. You might want to look at the Write-Progress cmdlet which you can use to disply a progress bar over a running script.

InternetExplorer.Application ReadyState is always blank

I have this very simple snippet from my script:
$ie = New-Object -ComObject InternetExplorer.Application
$ie.visible = $true
$ie.navigate("https://10.0.0.1/ccmadmin/phoneFindList.do")
while ($ie.readyState -ne 4) { Start-Sleep -Milliseconds 100; Write-Host $ie.readyState }
I have definitely used very similar code before with no issues, however for some reason when I run this script, $ie.readyState just contains a blank value, and so prints an empty string to the console, and it never gets set to 4 (or anything else for that matter.
I can see that the page has a cert error, but I'm pretty sure this should still work.
What could possibly cause this issue?
When stepping through the script, I can see that readyState contains a value of 0 all the way up til line 3, where we call navigate - at this point readyState is set to an empty string or blank...
When you look at all the API info on the underlying IHTMLXMLHttp* info on MSDN and W3C sites, there is no indication of SSL restriction in use. Do a trace on the request and see property it is getting hung on.
READYSTATE_UNINITIALIZED (0)
The object has been created, but not initialized (the IHTMLXMLHttpRequest::open method has not been called).
READYSTATE_LOADING (1)
A request has been opened, but the IHTMLXMLHttpRequest::send method has not been called.
READYSTATE_LOADED (2)
The IHTMLXMLHttpRequest::send method has been called. No data is available yet.
READYSTATE_INTERACTIVE (3)
Some data has been received; however, IHTMLXMLHttpRequest::responseText is not available.
READYSTATE_COMPLETE (4)
All the data has been received.

Using powershell to flip web sites

I'm trying to use PowerShell with IE object to load two different sites back and forth.
Here is the code I'm using:
$ie = new-object -com "InternetExplorer.Application"
$ie.visible = $true
while ($ie.busy) {sleep -milliseconds 50}
while($i -lt 10)
{
$i++
$ie.navigate("http://www.google.com")
sleep -s 10
$ie.navigate("http://www.microsoft.com")
sleep -s 10
}
Basically, I want load the browser, navigate to google, wait 10 seconds, navigate to Microsoft, wait 10 seconds, and loop that 10 times.
When I run it now, the page loads up, and navigates fine the first time, but then start popping "Method invocation failed because [System._ComObject] doesn't contain a method named 'navigate'." over and over.
I can't find anything in the documentation explaining how to do this. Any help would be greatly appreciated.

How to Quit current Powershell IE COM session using Try/Catch/Finally

Looking to terminate IE COM Object browser sessions at the end of my script, in my Finally{} statement.
I would think it be as simple as:
Finally{
$ie.Quit()
}
But its not. the $ie object is unreachable at this stage in the script and no methods can be called on it. Anyone know why this is? Did the $ie object go out of scope?
I have defined the $ie object earlier, before my Try{} statement:
$ie = New-Object -COMObject InternetExplorer.Application
Try{...}
But I dont actually Navigate() until inside the Try{} statement:
$ie = New-Object -COMObject InternetExplorer.Application
Try{
$ie.navigate("http://www.allregs.com/tpl/Main.aspx")
}
Then I have my catch{} statement:
Catch{
write-host “Exception Message: $($_.Exception.Message)” -ForegroundColor Red
}
And lastly my Finally{} statement:
Finally{
$ie.Quit() # 'can't call method on System.ComObject.' Null?
}
Any ideas why I can't Quit() the current $ie process at this stage in the script? Am I missing something? Is there another way to end the current Internet explorer session, without closing ALL of them? Any input welcome. Thanks.
Update:
So it appears $IE is unreachable at the end because the code never reaches the part where $ie actually navigates and gets a value. It only gets initialized at the start. In fact, it appears my Invoke-Webrequest requests (which log me in) are what prompt IE to start up and this is why a call to $ie.Quit() does nothing. My question now, is there a way to close IE sessions started using Invoke-Webrequest? -UseBasicParameter works to supress IE from starting up but it also messes up my code in weird ways so this doesn't seem like an option
You don't really need the finally{} block unless there is something in the catch{} block that would terminate the script/function at that point. If the script/function is able to carry on after the try{}catch{}, then you can just call $ie.Quit():
$ie = New-Object -COMObject InternetExplorer.Application
try {
$ie.navigate("http://www.allregs.com/tpl/Main.aspx")
}
catch {
Write-Host “Exception Message: $($_.Exception.Message)” -ForegroundColor Red
}
$ie.Quit()
Update
In light of the additional information in the question, I don't think that try-catch-finally has anything to do with the actual problem here, so I guess the answer to the title question is that as long as $ie is still a valid application object, the .Quit() method will still work in a finally{} block.

Properly displaying a tray balloon tooltip in PowerShell

Short version: I think I need help with properly using events in PowerShell that are invoked as a result of a Windows Message to get rid of a balloon tooltip's icon.
Long Version:
I have a long-running PowerShell command (a build) that I would like to be notified when it completes via a balloon tooltip in the system tray/notification area.
I was able to create a Write-BalloonTip script (below) that does roughly what I want. The only problem is that, as sometimes happens with tray icons, the tray icon doesn't disappear until I mouse over it. By re-using the same global variable to represent the NotifyIcon, I'm able to re-use this script and keep it so that only one system tray icon remains (until I mouse over it). This still feels like a hack. I tried to add an event handler so that it'd be notified on the BalloonTipClosed event and then dispose of it there. In the event handler, I tried all three techniques I've seen suggested for getting rid of the lingering icon to no avail.
The annoying part is that a simple .Dispose seems to work on subsequent calls of the script, leading me to think that the event script block isn't being called at all.
I've verified that BalloonTipClosed gets called after the tip fades away in a separate WinForms app.
Am I missing something basic? Any help is much appreciated. Thanks!
Here's the code for "Write-BalloonTip.ps1":
param
(
$text,
$title = "",
$icon = "Info",
$timeout=15000
)
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | out-null
if ($global:writeBalloonTipIcon)
{
# This gets rid of the previous one
$global:writeBalloonTipIcon.Dispose()
}
$global:writeBalloonTipIcon = new-object System.Windows.Forms.NotifyIcon
$global:writeBalloonTipIcon.Icon = [System.Drawing.SystemIcons]::Information
# FIXME: This *should* cleanup the icon after it's done, but it doesn't seem to work
$global:writeBalloonTipIcon.add_BalloonTipClosed(
{
# this *should* work, but it's not. What am I missing?
$global:writeBalloonTipIcon.Icon = $null;
$global:writeBalloonTipIcon.Visible = $false;
$global:writeBalloonTipIcon.Dispose();
});
$global:writeBalloonTipIcon.Visible = $true;
$global:writeBalloonTipIcon.ShowBalloonTip($timeout, $title, $text, $icon);
I think you need to execute this code in an STA thread. PowerShell (v2 shown here) executes in an MTA thread by default:
PS U:\> [System.Threading.Thread]::CurrentThread
ManagedThreadId : 5
ExecutionContext : System.Threading.ExecutionContext
Priority : Normal
IsAlive : True
IsThreadPoolThread : False
IsBackground : False
ThreadState : Running
ApartmentState : MTA
CurrentUICulture : en-US
CurrentCulture : en-US
Name : Pipeline Execution Thread
I would recommend using the Register-ObjectEvent to subscribe to the BalloonTipClosed event. This came up recently in another SO post. Check it out.