Introduction
If you are looking for something to read you should visit the primary article. This is short alternative article which includes downloads not available anywhere but here.
A full C# and a full VB.Net version of the software is provided.
This is the only managed minimalist Raw Input key logger I am aware of on the planet,
if there are others out there please comment where they are for download. Also this is the only Raw Input library that compiles property for 64-bit platforms that
I know of; again if you know of another please comment.
I know this article is short, but it's all been said in the main article. I'm writing this article
to release source code you simply will not find anywhere else but here.
I know many VB.Net and C# coders may have tried unsuccessfully to recreate this project
from the unmanaged versions in the original article. Most raw input libraries for C# contain multiple flaws and information on creating minimal message only windows
in C# or VB.NET is not intuitive.
Background
I have not come across a C# Raw Input library that compiles properly for 64-bit versions. I have not come across any for VB.Net; please comment and leave
the download link if you know of any. These Raw Input classes are in both languages and do compile properly for the 64-bit platform as well as the 32-bit platform.
If you read the original article you will be able to draw many parallels to the managed code.
Key logging is monitoring and logging keys being pressed and released
on your keyboard(s). The application that has the focus of the keyboard receives a message each time you press a key down. And a separate message when
you release the key (key up). When you hold a key down the application may receive multiple key down messages. An application receives key down and key up messages
(along with other forms of input messages like mouse input) through a very specifically named method called "WndProc
" every window that
is capable of receiving these messages has a "WndProc
" method.
A traditional key logger (one that does not use Raw Input) installs a global hook. You can research global hooks if you are unfamiliar with them.
But let us just admit it can affect your machines performance, especially if done improperly. Raw Input is a different technique altogether.
Raw Input is especially nice when you may have multiple keyboards or input devices and you are trying to keep your log decipherable.
Knowing which device the input is coming from is very handy.
Instead of installing a global hook, the raw input technique
only requires you to create a minimal window. It requires you to do so because you need to have a window handle to a window that has a "WndProc
".
You can then "register" that handle with RegisterRawInputDevices
and once you do so; that window will receive all input messages
that you registered to receive (parameters you pass to it determine which input messages you will receive). In the case of this key logger it registers to receive
all keyboard input. It would be fun to try with multiple keyboards. This Raw Input technique is very elegant compared to the traditional key logging scenario.
The hard part in doing all this is to have the proper structures and parameters needed to make the various Raw Input Native calls. It can be important to keep things
minimal and proper; the reason most managed Raw Input libraries do not compile properly for 64-bit versions is because of improper structures get bloated (offsets thrown off)
by an additional four bytes or are four bytes to small to store a pointer. The pointers for 64-bit are 8 bytes; the pointers for 32-bit are 4 bytes.
Which is why it is important to use pointers (System.IntPtr
) in the correct spot for parameters and structures and integers in their correct spots and not intermingle
the two. Often you may intermingle the two on 32-bit versions because they all are four bytes. But when you compile your 64-bit version the calls become unpredictable.
The fact is I can write a billion paragraphs here; but you will learn more by using the code and wrapping your own mind around the source code. Set break points, see for yourself what is going on. Even better; change it, modify it, improve it, add to it, comment about it and have fun with it.
Using the code
The code below demonstrates one of the most difficult challenges for beginners who may have wanted to port the code themselves. Which is how we can Marshal an hInstance
required for WNDCLASS
and also note that New IntPtr<code>(-3)
is HWND_MESSAGE
(which tells CreateWindowEx
to create a message only window).
Dim msg As New MSG
Dim wc As New WNDCLASS
Dim hInstance As System.IntPtr = New System.IntPtr(System.Runtime.InteropServices.Marshal.GetHINSTANCE(_
System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32())
wc.lpfnWndProc = AddressOf WndProcCallback
wc.hInstance = hInstance
wc.lpszClassName = "SimpleMessageWindow"
RegisterClass(wc)
m_handle = CreateWindowEx(0, wc.lpszClassName, Nothing, 0, 0, 0, 0, 0, New IntPtr(-3), IntPtr.Zero, hInstance, 0)
MSG msg = new MSG();
WNDCLASS wc = new WNDCLASS();
System.IntPtr hInstance = new System.IntPtr(System.Runtime.InteropServices.Marshal.GetHINSTANCE(
System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]).ToInt32());
wc.lpfnWndProc = WndProcCallback;
wc.hInstance = hInstance;
wc.lpszClassName = "SimpleMessageWindow";
RegisterClass(wc);
m_handle = CreateWindowEx(0, wc.lpszClassName, null, 0, 0, 0, 0, 0,
new System.IntPtr(-3), System.IntPtr.Zero, hInstance, 0);
The downloadable demo applications are complete examples of the entire project.
Points of Interest
Using Raw Input is fun, however it does not provide a way to detect which application or control has focus for the input. In other words, we know a key was pressed
or released but which window (application) was the original message sent to? For that determination there are a few techniques available. The technique I use myself is
to use SetWinEventHook
but don't confuse that with SetWinEventHookEx
. If I can ever get this article done I will post an tip about how
to use SetWinEventHook
to listen to system events (directly in your managed applications; which is suppose to be "impossible").
Anyway there is a system event (EVENT_OBJECT_FOCUS = 32773
) which you can hook into and be notified when the keyboard focus changes.
This will eliminate the need to constantly poll GetForegroundWindow
every time you want to know which application was the original
destination for the keyboard input message. Hopefully I will be able get that tip published and link to it here when it is.
History
I hope this stimulates more VB.Net and C# coders to utilize Raw Input. I look forward to hearing comments from VB.Net and C# coders;
all others can comment on the original article; this is only for VB.Net and C# coders.