-
Notifications
You must be signed in to change notification settings - Fork 528
Expand file tree
/
Copy pathInterceptKeys.cs
More file actions
104 lines (96 loc) · 4.11 KB
/
InterceptKeys.cs
File metadata and controls
104 lines (96 loc) · 4.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
using System;
using System.Diagnostics;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;
namespace Carnac.Logic.KeyMonitor
{
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
public class InterceptKeys : IInterceptKeys
{
public static readonly InterceptKeys Current = new InterceptKeys();
readonly IObservable<InterceptKeyEventArgs> keyStream;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
Win32Methods.LowLevelKeyboardProc callback;
InterceptKeys()
{
keyStream = Observable.Create<InterceptKeyEventArgs>(observer =>
{
Debug.Write("Subscribed to keys");
IntPtr hookId = IntPtr.Zero;
// Need to hold onto this callback, otherwise it will get GC'd as it is an unmanged callback
callback = (nCode, wParam, lParam) =>
{
if (nCode >= 0)
{
var eventArgs = CreateEventArgs(wParam, lParam);
observer.OnNext(eventArgs);
if (eventArgs.Handled)
return (IntPtr)1;
}
// ReSharper disable once AccessToModifiedClosure
return Win32Methods.CallNextHookEx(hookId, nCode, wParam, lParam);
};
hookId = SetHook(callback);
return Disposable.Create(() =>
{
Debug.Write("Unsubscribed from keys");
Win32Methods.UnhookWindowsHookEx(hookId);
callback = null;
});
})
.Publish().RefCount();
}
public IObservable<InterceptKeyEventArgs> GetKeyStream()
{
return keyStream;
}
static InterceptKeyEventArgs CreateEventArgs(IntPtr wParam, IntPtr lParam)
{
bool alt = (Control.ModifierKeys & Keys.Alt) != 0;
bool control = (Control.ModifierKeys & Keys.Control) != 0;
bool shift = (Control.ModifierKeys & Keys.Shift) != 0;
bool keyDown = wParam == (IntPtr)Win32Methods.WM_KEYDOWN;
bool keyUp = wParam == (IntPtr)Win32Methods.WM_KEYUP;
int vkCode = Marshal.ReadInt32(lParam);
var key = (Keys)vkCode;
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms646286(v=vs.85).aspx
if (key != Keys.RMenu && key != Keys.LMenu && wParam == (IntPtr)Win32Methods.WM_SYSKEYDOWN)
{
alt = true;
keyDown = true;
}
if (key != Keys.RMenu && key != Keys.LMenu && wParam == (IntPtr)Win32Methods.WM_SYSKEYUP)
{
alt = true;
keyUp = true;
}
if (wParam == (IntPtr)Win32Methods.WM_SYSKEYDOWN && key == Keys.LMenu)
{
keyDown = true;
}
return new InterceptKeyEventArgs(
key,
keyDown ?
KeyDirection.Down : keyUp
? KeyDirection.Up : KeyDirection.Unknown,
alt, control, shift);
}
static IntPtr SetHook(Win32Methods.LowLevelKeyboardProc proc)
{
// NOTE: This requires FullTrust to use the Process class.
// There don't seem to be alternatives to achieving this in
// MediumTrust environment which is fine because that's a
// concept that has long been obsoleted. But just a warning
// if you ever try and run Carnac in that sort of way.
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return Win32Methods.SetWindowsHookEx(Win32Methods.WH_KEYBOARD_LL, proc, Win32Methods.GetModuleHandle(curModule.ModuleName), 0);
}
}
}
}