【问题标题】:Bring window to foreground refuses to work in windows 10将窗口置于前台拒绝在 Windows 10 中工作
【发布时间】:2021-06-01 09:50:58
【问题描述】:

我有一个 powershell 脚本,它打开一个名为 CNCScreenE 的程序,保存此窗口的屏幕截图,然后关闭该应用程序。

在我运行它的前几次它运行良好。

在这几次之后,现在每次调用程序时,它都不是在前台打开,而是在其他已经打开的东西后面打开(在这种情况下是 Visual Studio 代码)。屏幕截图仍然被保存,但图像包含前景中的任何内容,在这种情况下是 Visual Studio 代码。

我尝试了各种脚本来将窗口置于前台,这些脚本基本上都是同一事物的细微变化,但没有取得多大成功。它们通常只会导致窗口在任务栏中闪烁。

我发现了一些建议,在不首先满足某些条件的情况下,并不总是可以将窗口带到前台,例如最后一个输入必须来自前台的程序,或者您可以先最小化窗口,然后可以被带到前台。

我的问题是我在通过 powershell 使用 windows api 方面不是很有经验。我知道add-type是编译c#代码,然后允许powershell访问api,但我对c#几乎一无所知,这是我第一次使用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)

我也试过这个看起来写得很好的脚本,但每次我尝试运行它时都会出现一系列错误。

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}

它产生的错误都与下面基本相同,但指的是各种字符:

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

我认为这与看起来很奇怪的引号有关,但是当我用常规引号替换它们时,它会给出这些对我来说没有多大意义的错误

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

【问题讨论】:

    标签: c# powershell user32 setforegroundwindow


    【解决方案1】:

    像往常一样,我在发帖寻求帮助后发现了一些有用的东西。

    另外,我想我可能已经有了一个可行的解决方案,但没有意识到,因为我正在逐步执行该程序。我记得看到程序闪到前台,然后立即转到 vsc 后面,我想这是因为我刚刚单击了降级按钮。如果我只是让它运行而不按任何按钮,它可能会起作用。

    无论如何,这里是我发现的像魅力一样工作的代码

    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
    

    这是我发现它在应得的地方给予信任的地方

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

    另外我认为您确实需要在将其置于前台之前将其最小化(或以其他方式更改其状态),否则窗口将只允许您激活窗口但不能将其向前推进。

    【讨论】:

      【解决方案2】:

      好吧,我尝试通过 c# 来做,发现 SetForegroundWindow 不起作用。搜索解决方法我得到了this one

      所以工作的 C# 部分代码将如下所示:

      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);
              }
          }
      }
      

      只需从 PowerShell 调用 BringToFront 方法。你也可以去掉这部分:

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

      【讨论】:

        【解决方案3】:

        我最好的猜测是您应该只更改外部引号,如下所示:

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

        @" 和 "@ 包围多行的结构称为此处字符串。此处字符串内的引号不应加引号。

        【讨论】:

        • 我尝试了您的建议,它产生的第二个错误与我原来的问题中发布的相同。我还尝试将 Win32ShowWindowAsync 周围的引号替换为常规引号并将 Here-String 引号保留原样,它产生了我发布的第一个错误。
        • 我编辑了答案。我忘了更改 Win32ShowWindowAsync 周围的引号。
        猜你喜欢
        • 2020-04-26
        • 2012-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-19
        • 2010-09-17
        • 2021-03-10
        相关资源
        最近更新 更多