6

I understand that this can easily be accomplished by using alt-tab, but the main purpose of this script's creation is to teach myself some PowerShell basics.

I'm writing a script that, when run, toggles the foreground window between powershell and the current foreground window. I read this question and used one of the answers to get the code for retrieving the current foreground window but it doesn't seem to grab the correct window - it instead seems to grab explorer.exe

Below is my code for the script with hopefully helpful comments:

# Toggle-PowerShell.ps1
# Toggles focus between powershell and the current active window.
# If this script isn't run with -NoProfile, it will switch focus to itself.

. $PSScriptRoot\..\Functions\Toggle-Window.ps1

Add-Type @"
  using System;
  using System.Runtime.InteropServices;
  public class Util {
    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();
}
"@

$a = [util]::GetForegroundWindow()

# Get-Unique may be unnecessary here, but including it for the case when
# working with Chrome as the stored process
$storedProcess=get-process | ? { $_.mainwindowhandle -eq $a } | Get-Unique

If(Test-Path $PSScriptRoot\Toggle-PowerShell.temp)
{
    $tempArray=(Get-Content $PSScriptRoot\Toggle-Powershell.temp)

    # the id number is at index three of tempArray
    Show-Process -Process (Get-Process -id $tempArray[3])

    # delete the file so the next time we run the script it toggles to PS
    Remove-Item $PSScriptRoot\Toggle-PowerShell.temp
} Else
{
    $propertiesFile=$PSScriptRoot\..\currentSession.properties
    $propertiesMap = convertfrom-stringdata (get-content $propertiesfile -raw)

    Show-Process -Process (Get-Process -id $propertiesMap.'PowerShellPID')

    # write a new temp file that contains the stored process's id
    # so that the next time this script is run it toggles back
    $storedProcess | Select-Object Id > $PSScriptRoot\Toggle-PowerShell.temp
}

I was thinking perhaps that maybe I should try getting the active window instead of the foreground window but another question's answer said that foreground implies active.

I'm on Windows 10.

EDIT: I think that it may be using explorer.exe as the 'foreground window' because I call the script via a shortcut, which launches from explorer.

mystbinder
  • 73
  • 1
  • 1
  • 6
  • Run the script from a command prompt and see if it still returns Explorer.exe as the foreground window. – TheMadTechnician Sep 21 '17 at 22:08
  • It returned the PID of the prompt it was run in. – mystbinder Sep 22 '17 at 01:42
  • So what is the goal here? – TheMadTechnician Sep 22 '17 at 17:31
  • Pretty much I want to have a keyboard shortcut that when pressed, stores the current process (for example, say I'm doing some research on Chrome, then it would store the browser's PID) in memory and switches to powershell. Then, when pressed again, switches back to the stored process. The way I was going about that was to write this script and then creating a keyboard shortcut that calls it. – mystbinder Sep 22 '17 at 18:22

2 Answers2

2

Try this one to get the ID of the process you like or just get it by any other way you prefer.

Add-Type @"
  using System;
  using System.Runtime.InteropServices;
  public class Tricks {
    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();
}
"@

$a = [tricks]::GetForegroundWindow()

$WH = get-process | ? { $_.mainwindowhandle -eq $a }

Now, imagine that you active window is another one and you would like to go back to the one related to $WH. Just use the following code to move back to it. You can do it with whatever trigger you like, as you mentioned with keyboard hotkey or automatically.

$sig = '
    [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
    [DllImport("user32.dll")] public static extern int SetForegroundWindow(IntPtr hwnd);
'

$type = Add-Type -MemberDefinition $sig -Name WindowAPI -PassThru
# maximize the window related to $WH; feel free to play with the number
$null = $type::ShowWindowAsync($WH, 4)
# change the focus to $WH
$null = $type::SetForegroundWindow($WH) 
Alin
  • 350
  • 2
  • 13
1

I found this fast and non-CPU intensive solution from Justin Rich at social.technet.microsoft.com

Add-Type  @"
 using System;
 using System.Runtime.InteropServices;
 using System.Text;
public class APIFuncs
   {
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
   public static extern int GetWindowText(IntPtr hwnd,StringBuilder
lpString, int cch);
    [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
   public static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
       public static extern Int32 GetWindowThreadProcessId(IntPtr hWnd,out
Int32 lpdwProcessId);
    [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
       public static extern Int32 GetWindowTextLength(IntPtr hWnd);
    }
"@
 
while(1)
{
$w = [apifuncs]::GetForegroundWindow()
$len = [apifuncs]::GetWindowTextLength($w)
$sb = New-Object text.stringbuilder -ArgumentList ($len + 1)
$rtnlen = [apifuncs]::GetWindowText($w,$sb,$sb.Capacity)
write-host "Window Title: $($sb.tostring())"
sleep 1
} 
ndemou
  • 4,691
  • 2
  • 30
  • 33