I have the following two line code which will bring the respective window to the front that will help me take screenshots automatically. But the problem is that, sometimes, it works like a charm and sometimes it doesn't. And when I say it doesn't work, I don't mean that it is failing with any error message or warnings. Nothing happens. Just the code runs and the windows doesn't come to the front.
I would like to know if there is any hidden prerequisites for this snippet to run successfully? Should the window in question should be in any state before I run this code to bring that window upfront?
Here is the code:
[void][System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
[Microsoft.VisualBasic.Interaction]::AppActivate("R")
I've had trouble using the Title-overload before. Also, a single change in the windows title will break AppActivate(string Title)
.
Does it work better with the ProcessID
-overload? Ex.
$excelpid = (Get-Process excel).ID
[Microsoft.VisualBasic.Interaction]::AppActivate($excelpid)
If you're using a COM-application, you need to get the process based on the MainWindowHandle
, like this:
[void][System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
#start excel
$excel = New-Object -ComObject excel.application
#make excel visible
$excel.Visible = $true
#get pid from windows handle
$excelpid = (Get-Process | Where-Object { $_.MainWindowHandle -eq $excel.Hwnd }).Id
#Activate window
[Microsoft.VisualBasic.Interaction]::AppActivate($excelpid)
Possibilities are that the program takes the control back as soon as powershell switches control to the desired program... Try to loop the activate until you take the screenshot...
I simply iterate AppActivate several times.I know this is not beautiful solution. But it works for me.
sample code here.
for ($i = 1; $i -lt 5; $i++)
{
[Microsoft.VisualBasic.Interaction]::AppActivate("R")
}
$IEtitles=get-process iexplore |?{$_.MainWindowTitle}|select -exp MainWindowTitle
foreach($IEtitle in $IEtitles)
{
Add-Type -AssemblyName Microsoft.VisualBasic
[Microsoft.VisualBasic.Interaction]::AppActivate("$IEtitle") | Out-Null
}
Using AppActivate to bring out window title sometimes behaves indeed quite weird: no exception occurs and checking foreground window handle(using Win32 API: GetForegroundWindow) is accurate either, except that the window is still not showing anyway.
To make sure the window does show before everything is completed, here is an alternative to using AppActivate. I tried using win32 API: "ShowWindowAsync" and "SetForegroundWindow" together, and check the return value to make sure. It works much more consistently than AppActivate.
(P.S. ShowWindowAsync: "Sets the show state of a window without waiting for the operation to complete.", according to Microsoft online docs).
Code example: Assuming that the window title "R" already exists.
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class SFW {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
}
"@
$Target_Title="R"
$hwnd =(Get-Process | Where-Object { $_.MainWindowTitle -eq $Target_Title } ).MainWindowHandle
Do
{
try
{
$r1=[SFW]::ShowWindowAsync($hwnd, 3)
$r2=[SFW]::SetForegroundWindow($hwnd)
}
catch
{
Write-Host "Error activating! Press any key to exit!"
cmd /c pause | out-null
exit
}
}while(($r1 -or $r2) -eq $false)