Powershell New-Object -comobject InternetExplorer.Application bring to front

5.4k Views Asked by At

I searched but didn't find anything that specifically addresses this question.

Consider the code, can the new IE window be created in front of all other open windows? VBScript had the Run Method (Windows Script Host) that offered intWindowStyle Optional. Integer value indicating the appearance of the program's window. Note that not all programs make use of this information.

Does Powershell's new com object have anything similar?

Param(
[Parameter(Mandatory=$false)]
[string]$svr,
[Parameter(Mandatory=$false)]
[string]$last,
[Parameter(Mandatory=$false)]
[string]$desc,
[Parameter(Mandatory=$false)]
[string]$date)
$ie = new-object -comobject InternetExplorer.Application
$ie.navigate("http://www.stackoverflow.com")
# Wait for the page to finish loading
 do {sleep 1} until (-not ($ie.Busy))
$doc = $ie.document
$link = $doc.getElementById("Sym_Msg").Value = "$svr"
$link = $doc.getElementById("Lname").Value = "$last"
$link = $doc.getElementById("Desc").Value = "$desc"
$link = $doc.getElementById("Date_Ent").Value = "$date"
$button = $doc.getElementById("submit1")
$ie.Visible = $true
$button.click();
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ie) | Out-Null
5

There are 5 best solutions below

2
On BEST ANSWER

Unfortunately I don’t think this is part of ‘new-object’. There are several PowerShell extensions that implement this as cmdlet (Such as http://pscx.codeplex.com/ "Set-ForegroundWindow").

The way this is implemented is a call to "user32.dll".

In your code this should work:

#Load DLL
$pinvoke = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow)'   
Add-Type -MemberDefinition $pinvoke -name NativeMethods -namespace Win32

#Get WindowHandle of the COM Object
$hwnd = $ie.HWND

# Minimize window
[Win32.NativeMethods]::ShowWindowAsync($hwnd, 2)

# Restore window
[Win32.NativeMethods]::ShowWindowAsync($hwnd, 4)

--Edited to add ' after nCmdShow)

6
On

For future reference, the only way to make a window topmost involves some P/Invoke digging into Win32 APIs. Fun stuff, especially from PowerShell!

Add-Type -Namespace PInvoke -Name SWP '[DllImport("user32.dll", SetLastError=true)] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);'
[PInvoke.SWP]::SetWindowPos($hWnd, -1, 0, 0, 0, 0, 3)

This code was cobbled together from this answer, Lee Holmes' old blog post on P/Invoke in PoSh, and the pinvoke.net/MSDN docs.

0
On

Finally with the pointer from @Nathan Tuggy, I discovered this page which ends up working great. The new IE window is created then brought to the front of all open windows.

Summary of code in PowerShell to create a new IE ComObject and bring it to the front.

$ie = New-Object -ComObject InternetExplorer.Application
$ie.navigate("http://www.stackoverflow.com")
do {sleep 1} until (-not ($ie.Busy))
$signature = @"

[DllImport("user32.dll")]  
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);  

public static IntPtr FindWindow(string windowName){
    return FindWindow(null,windowName);
}

[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, 
IntPtr hWndInsertAfter, int X,int Y, int cx, int cy, uint uFlags);

[DllImport("user32.dll")]  
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); 

static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);

const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;

const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

