PowerShell:带有 PID 的 AppActivate 不起作用

标签 powershell

在 Windows 10 下,我想将按键发送到现有且正在运行的 Windows 应用程序。按键发送工作正常,但通过 Windows 应用程序 PID 的 AppActivate 不起作用。这是我的代码:

Function SendCommandToExistingProcess([int] $processId, [string] $processName, [string] $command)
{
  $functName = 'SendCommandToExistingProcess()' # function name for log

  Add-Type -AssemblyName Microsoft.VisualBasic
  Add-Type -AssemblyName System.Windows.Forms
  [Microsoft.VisualBasic.Interaction]::AppActivate($processId)
  [System.Windows.Forms.SendKeys]::SendWait($command)

  WriteLogEntry -severity $sevInfo -functName $functName `
                -entryText ("Command '" + $command + "' sent to process '" + `
                            $processName + "' with id '" + $processId)
}
  • $processId 包含要设置焦点的 Windows 应用程序的 PID
  • $command 包含要发送的按键 ('p')

对于AppActivate,我使用PID而不是应用程序窗口标题,因为应用程序标题包含两个特殊字符(类似于版权符号)。看来 AppActivate 只适用于标题(已成功测试),但不适用于 PID,尽管 AppActivate 文档显示了接受 PID 的 AppActivate 覆盖层。我尝试通过直接在 AppActivate 中输入 PID 作为数字来将焦点设置到 Windows 计算器;没有工作。

*** 更新 ***

PID的获取方式如下:

