Winapi 如何从Powershell事件订阅服务器操作设置前景窗口

Winapi 如何从Powershell事件订阅服务器操作设置前景窗口,winapi,events,powershell,window-management,Winapi,Events,Powershell,Window Management,我有一个FileSystemWatcher实例在我的高级会话后台运行,监视文本文件的更改。PoSh事件订阅者连接到此事件,当被触发时,它通过调用启动进程启动控制台程序。这个程序从当前的前台窗口(我的豪华控制台)窃取去焦点。从PoSh事件订阅者调用SetForegroundWindow将焦点返回到我的PoSh控制台不起作用切换到此窗口在大多数情况下都有效,但根据MSDN文档,不应使用此窗口 在这种情况下,我是否可以防止启动进程窃取焦点,或者将焦点从事件订阅服务器设置回触发此事件之前拥有焦点的窗口?

我有一个FileSystemWatcher实例在我的高级会话后台运行,监视文本文件的更改。PoSh事件订阅者连接到此事件,当被触发时,它通过调用启动进程启动控制台程序。这个程序从当前的前台窗口(我的豪华控制台)窃取去焦点。从PoSh事件订阅者调用SetForegroundWindow将焦点返回到我的PoSh控制台不起作用切换到此窗口在大多数情况下都有效,但根据MSDN文档,不应使用此窗口


在这种情况下,我是否可以防止启动进程窃取焦点,或者将焦点从事件订阅服务器设置回触发此事件之前拥有焦点的窗口?

对于我来说
SetForegroundWindow
工作正常。检查此代码:

Add-Type @"
  using System;
  using System.Runtime.InteropServices;
  public class Tricks {
     [DllImport("user32.dll")]
     [return: MarshalAs(UnmanagedType.Bool)]
     public static extern bool SetForegroundWindow(IntPtr hWnd);
  }
"@
sleep -sec 2
$h = (Get-Process firefox).MainWindowHandle
[void] [Tricks]::SetForegroundWindow($h)
sleep -sec 2
$h = (Get-Process -id $pid).MainWindowHandle
[void] [Tricks]::SetForegroundWindow($h)
但请注意,如果您托管PowerShell或使用例如Console(),则MainWindowHandle是主机程序的句柄。 因此,您需要的不是
(获取进程-id$pid).MainWindowHandle
,而是
[技巧]::SetForegroundWindow((获取进程控制台).MainWindowHandle)

计时器事件的示例:

$timer = New-Object Timers.Timer
$timer.Interval = 5000
$h = (Get-Process -id $pid).MainWindowHandle
$action = { 
    notepad; 
    sleep -sec 1;  # wait until the program starts (very simple approach)
    [Tricks]::SetForegroundWindow($h) }
Register-ObjectEvent $timer Elapsed -Action $action
$timer.Start()

否则,若您运行的进程的窗口被隐藏,它可能会解决您的问题

$ps = new-object system.diagnostics.processstartinfo 'notepad'
$ps.windowStyle = 'hidden'
[system.diagnostics.process]::Start($ps)

从msdn上关于类的文档中选取并修改的示例听起来好像不起作用,因为您在失去焦点之前设置了焦点

你有没有试过通过工作来集中注意力?当您使用控制台时,它在后台运行

像这样的方法可能会奏效,它会在活动结束后保持你的注意力10秒钟

Start-Job -ScriptBlock {
    1..100 | %{
        sleep -Milliseconds 100
        #Set focus back
    }
}
如果你加入了GetForegroundWindow,你可以等到你失去注意力,然后再把它抓回来


当我发现这个问题时,我从上面的@stej-answer中得到了提示,因为我正试图做同样的事情,我扩展生成了这段代码,无论是在ISE、控制台窗口中运行脚本,还是通过cmd提示符(通过批处理文件)运行脚本,这都会使脚本重新成为焦点

或者,为了更方便地重用,将以下内容保存为模块目录中的.psm1文件-从PS v3开始,您不必导入它,在模块目录中调用模块中的函数即可导入它

若要手动导入,
导入模块。\Getfocus.psm1
(假设它位于当前路径中)


您是否再次尝试过
SetForegroundWindow
?我需要在新的ps控制台进程窃取焦点后立即将焦点返回到上一个进程,但不能等待任意预定义的秒数来绘制新窗口。我想如果不等待,就不可能实现。考虑运行该进程,但窗口在某个时间之后出现,然后它将捕获焦点。您需要确定何时恢复焦点。你执行什么过程?我想答案是使用
CreateProcess
STARTUP\u INFO
dwFlags
wShowWindow
。这将涉及在powershell脚本中嵌入一个C#类来执行API调用,因为.NET的
进程
类似乎不会公开
dwFlags
wShowWindow
,或者至少不会公开它们的所有有效值。
#bring script back into focus
Add-Type @"
  using System;
  using System.Runtime.InteropServices;
  public class Tricks {
     [DllImport("user32.dll")]
     [return: MarshalAs(UnmanagedType.Bool)]
     public static extern bool SetForegroundWindow(IntPtr hWnd);
  }
"@

$parent = Get-Process -id ((gwmi win32_process -Filter "processid='$pid'").parentprocessid)
If ($parent.Name -eq "cmd") {# Being run by via cmd prompt (batch file)
    $h = (Get-Process cmd).MainWindowHandle
    [void] [Tricks]::SetForegroundWindow($h)
    }
    else{# being run in powershell ISE or console
          $h = (Get-Process -id $pid).MainWindowHandle
          [void] [Tricks]::SetForegroundWindow($h)
    } 
Function Get-Focus{
#bring script back into focus
Add-Type @"
  using System;
  using System.Runtime.InteropServices;
  public class Tricks {
     [DllImport("user32.dll")]
     [return: MarshalAs(UnmanagedType.Bool)]
     public static extern bool SetForegroundWindow(IntPtr hWnd);
  }
"@

$parent = Get-Process -id ((gwmi win32_process -Filter "processid='$pid'").parentprocessid)
If ($parent.Name -eq "cmd") {# Being run by via cmd prompt (batch file)
    $h = (Get-Process cmd).MainWindowHandle
    [void] [Tricks]::SetForegroundWindow($h)
    }
    else{# being run in powershell ISE or console
          $h = (Get-Process -id $pid).MainWindowHandle
          [void] [Tricks]::SetForegroundWindow($h)
    }
} 

Export-ModuleMember -Function Get-Focus