Reputation: 202
I'm using a modified version of the code given here: https://stackoverflow.com/a/3654821/3179989
If the user presses Ctrl+B the hotkey activates.
If the user presses Ctrl+B, it actives, but continues to hold Ctrl then pressed B again, it does active.
Is there a way I can have the hotkey behave more like copy/paste
does in windows?
For example, holding down Ctrl and tapping V will paste multiple times.
EDIT:
The issue was due to my addition of SendKeys.Send()
when a hotkey was pushed. The original code above does not contain this problem. The question now is how can I send keys without losing this functionality?
Upvotes: 0
Views: 1212
Reputation: 8144
EDIT:
To answer your new question :)
If you write your own global hook, you can specify these types of situations.
I believe the issue you're having is that if you send CTRL+V, it sends a KeyDown
and KeyUp
for Control, which makes the hotkey program assume that you are no longer holding it down.
You need to explicitly handle this scenario by not changing your toggle during key sends.
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (!SENDING_KEYS) //If we're sending keys, ignore everything below
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) //KeyDown
{
int vkCode = Marshal.ReadInt32(lParam);
string theKey = ((Keys)vkCode).ToString();
Console.Write(theKey);
if (theKey.Contains("ControlKey"))
{
//Our Program still thinks CTRL is down even if we send it using SendKeys
CONTROL_DOWN = true;
}
else if (CONTROL_DOWN && theKey == "B")
{
Console.WriteLine("\n***HOTKEY PRESSED***"); //Our hotkey has been pressed
SENDING_KEYS = true; //Now we will be sending keys
SendKeys.Send("^v"); //Send the keys (CTRL+V) - Paste
SENDING_KEYS = false; //Now we are done sending the keys
return (IntPtr)1; //Block our hotkey from being sent anywhere
}
else if (theKey == "Escape")
{
UnhookWindowsHookEx(_hookID);
Environment.Exit(0);
}
}
else if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP) //KeyUP
{
int vkCode = Marshal.ReadInt32(lParam);
string theKey = ((Keys)vkCode).ToString();
if (theKey.Contains("ControlKey"))
{
//During send keys, this will not be triggered
CONTROL_DOWN = false;
}
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
Original Answer:
You can create your own Global Keyhook.
Here's an example using windows forms:
Here is an example in console:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ConsoleKeyhook
{
class Hooky
{
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x0101;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
private static bool CONTROL_DOWN = false;
public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) //KeyDown
{
int vkCode = Marshal.ReadInt32(lParam);
string theKey = ((Keys)vkCode).ToString();
Console.Write(theKey);
if (theKey.Contains("ControlKey"))
{
CONTROL_DOWN = true;
}
else if (CONTROL_DOWN && theKey == "B")
{
Console.WriteLine("\n***HOTKEY PRESSED***");
}
else if (theKey == "Escape")
{
UnhookWindowsHookEx(_hookID);
Environment.Exit(0);
}
}
else if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP) //KeyUP
{
int vkCode = Marshal.ReadInt32(lParam);
string theKey = ((Keys)vkCode).ToString();
if (theKey.Contains("ControlKey"))
{
CONTROL_DOWN = false;
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
}
Upvotes: 2