在 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/