Function SendCommandToProcess([string] $processName, [string] $command)
{
  $result    = $false                   # initialise to process does not exist
  $functName = 'SendCommandToProcess()' # function name for log

  $processId = (Get-Process -Name $processName -erroraction 'silentlycontinue').Id
  if ($processId.Count -gt 0)           # procss(es) exist(s)
  {                                     # normally just one process but could be serveral
    Foreach ($id IN $processId)
    { SendCommandToExistingProcess -processId $id -processName $processName -command $command }
                                        # send command to each of them
    $result = $true                     # command sent to specified process
  }
  else
  {
    WriteLogEntry -severity $sevWarning -functName $functName `
                  -entryText ("Process '" + $processName + "' not found (not running)")
  }

  return $result
}
  • $processName 包含字符串“Prepar3D”

当我在具有管理员权限的 PowerShell 中运行上述代码时,我收到以下错误消息:

  • Ausnahme beim Aufrufen von “AppActivate” mit 1 Argument(en):“Der Prozess {0} wurde nicht gefunden。”
  • 英语:使用 1 个参数调用“AppActivate”时出现异常:“找不到进程 {0}”

什么在愚弄我?感谢您的帮助 汉内斯

最佳答案

我有同样的问题,因为我想关注一个名为 ace.exe 的应用程序。对我有用的解决方案是遵循本指南:

https://powershell.one/powershell-internals/extending-powershell/vbscript-and-csharp#c-to-the-rescue

在此,Tobias Weltner 博士讨论了关注应用程序窗口的 3 种方法。对您有用的一章是“C# 救援”。

我使用了 admin ISE-PowerShell 并从上面的链接复制了代码。 (章节:C# 的救援)

请勿复制此代码,而是按照链接页面上的说明进行操作。

这是我复制和使用的代码:

PS C:\Windows\system32> $code = @'
using System;
using System.Runtime.InteropServices;

namespace API
{

    public class FocusWindow
    {
        [DllImport("User32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern bool AttachThreadInput(IntPtr idAttach, IntPtr idAttachTo, bool fAttach);


        [DllImport("User32.dll")]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("User32.dll")]
        private static extern IntPtr GetWindowThreadProcessId(IntPtr hwnd, IntPtr lpdwProcessId);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        [DllImport("user32")]
        private static extern int BringWindowToTop(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool SystemParametersInfo(uint uiAction, uint uiParam, IntPtr pvParam, uint fWinIni);

        private const uint SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000;
        private const uint SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001;
        private const int SPIF_SENDCHANGE = 0x2;

        private const int SW_HIDE = 0;
        private const int SW_SHOWNORMAL = 1;
        private const int SW_NORMAL = 1;
        private const int SW_SHOWMINIMIZED = 2;
        private const int SW_SHOWMAXIMIZED = 3;
        private const int SW_MAXIMIZE = 3;
        private const int SW_SHOWNOACTIVATE = 4;
        private const int SW_SHOW = 5;
        private const int SW_MINIMIZE = 6;
        private const int SW_SHOWMINNOACTIVE = 7;
        private const int SW_SHOWNA = 8;
        private const int SW_RESTORE = 9;
        private const int SW_SHOWDEFAULT = 10;
        private const int SW_MAX = 10;

        public static void Focus(IntPtr windowHandle)
        {
            IntPtr blockingThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
            IntPtr ownThread = GetWindowThreadProcessId(windowHandle, IntPtr.Zero);

            if (blockingThread == ownThread || blockingThread == IntPtr.Zero)
            {
                SetForegroundWindow(windowHandle);
                ShowWindow(windowHandle, 3);
            }
            else
            {
                if (AttachThreadInput(ownThread, blockingThread, true))
                {
                    BringWindowToTop(windowHandle);
                    SetForegroundWindow(windowHandle);
                    ShowWindow(windowHandle, SW_MAXIMIZE);
                    AttachThreadInput(ownThread, blockingThread, false);
                }
            }

            if (GetForegroundWindow() != windowHandle)
            {
                IntPtr Timeout = IntPtr.Zero;
                SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, Timeout, 0);
                SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, IntPtr.Zero, SPIF_SENDCHANGE);
                BringWindowToTop(windowHandle);
                SetForegroundWindow(windowHandle);
                ShowWindow(windowHandle, SW_MAXIMIZE);
                SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Timeout, SPIF_SENDCHANGE);
            }
        }
    }
}
'@

# remove -PassThru in production. It is used only to
# expose the added types:
Add-Type -PassThru -TypeDefinition $code

IsPublic IsSerial Name                                     BaseType                                                                                                      
-------- -------- ----                                     --------                                                                                                      
True     False    FocusWindow                              System.Object                                                                                                 



PS C:\Windows\system32> Add-Type -PassThru -TypeDefinition $code |
  Where-Object IsPublic |
  Select-Object -Property FullName

FullName       
--------       
API.FocusWindow



PS C:\Windows\system32> # get the main window handle for the process
# you want to switch to the foreground:

# in this example, the first instance of notepad is used
# (make sure notepad runs)
$process = Get-Process -Name aces -ErrorAction SilentlyContinue | 
    Select-Object -First 1

$mainWindowHandle = $process.MainWindowHandle

if (!$mainWindowHandle)
{
    Write-Host "Window may be minimized, or process may not be running" -Foreground Red
}

# focus application window (and maximize it)
[API.FocusWindow]::Focus($mainWindowHandle)

PS C:\Windows\system32> 

我还尝试使用 nuget 包“PSOneApplicationWindow”,该包位于提供的链接的第一章中,但无法将焦点集中在 ace.exe 上。

关于PowerShell:带有 PID 的 AppActivate 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65182731/

相关文章:

powershell - Powershell cmdlet忽略数组参数

powershell - 如何批量清空回收站

arrays - 如果数组中的元素匹配值,则删除它们

mysql - PowerShell MySQL 转储脚本未正确转储

powershell - 获取成员(member)并在 Powershell 中浏览 .Net

powershell - Powershell中的start-process执行完成后自动关闭窗口

Azure DevOps Pipeline 条件脚本参数

O365 GUI 的 Powershell : permissions

powershell - Powershell静默卸载 “Microsoft Report Viewer Runtime 2012”

shell - Jenkins Pipeline Access 阶段步骤变量在 powershell 执行中