How to set foreground Window from Powershell event subscriber action

有些话、适合烂在心里 提交于 2019-12-07 15:20:28

问题


I have a FileSystemWatcher instance running in the background of my PoSh session watching for changes to text files. A PoSh event subscriber is attached to this event and, when fired, it launches a console program by calling Start-Process. This program steals de focus from the current foreground window (my PoSh console). Calling SetForegroundWindow from the PoSh event subscriber to return the focus to my PoSh console doesn't work. SwitchToThisWindow does work most of the time, but according to the MSDN docs, it shoulnd't be used.

Can I prevent Start-Process from stealing the focus in this situation or set it back from the event subscriber to the window that had it before this event is fired?


回答1:


For me SetForegroundWindow works well. Check this code:

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)

But note that if you host PowerShell or use e.g. Console (http://sourceforge.net/projects/console/) then MainWindowHandle is the handle of your host program. So instead of (Get-Process -id $pid).MainWindowHandle you would need [tricks]::SetForegroundWindow((Get-Process console).MainWindowHandle).

Example with timer event:

$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()

Otherwise if you run process that has its window hidden, it could solve your problem.

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

Example taken and altered from documentation on msdn about Process class




回答2:


It sounds like it didnt work because you set focus, before you lost focus.

Have you tried setting focus through a job? It runs in the background while you use the console.

Something like this might work, it keeps your focus for 10 seconds after the event

Start-Job -ScriptBlock {
    1..100 | %{
        sleep -Milliseconds 100
        #Set focus back
    }
}

If you mix in GetForegroundWindow, you can wait till you lose focus, then grab it back

http://www.leeholmes.com/blog/MorePInvokeInPowerShell.aspx




回答3:


Taking my cue from @stej answer above when I found this question because I was trying to do the same thing, I expanded to produce this code, which will bring the script back into focus whether being run in the ISE, console window, or via cmd prompt (through a batch file).

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

Or to re-use more readily, save the following as a .psm1 file in your module directory - from PS v3 onwards, you don't have to import it, calling a function in a module in your module directory imports it.

To import manually, Import-Module .\Getfocus.psm1 (Assuming it's in your current path).

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


来源:https://stackoverflow.com/questions/2556872/how-to-set-foreground-window-from-powershell-event-subscriber-action

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!