简体   繁体   中英

Bring window to foreground refuses to work in windows 10

I have a powershell script that opens a program called CNCScreenE, saves a screenshot of this window, and then closes down the application.

it worked perfectly the first few times I ran it.

after those few times, now every time the program gets called, instead of opening in the foreground, it opens behind whatever else is already open (visual studio code in this case). the screenshot still gets saved, but the image contains whatever was in the foreground, which is visual studio code in this case.

I have tried various scripts to bring the window to the foreground that are all basically slight variations on the same thing without much success. they usually just cause the window to flash in the taskbar.

I have found some suggestions that its not always possible to bring windows to the foreground without first fulfilling certain criteria, such as last input must be from a program in the foreground, or you can minimize the window first and then it can be brought to the foreground.

My problem is that I am not very experienced at working with windows api's through powershell. I understand that add-type is compiling the c# code and then allowing powershell to access the api, but I know almost nothing about c# and this is my first time using add-type.

#open screen viewer app
Start-Process  -FilePath 'C:\Program Files (x86)\CNCScreenE\cncscrne.exe' -ArgumentList 'C:\Users\mcnc\Documents\programming\p900_program\p900' -Passthru

start-sleep -seconds 1

#Get PID for p900 screen viewer
$Screen_viewer = (Get-Process -Name 'CNCScrnE').MainWindowHandle

#bring program to the foreground
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);
}
"@

#call class SFW with Function SetForegroundWindow to bring screen viewer to the front
[SFW]::SetForegroundWindow($Screen_viewer)

I also tried this script which looks very well written, but it gives me a series of erros every time I try to run it.

Function Set-WindowStyle 
{
    param
    (
        [Parameter()]
        [ValidateSet('FORCEMINIMIZE', 'HIDE', 'MAXIMIZE', 'MINIMIZE', 'RESTORE', 
            'SHOW', 'SHOWDEFAULT', 'SHOWMAXIMIZED', 'SHOWMINIMIZED', 
            'SHOWMINNOACTIVE', 'SHOWNA', 'SHOWNOACTIVATE', 'SHOWNORMAL')]
        $Style = 'SHOW',
        [Parameter()]
        $MainWindowHandle = (Get-Process -Id $pid).MainWindowHandle
    )

    $WindowStates = @{
        FORCEMINIMIZE = 11; HIDE = 0
        MAXIMIZE = 3; MINIMIZE = 6
        RESTORE = 9; SHOW = 5
        SHOWDEFAULT = 10; SHOWMAXIMIZED = 3
        SHOWMINIMIZED = 2; SHOWMINNOACTIVE = 7
        SHOWNA = 8; SHOWNOACTIVATE = 4
        SHOWNORMAL = 1
    }
    Write-Verbose ("Set Window Style {1} on handle {0}" -f $MainWindowHandle, $($WindowStates[$style]))

    $Win32ShowWindowAsync = Add-Type –memberDefinition @” 
    [DllImport("user32.dll")] 
    public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
“@ -name “Win32ShowWindowAsync” -namespace Win32Functions –passThru

    $Win32ShowWindowAsync::ShowWindowAsync($MainWindowHandle, $WindowStates[$Style]) | Out-Null
}

# Usage

# Minimize a running process window
Get-Process -Name Taskmgr | %{Set-WindowStyle MINIMIZE $PSItem.MainWindowHandle}
Get-Process -Name notepad | %{Set-WindowStyle MINIMIZE $PSItem.MainWindowHandle}

# Restore a running process window - the last window called will be topmost
Get-Process -Name Taskmgr | %{Set-WindowStyle RESTORE $PSItem.MainWindowHandle}
Get-Process -Name notepad | %{Set-WindowStyle RESTORE $PSItem.MainWindowHandle}

the errors it produces are all basically the same as below but refer to a variety of characters:

Add-Type : c:\Users\mcnc\AppData\Local\Temp\oegsfdcr\oegsfdcr.0.cs(1) : Unexpected 
character '€'
c:\Users\mcnc\AppData\Local\Temp\oegsfdcr\oegsfdcr.0.cs(1) : >>> â€memberDefinition @†
c:\Users\mcnc\AppData\Local\Temp\oegsfdcr\oegsfdcr.0.cs(2) :     [DllImport(user32.dll)]  
At C:\Users\mcnc\Documents\programming\p900_program\powershell 
screenshot\show-process.ps1:49 char:29
+     $Win32ShowWindowAsync = Add-Type –memberDefinition @â€
+                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (Microsoft.Power...peCompilerError:AddTypeComp 
   ilerError) [Add-Type], Exception
    + FullyQualifiedErrorId : SOURCE_CODE_ERROR,Microsoft.PowerShell.Commands.AddTypeComm 
   and

I think it has to do with the odd looking quotation marks but when I replace them with regular quotes, it gives these errors which don't make much sense to me

