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