Search code examples
.netpowershellglobal-hotkey

Using global hotkeys in Powershell


I'm currently making a very basic script that takes a screenshot of the computer every 10 minutes. However, I've now been asked to also add in the option to click a hotkey to activate it manually.

I found this, which essentially helps me some of the way:

Add-Type -TypeDefinition '
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace KeyLogger {
  public static class Program {
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;

    private static HookProc hookProc = HookCallback;
    private static IntPtr hookId = IntPtr.Zero;
    private static int keyCode = 0;

    [DllImport("user32.dll")]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll")]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll")]
    private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    public static int WaitForKey() {

        hookId = SetHook(hookProc);
        Application.Run();
        UnhookWindowsHookEx(hookId);
        return keyCode;

    }

    private static IntPtr SetHook(HookProc hookProc) {
          IntPtr moduleHandle = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
          return SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, moduleHandle, 0);
    }

    private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
          if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) {
            keyCode = Marshal.ReadInt32(lParam);
            Application.Exit();
      }
      return CallNextHookEx(hookId, nCode, wParam, lParam);
    }
  }
}
' -ReferencedAssemblies System.Windows.Forms

DO
{
    $IsTrue = $true
    while ($IsTrue -eq $true) {        
        $key = [System.Windows.Forms.Keys][KeyLogger.Program]::WaitForKey()
        if ($key -eq "F2") {
            
            #Do something
            
            $IsTrue = $false
        }
    }

} Until ($SomeValue -eq $true)

Problem is, how do I have this work, so that if the key hasn't been pressed in 10 minutes, the script will then execute the remainder of the script? Because as it is right now, it waits until a key has pressed, before going any further.

Thanks in advance!


Solution

  • you are going to need the following in the while loop. The counter will break out at 10 min

    DO
    {
    #$i makes a timer / counter
        $i = 0
        $IsTrue = $true
        while ($IsTrue -eq $true) {        
            $key = [System.Windows.Forms.Keys][KeyLogger.Program]::WaitForKey()
    #at 600 seconds or 10 mins the $i counter will break this loop.
            if ($key -eq "F2" -or $i -eq 600) {
                
                #Do something
                
                $IsTrue = $false
            }
    
    #Sleep waits a second
            Start-Sleep -Seconds 1
    #Counter $i adds 1
            $I++
        }
    
    } Until ($SomeValue -eq $true)
    

    The following works for time and F2

    #$i makes a timer / counter
    $i = 0
    $IsTrue = $true
    while ($IsTrue -eq $true) {  
        If ([Console]::KeyAvailable -or $i -eq 30) {
            [Console]::ReadKey('F2') | Out-Null
            $IsTrue = $false
        }
        Start-Sleep -Seconds 1
        #Counter $i adds 1
        $I++
    }