At C:\Users\mcnc\Documents\programming\p900_program\powershell 
screenshot\show-process.ps1:50 char:5
+     [DllImport("user32.dll")]
+     ~~~~~~~~~~~~~~~~~~~~~~~~~
Unexpected attribute 'DllImport'.
At C:\Users\mcnc\Documents\programming\p900_program\powershell 
screenshot\show-process.ps1:51 char:5
+     public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdSh ...
+     ~~~~~~
Unexpected token 'public' in expression or statement.
At C:\Users\mcnc\Documents\programming\p900_program\powershell 
screenshot\show-process.ps1:52 char:11
+ "@ -name "Win32ShowWindowAsync" -namespace Win32Functions –passThru
+           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unexpected token 'Win32ShowWindowAsync" -namespace Win32Functions –passThru' in    
expression or statement.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : UnexpectedAttribute

My best guess is that you should only change the outer quotation marks, like this:

$Win32ShowWindowAsync = Add-Type –memberDefinition @" 
[DllImport("user32.dll")] 
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
"@ -name "Win32ShowWindowAsync" -namespace Win32Functions –passThru

The construct where @" and "@ surround multiple lines is called a here-string. Within the here-string quotation marks should not be quoted.

Well, I tried to do it via c#, and found that SetForegroundWindow will not work. Searching workaround I got this one

So working C# part code will look like:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
public class SFW
{
    [DllImport("user32.dll", SetLastError = true)]
    static extern bool SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool ShowWindowAsync(HandleRef hWnd, int nCmdShow);
    public const int SW_RESTORE = 9;

    public static void BringToFront()
    {
        Process[] processes = Process.GetProcessesByName("CNCScrnE");

        foreach (Process p in processes)
        {
            IntPtr windowHandle = p.MainWindowHandle;
            ShowWindowAsync(new HandleRef(null, windowHandle), SW_RESTORE);
            SetForegroundWindow(windowHandle);
        }
    }
}

Just call BringToFront method from your PowerShell. And you can also remove this part:

    #Get PID for p900 screen viewer
    $Screen_viewer = (Get-Process -Name 'CNCScrnE').MainWindowHandle

as usual, I ended up finding something that works just after posting for help.

also, I think I may have had a working solution already but didn't realize it because I was stepping through the program. I remember seeing the program flash to the foreground and then immediately go behind vsc and I think it was because I had just clicked the step down button. If i had just let it run without any button presses it probably would have worked.

anyway here is the code I found that works like a charm

Function Set-WindowState {
    <#
    .LINK
    https://gist.github.com/Nora-Ballard/11240204
    #>

    [CmdletBinding(DefaultParameterSetName = 'InputObject')]
    param(
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [Object[]] $InputObject,

        [Parameter(Position = 1)]
        [ValidateSet('FORCEMINIMIZE', 'HIDE', 'MAXIMIZE', 'MINIMIZE', 'RESTORE',
                     'SHOW', 'SHOWDEFAULT', 'SHOWMAXIMIZED', 'SHOWMINIMIZED',
                     'SHOWMINNOACTIVE', 'SHOWNA', 'SHOWNOACTIVATE', 'SHOWNORMAL')]
        [string] $State = 'SHOW'
    )

    Begin {
        $WindowStates = @{
            'FORCEMINIMIZE'     = 11
            'HIDE'              = 0
            'MAXIMIZE'          = 3
            'MINIMIZE'          = 6
            'RESTORE'           = 9
            'SHOW'              = 5
            'SHOWDEFAULT'       = 10
            'SHOWMAXIMIZED'     = 3
            'SHOWMINIMIZED'     = 2
            'SHOWMINNOACTIVE'   = 7
            'SHOWNA'            = 8
            'SHOWNOACTIVATE'    = 4
            'SHOWNORMAL'        = 1
        }

        $Win32ShowWindowAsync = Add-Type -MemberDefinition @'
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
'@ -Name "Win32ShowWindowAsync" -Namespace Win32Functions -PassThru

        if (!$global:MainWindowHandles) {
            $global:MainWindowHandles = @{ }
        }
    }

    Process {
        foreach ($process in $InputObject) {
            if ($process.MainWindowHandle -eq 0) {
                if ($global:MainWindowHandles.ContainsKey($process.Id)) {
                    $handle = $global:MainWindowHandles[$process.Id]
                } else {
                    Write-Error "Main Window handle is '0'"
                    continue
                }
            } else {
                $handle = $process.MainWindowHandle
                $global:MainWindowHandles[$process.Id] = $handle
            }

            $Win32ShowWindowAsync::ShowWindowAsync($handle, $WindowStates[$State]) | Out-Null
            Write-Verbose ("Set Window State '{1} on '{0}'" -f $MainWindowHandle, $State)
        }
    }
}

Get-Process -Name CNCScrnE | Set-WindowState -State Minimize
Get-Process -Name CNCScrnE | Set-WindowState -State Restore

and here is where I found it to give credit where its due

https://gist.github.com/lalibi/3762289efc5805f8cfcf

Also I think that you do actually need to minimize it (or otherwise change its state) before bringing it to the foreground otherwise windows will only allow you to activate the window but not bring it forward.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM