My requirement is to take screenshot of full page in Internet Explorer. I am aware of different tools available online for doing the same but since I want to achieve this on system where I don't have Admin rights, I can do it only with some program which doesn't require any installation.
I am looking for a program which can accept input(site URLs) from excel and then take full page screen shots of those sites.
Till now I only managed to open a site in IE, then do F11 to maximize screen and take screenshot of it. But, that gives cropped page snap since page height is more. I tried this with PowerShell.
Any suggestion/ideas?
Capturing a screenshot
Param(
#[Parameter(Mandatory = $true)][string]
$Path = "C:\Sagar\Screens"
)
$FileName = "$env:COMPUTERNAME - $(get-date -f yyyy-MM-dd_HHmmss).bmp"
$File = "$Path\$FileName"
Add-Type -AssemblyName System.Windows.Forms
Add-type -AssemblyName System.Drawing
# Gather Screen resolution information
$Screen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$Width = $Screen.Width
$Height = $Screen.Height
$Left = $Screen.Left
$Top = $Screen.Top
$ie = New-Object -com internetexplorer.application
$ie.Visible = $true
$ie.Navigate2("https://www.google.com/webhp?sourceid=chrome- instant&ion=1&espv=2&ie=UTF-8#q=how%20to%20send%20keys%20through%20powershell")
while($ie.busy){
Sleep -Seconds 2
}
Sleep -Seconds 2
$sig = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32
# Minimize window
[Win32.NativeMethods]::ShowWindowAsync($ie.HWND, 2)
# Restore window
[Win32.NativeMethods]::ShowWindowAsync($ie.HWND, 3)
#$ie.FullScreen = $true
Sleep -seconds 3
[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
[System.Windows.Forms.SendKeys]::SendWait("{F11}")
sleep -Seconds 2
Add-Type -AssemblyName System.Drawing
# Create bitmap using the top-left and bottom-right bounds
$bitmap = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $ie.Width, $ie.Height
# Create Graphics object
$graphic = [System.Drawing.Graphics]::FromImage($bitmap)
$ie.Left
$ie.Top
# Capture screen
$graphic.CopyFromScreen($ie.Left, $ie.Top, 0, 0, $bitmap.Size)
# Save to file
$bitmap.Save($File)
Write-Output "Screenshot saved to:"
Write-Output $File
#
Found a solution to this. There is this thing called 'Watin' which helps in automation using IE and Firefox. It has a method to capture full page screenshot. Though screenshot quality is average, Watin works perfectly.
$executingScriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$filePath = $executingScriptDirectory + "\WatiN-2.1.0.1196\bin\net40\WatiN.Core.dll" #$executingScriptDirectory + "\net20\WatiN.Core.dll"
$filePath1 = $executingScriptDirectory + "\WatiN-2.1.0.1196\bin\net40\Interop.SHDocVw.dll"
[System.Reflection.Assembly]::LoadFile($filePath);
[System.Reflection.Assembly]::LoadFile($filePath1);
$url = "https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=how%20to%20send%20keys%20through%20powershell"
$ie = new-object WatiN.Core.IE $URL
while($ie.busy){
sleep -milliseconds 50
}
$ie.BringToFront()
$ie.CaptureWebPageToFile("C:\Sagar\Scripts\IsIt.jpg");
Related
I want to make a scheduled task so I can make a screenshot o a website every hour for monitoring purposes. I am using the following powershell script:
#Open website
start https://nu.nl
#wait
Sleep -Seconds 5
#make screenshot
$File = "C:\Temp\Screenshot1.bmp"
Add-Type -AssemblyName System.Windows.Forms
Add-type -AssemblyName System.Drawing
# Gather Screen resolution information
$Screen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$Width = $Screen.Width
$Height = $Screen.Height
$Left = $Screen.Left
$Top = $Screen.Top
# Set bounds
$bitmap = New-Object System.Drawing.Bitmap $Width, $Height
# Create Object
$graphic = [System.Drawing.Graphics]::FromImage($bitmap)
# Capture
$graphic.CopyFromScreen($Left, $Top, 0, 0, $bitmap.Size)
# Save
$bitmap.Save($File)
We use a automation tool to execute the script but I am getting an error while running it:
Exception calling "CopyFromScreen" with "5" argument(s): "The handle is invalid"
Someone an Idea to resolve this?
When I try this local I have no issue but I'm not able to do it with the automation tool
What I am trying to accomplish is to create buttons that launch exe files in a certain directory when clicked, but when I try using a foreach loop to create a few buttons, all of the buttons just launch the file the last button is supposed to launch.
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Main Window'
$form.Size = New-Object System.Drawing.Size(600,400)
$flp = New-Object System.Windows.Forms.FlowLayoutPanel
$flp.Location = New-Object System.Drawing.Point(0,0)
$flp.Height = $form.Height
$flp.Width = $form.Width
$form.Controls.Add($flp)
$files = Get-ChildItem "$home\Downloads" -Include *.exe -Name
foreach ($file in $files){
$button = New-Object System.Windows.Forms.Button
$flp.Controls.Add($button)
$button.Width = 100
$button.Height = 50
$button.Text = $file
$button.Add_Click{
Start-Process -FilePath "$home\Downloads\$file"
}
}
$form.Topmost = $true
$form.ShowDialog()
Whatever I'm doing is probably pretty stupid, so I was just looking for any alternatives or solutions to this other than to just hard code everything.
It is likely that you need to use .GetNewClosure() ScriptBlock method so that each script block (button click event) holds the current value of the $file variable at the moment of enumeration.
Example of what this means:
$blocks = foreach($i in 0..5) {
{ "hello $i" }
}
& $blocks[0] # => hello 5
& $blocks[1] # => hello 5
$blocks = foreach($i in 0..5) {
{ "hello $i" }.GetNewClosure()
}
& $blocks[0] # => hello 0
& $blocks[1] # => hello 1
In that sense, and assuming this is the issue, the following should work:
foreach ($file in $files) {
$button = New-Object System.Windows.Forms.Button
$button.Width = 100
$button.Height = 50
$button.Text = $file
$thisEvent = {
Start-Process -FilePath "$home\Downloads\$file"
}.GetNewClosure()
$button.Add_Click($thisEvent)
$flp.Controls.Add($button)
}
A nice alternative to having a need to use .GetNewClosure() can be seen on this answer. The .Tag property of the Button can be used to store the information of the file's path which then can be used on the button's .Click event:
foreach ($file in $files) {
$button = New-Object System.Windows.Forms.Button
$button.Width = 100
$button.Height = 50
$button.Text = $file
# Store the file's path in the Tag's property of this Button
$button.Tag = "$home\Downloads\$file"
$button.Add_Click({
Start-Process -FilePath $this.Tag
})
$flp.Controls.Add($button)
}
I wrote a script that works fine:
# Use Internet Explorer
$ie = New-Object -ComObject 'internetExplorer.Application'
$ie.Visible= $true # Make it visible
# Set Credentials
$username="name.surname#mail.com"
$password="password"
#Navigate to URL
$ie.Navigate("https://service.post.ch/zopa/dlc/app/?service=dlc-web&inMobileApp=false&inIframe=false&lang=fr#!/main")
While ($ie.Busy -eq $true) {Start-Sleep -Seconds 3;}
# Login
$usernamefield = $ie.document.getElementByID('isiwebuserid')
$usernamefield.value = "$username"
$passwordfield = $ie.document.getElementByID('isiwebpasswd')
$passwordfield.value = "$password"
$Link = $ie.document.getElementByID('actionLogin')
$Link.click()
Start-Sleep -seconds 5
# Find file to download
$link = $ie.Document.getElementsByTagName('A') | where-object {$_.innerText -like 'post_adressdaten*'}
$link.click()
Start-Sleep -seconds 3
# Press "Alt + s" on the download dialog
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait("%s")
Start-Sleep -seconds 3
# Quit Internet Explorer
$ie.Quit()
But if I change $ie.Visible= $true to $ie.Visible= $false the script doesn't work.
Why?
Because of these two lines:
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait("%s")
In these two lines I'm working on the download dialog box of Internet Explorer and if the browser works in background the the script cannot click on it.
How can I send the input in background or in alternative how to keep Internet Explorer always on top?
As your own answer implies, in order to be able to send keystrokes to an application with [System.Windows.Forms.SendKeys]::SendWait(), it must have a window that is (a) visible and (b) has the (input) focus.
A simpler and faster alternative to the technique shown in your answer - where you use ad-hoc compilation of C# code that wraps WinAPI functions via P/Invoke declarations, via Add-Type - is the following:
# Create an Internet Explorer instance and make it visible.
$ie = New-Object -ComObject 'internetExplorer.Application'; $ie.Visible= $true
# Activate it (give it the focus), via its PID (process ID).
(New-Object -ComObject WScript.Shell).AppActivate(
(Get-Process iexplore | Where-Object MainWindowHandle -eq $ie.hWnd).Id
)
Taking a step back:
GUI scripting (automating a task by simulating user input to a GUI) is inherently unreliable; for instance, the user may click away from the window that is expected to have the focus.
While there is no built in solution, it sounds like Selenium offers robust programmatic browser control.
The third-party Selenium PowerShell module is a PowerShell-friendly wrapper for it (available via the PowerShell Gallery and therefore with Install-Module Selenium), but I don't know if it still works (the project is looking for maintainers as of this writing).
The closest thing I have found is this and it's ugly as hell:
# Start Internet Explorer on top
Add-Type -TypeDefinition #"
using System;
using System.Runtime.InteropServices;
public class Win32SetWindow {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
}
"#
$ie = new-object -comobject InternetExplorer.Application;
$ie.visible = $true;
[Win32SetWindow]::SetForegroundWindow($ie.HWND) # <-- Internet Explorer window on top
# Set Credentials
$username="name.surname#mail.com"
$password="password"
#Navigate to URL
$ie.Navigate("https://service.post.ch/zopa/dlc/app/?service=dlc-web&inMobileApp=false&inIframe=false&lang=fr#!/main")
While ($ie.Busy -eq $true) {Start-Sleep -Seconds 3;}
# Login
$usernamefield = $ie.document.getElementByID('isiwebuserid')
$usernamefield.value = "$username"
$passwordfield = $ie.document.getElementByID('isiwebpasswd')
$passwordfield.value = "$password"
$Link = $ie.document.getElementByID('actionLogin')
$Link.click()
Start-Sleep -seconds 5
# Find file to download
$link = $ie.Document.getElementsByTagName('A') | where-object {$_.innerText -like 'post_adressdaten*'}
$link.click()
Start-Sleep -seconds 3
# Press "Alt + s" on the download dialog
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait("%n{TAB}{ENTER}") # or use SendWait("%s")
Start-Sleep -seconds 3
# Quit Internet Explorer
$ie.Quit()
I'm writing PS Script and following block of code shows dialogbox below windows forms gui.
$btn1 = New-Object Windows.Forms.Button
$btn1.Text = "Wybierz folder projektowy"
$btn1.Location = New-Object System.Drawing.Point(170,140)
$btn1.Size = New-Object System.Drawing.Size(160,20)
$btn1.add_Click({
function Select-Folder($message='Select a folder', $path = 0) {
$object = New-Object -comObject Shell.Application
$object.topmost=$true
$folder = $object.BrowseForFolder(0, $message, 0, $path)
if ($folder -ne $null) {
$folder.self.Path
}
}
$folderPath = Select-Folder 'Select the folder where the move scripts reside'
If ($folderPath) {
Set-Content -Path "C:\Projekty\logs\temp_path.txt" -Value $folderPath.ToString() -Encoding Unicode
write-host $folderPath
get-content -Path "C:\Projekty\logs\temp_path.txt"
}
Else { Write-Host 'I do not have a folder path' }
})
$form_acl.Controls.Add($btn1)
Is there any way to make it display on top?
Here's screenshot of a problem:
You can try this alternative instead:
function Select-Folder {
[CmdletBinding()]
param (
# sets the descriptive text displayed above the tree view control in the dialog box
[Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
[string]$Message = "Please select a directory.",
# sets the (pre)selected path
[Parameter(Mandatory=$false, Position=1)]
[string]$InitialDirectory,
# sets the root folder where the browsing starts from
[Parameter(Mandatory=$false)]
[System.Environment+SpecialFolder]$RootFolder = [System.Environment+SpecialFolder]::Desktop,
# sets a value indicating whether the 'New Folder' button appears in the folder browser dialog box
[switch]$ShowNewFolderButton
)
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog
$dialog.Description = $Message
$dialog.SelectedPath = $InitialDirectory
$dialog.RootFolder = $RootFolder
$dialog.ShowNewFolderButton = $ShowNewFolderButton.IsPresent
$selected = $null
# force the dialog TopMost:
# because the owning window will not be used after the dialog has been closed,
# you can simply create a new form inside the method call.
$result = $dialog.ShowDialog((New-Object System.Windows.Forms.Form -Property #{TopMost = $true; TopLevel = $true}))
if ($result -eq [Windows.Forms.DialogResult]::OK){
$selected = $dialog.SelectedPath
}
# clear the FolderBrowserDialog from memory
$dialog.Dispose()
# return the selected folder
$selected
}
Select-Folder -Message 'Select the folder where the move scripts reside' -ShowNewFolderButton
Theo's helpful answer shows an alternative, WinForms-based way to invoke a folder-browsing dialog, via the System.Windows.Forms.FolderBrowserDialog class.
However, it seems that all that is missing from your original approach is to pass your form's window handle (hWND, .Handle) as the first argument to the Shell.Application COM object's .BrowseForFolder() method, which makes it the dialog's owner window and therefore shows the dialog on top of it - even if the form itself has the .TopMost property set:
$folder = (New-Object -ComObject Shell.Application).BrowseForFolder(
$form_acl.Handle, # Pass your form's window handle to make it the owner window
$message,
0,
$path)
Here's a simplified, self-contained example (requires PSv5+, but can be adapted to earlier versions):
using namespace System.Windows.Forms
using namespace System.Drawing
Add-Type -AssemblyName System.Windows.Forms
# Create a sample topmost form with a single
# button that invokes a folder-browsing dialog.
($form = [Form] #{
Text = "Topmost Form"
Size = [Size]::new(300, 100)
TopMost = $true # Make the form topmost.
StartPosition = 'CenterScreen'
}).Controls.AddRange(#(
($folderBrowseButton = [Button] #{
Location = [Point]::new(70, 20)
Size = [Size]::new(160,30)
Text = 'Browse for Folder'
})
))
$folderBrowseButton.add_Click({
$path = 'C:\'
# IMPORTANT: Pass $form.Handle, the form's window handle (HWND) as the first argument, which
# makes the form the owning window, ensuring that the dialog is displayed on
# top - even if the form itself is set to topmost.
$folder = (New-Object -ComObject Shell.Application).BrowseForFolder(
$form.Handle,
'Pick a target folder:',
0, # Options
$path # Starting directory path.
)
if ($null -ne $folder) {
Write-Verbose -vb ('Folder picked: ' + $folder.self.Path)
} else {
Write-Verbose -vb 'Folder browsing canceled.'
}
})
$null = $form.ShowDialog()
Pardon me if this is too simple a question, but I'm not finding anything in the help files or online so far regarding doing this. I'm opening up a new browser window to test the login/logout feature of a web based application, but I want to open the IE window in maximized mode. I could set the size as:
$ie.height = 1024
$ie.width - 768
But is there a keyword or anything that I can use to just open it up maximized automatically or would I need to query the screen size first and then fill in the values from that query?
/matt
(new-object -com wscript.shell).run("url",3)
Solved the Problem of starting IE maximized with following:
Function maxIE
{
param($ie)
$asm = [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
$ie.Width = $screen.width
$ie.Height =$screen.height
$ie.Top = 0
$ie.Left = 0
}
cls
$ie = new-object -com "InternetExplorer.Application"
$ie.visible = $true
maxIE $ie
while ($ie.busy) {sleep -milliseconds 50}
$ie.navigate("http://www.google.com")
I couldn't get any specific answer to work, but did get a combination to work. The order in which these are called is important else it doesn't work.
Full Example:
$ie = New-Object -Com "InternetExplorer.Application"
$urls = #("http://www.google.com","http://www.yahoo.com")
$ie.Visible = $true
CLS
write-output "Loading pages now..."
#Maximize IE window
$asm = [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
$ie.height = $screen.height
#Open webpages
foreach ($link in $urls) {
$ie.Navigate2($link, 0x1000)
}
#close first blank tab
$sa = New-Object -ComObject Shell.Application
$tab = $sa.windows() | Where {$_.Name -match 'Internet Explorer' -and $_.LocationName -eq ''}
$tab.quit()
If you have the PowerShell Community Extensions 1.2 (PSCX) installed on PowerShell 2.0, I have verified that this works:
Pscx\Start-Process IExplore.exe; Start-Sleep 3; $hwnd = Get-ForegroundWindow
$sig = #'
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
'#
Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32
[Win32.NativeMethods]::ShowWindowAsync($hwnd, 3)
It is a little dicey because it is using a wait (start-sleep) of 3 secs to wait for IE to open and then it uses a PSCX cmdlet to get the window handle of the foreground window. If you only have one instance of IExplore running then you can use this to get that handle:
#(Get-Process IExplore)[0].MainWindowHandle
PowerShell 2.0 is required for the Add-Type support that allows us to call down to the Win32 API.
BTW from a quick Bing search it seems that getting IE to start maximized is a pretty common problem. For instance, with Start-Process you can specify -WindowStyle Maximized but IE doesn't honor that.
Just incase anyone else needs help I ended up just calling up iexplore maximized and then connected to it. You need to sleep because it calls too fast to connect. I'm sure there is a better way but I couldn't figure it out.
start iexplore -WindowStyle maximized
Start-Sleep -seconds 1
$ie = (New-Object -COM "Shell.Application").Windows() | ? { $_.Name -eq "Internet Explorer" }
$ie.navigate("URL")
#We will use the Win32 API function ShowWindowAsync, and spawn an IE Window Maximized.
#Parameters can be used for ShowWindowAsync
$Hide = 0
$Normal = 1
$Minimized = 2
$Maximized = 3
$ShowNoActivateRecentPosition = 4
$Show = 5
$MinimizeActivateNext = 6
$MinimizeNoActivate = 7
$ShowNoActivate = 8
$Restore = 9
$ShowDefault = 10
$ForceMinimize = 11
#Specify an interwebs address :)
$URL="http://www.google.com/"
#Create internetexplorer.application object
$IE=new-object -com internetexplorer.application
#Set some parameters for the internetexplorer.application object
$IE.TheaterMode = $False
$IE.AddressBar = $True
$IE.StatusBar = $False
$IE.MenuBar = $True
$IE.FullScreen = $False
$IE.visible = $True
#Navigate to the URL
$IE.navigate2($URL)
#the C#-style signature of an API function
$code = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
#add signature as new type to PowerShell (for this session)
$type = Add-Type -MemberDefinition $code -Name myAPI -PassThru
#Magic:
$type::ShowWindowAsync($IE.HWND[0], $Maximized)