0

我有一个 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
4

3 回答 3

0

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

另外,我想我可能已经有了一个可行的解决方案,但没有意识到,因为我正在逐步执行该程序。我记得看到程序闪到前台,然后立即转到 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

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

于 2021-03-02T21:41:26.573 回答
0

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

所以工作 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
于 2021-03-02T20:59:30.573 回答
0

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

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

@" 和 "@ 包围多行的结构称为此处字符串。here-string 内的引号不应被引用。

于 2021-03-02T20:34:28.847 回答