public static void MakeTopMost (IntPtr fHandle)
{
    SetWindowPos(fHandle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
}

public static void MakeNormal (IntPtr fHandle)
{
    SetWindowPos(fHandle, HWND_NOTOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
}
"@
$hWnd = $ie.HWND
$app = Add-Type -MemberDefinition $signature -Name Win32Window -Namespace ScriptFanatic.WinAPI -ReferencedAssemblies System.Windows.Forms -Using System.Windows.Forms -PassThru
$null = $app::MakeTopMost($hWnd)
$ie.Visible = $true
$button.click();
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ie) | Out-Null
0
On

Preface:

  • Given the obsoleteness of Internet Explorer, Excel (Excel.Application) is used in the example below. The solution should generally work for out-of-process COM Automation servers, including others such as Microsoft Word (Word.Application).

  • Activating a window (making it the foreground window with the input focus) isn't always allowed by the system, but does work if the caller is itself the foreground window at the time of trying to activate a different process' window. When the system prevents activation, the target window's taskbar icon flashes a few times instead of the window getting activated.

    • For a WinAPI-assisted solution that allows activation in other scenarios too (use with caution), see this answer.

There is an alternative to a P/Invoke-solution that calls Windows APIs via C# code that is ad hoc-compiled using Add-Type (which is not only complex, but entails a compilation runtime penalty on first use in a given session):

(New-Object -ComObject WScript.Shell).AppActivate(...) can be used, which accepts either a window title (or substring thereof) or, more robustly, a PID (process ID) to activate the window / process of interest.

The following, self-contained Excel example demonstrates this, using the PID of the Excel process.[1]

& {

  # Create the Excel COM object (out of process).
  $xl = New-Object -ComObject Excel.Application
  # Determine the PID (process ID) of the newly launched Excel process.
  $xlPid = (Get-Process Excel | Sort-Object StartTime | Select-Object -Last 1).Id
  
  # Before making Excel visible, open a workbook, for instance.
  # Be sure to use a *full path*.
  # $wb = $xl.Workbooks.Open("$PWD/book1.xlsx")

  # Make Excel visible.
  $xl.Visible = $true

  # Activate it (make it the foreground window), via its PID.
  $null = (New-Object -ComObject WScript.Shell).AppActivate($xlPid)

  # Wait for the user to press Enter before quitting Excel.
  $null = Read-Host 'Press Enter to quit Excel'
  $xl.Quit()

}

Note:

  • By executing the code in a child scope, via a & { ... }, there is no need to manually release any of the COM objects created; once .Quit() is called and the variables storing COM objects have gone out of scope, all references are implicitly released, and the Excel process will disappear after a couple of seconds - see this answer for more information.

[1] Note: The way that the PID is determined - by obtaining it from the most recently launched process named Excel, is hypothetically not fully robust, but very likely to work as intended in practice. The - cumbersome - alternative would be to derive the PID from the .hWnd value of the Excel object, but that would again require P/Invoke calls, which defeats the simplicity of the (New-Object -ComObject WScript.Shell).AppActivate(...) approach.

0
On

This was incredibly helpful for me. I needed to do the same with a windows form, not IE, and so I needed to do a few tweaks. I borrowed the form creation code from someone else on the internet.

This works great when the SCCM client executes a script-based installer.

In particular I had to add the following at the end, and tweak a few other things:

$appContext = New-Object System.Windows.Forms.ApplicationContext 
[void][System.Windows.Forms.Application]::Run($appContext)

Here's the code I used:

Add-Type -AssemblyName PresentationFramework, System.Drawing, System.Windows.Forms, WindowsFormsIntegration

    $objForm = New-Object System.Windows.Forms.Form 
    $objForm.Text = "Test"
    $objForm.Size = New-Object System.Drawing.Size(400,200) 
    $objForm.StartPosition = "CenterScreen"
    $objform.icon = "$StrInstDir\source\favicon.ico"

    $objForm.KeyPreview = $True
    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") 
        {$output = "OK";$objForm.Close()}})
    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") 
        {$output = "Cancel";$objForm.Close()}})

    $OKButton = New-Object System.Windows.Forms.Button
    $OKButton.Location = New-Object System.Drawing.Size(105,120)
    $OKButton.Size = New-Object System.Drawing.Size(75,23)
    $OKButton.Text = "OK"
    $OKButton.Add_Click({$global:output="OK";$objForm.Close()})
    $objForm.Controls.Add($OKButton)

    $CancelButton = New-Object System.Windows.Forms.Button
    $CancelButton.Location = New-Object System.Drawing.Size(220,120)
    $CancelButton.Size = New-Object System.Drawing.Size(75,23)
    $CancelButton.Text = "Cancel"
    $CancelButton.Add_Click({$global:output = "Cancel";$objForm.Close()})
    $objForm.Controls.Add($CancelButton)

    $objform.Add_Closing({[System.Windows.Forms.Application]::Exit()})

    $objLabel = New-Object System.Windows.Forms.Label
    $objLabel.Location = New-Object System.Drawing.Size(60,20) 
    $objLabel.Size = New-Object System.Drawing.Size(280,200) 
    $objLabel.Text = "Test Text"
    $objForm.Controls.Add($objLabel) 


    [void] $objForm.Show()
    $objForm.Add_Shown({$objForm.Activate()})



    $signature = @"

    [DllImport("user32.dll")]  
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);  

    public static IntPtr FindWindow(string windowName){
        return FindWindow(null,windowName);
    }

    [DllImport("user32.dll")]
    public static extern bool SetWindowPos(IntPtr hWnd, 
    IntPtr hWndInsertAfter, int X,int Y, int cx, int cy, uint uFlags);

    [DllImport("user32.dll")]  
    public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); 

    static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
    static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);

    const UInt32 SWP_NOSIZE = 0x0001;
    const UInt32 SWP_NOMOVE = 0x0002;

    const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

    public static void MakeTopMost (IntPtr fHandle)
    {
        SetWindowPos(fHandle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
    }

    public static void MakeNormal (IntPtr fHandle)
    {
        SetWindowPos(fHandle, HWND_NOTOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
    }
"@
    $hWnd = $objform.handle
    $app = Add-Type -MemberDefinition $signature -Name Win32Window -Namespace ScriptFanatic.WinAPI -ReferencedAssemblies System.Windows.Forms -Using System.Windows.Forms -PassThru
    $null = $app::MakeTopMost($hWnd)
    $objform.Visible = $true

    $appContext = New-Object System.Windows.Forms.ApplicationContext 
    [void][System.Windows.Forms.Application]::Run($appContext)