在寻找阅读材料时,应该访问主文章。这是一篇简短的替代文章,包含了仅在这里可用的下载链接。
本文提供了完整的C#和VB.Net软件版本。据所知,这是地球上唯一的管理式极简Raw Input键盘记录器,如果还有其他的,请在评论中告知下载链接。同时,这也是所知的唯一能够正确编译为64位平台的Raw Input库;如果知道其他的,请在评论中告知。
知道这篇文章很短,但主要内容都在主文章中提到了。写这篇文章是为了发布源代码,这些源代码在其他地方是找不到的。
知道许多VB.Net和C#的开发者可能尝试过从原始文章中的非托管版本重建这个项目,但大多数都失败了。大多数C#的Raw Input库都包含多个缺陷,而在C#或VB.NET中创建最小化消息窗口的信息并不直观。
还没有遇到一个能够正确编译64位版本的C#Raw Input库。对于VB.Net,如果知道的话,请在评论中留下下载链接。这些Raw Input类在两种语言中都有,并且能够正确编译64位平台以及32位平台。
如果阅读了原始文章,会发现许多与托管代码相似的地方。
键盘记录是监控和记录键盘上按下和释放的键。当按下一个键时,拥有键盘焦点的应用程序会收到一条消息,当释放键时,会收到另一条消息。当按住一个键时,应用程序可能会收到多个按下消息。应用程序通过一个非常特定的方法 "WndProc" 接收按下和释放的消息(以及其他形式的输入消息,如鼠标输入)。
传统的键盘记录器(不使用Raw Input)会安装一个全局钩子。如果不熟悉全局钩子,可以进行研究。但让承认,如果操作不当,它可能会影响机器的性能。Raw Input是一种完全不同的技术。
Raw Input特别适合于可能有多个键盘或输入设备,并且试图保持日志可读性时使用。知道输入来自哪个设备非常方便。
与传统的键盘记录技术相比,Raw Input技术只需要创建一个最小化的窗口。这是因为需要有一个窗口句柄,该窗口具有 "WndProc"。然后可以使用 "RegisterRawInputDevices" 注册该句柄;一旦这样做了,该窗口将接收所有注册接收的输入消息(传递给它的参数决定了将接收哪些输入消息)。在这个键盘记录器的案例中,它注册接收所有键盘输入。尝试使用多个键盘可能会很有趣。这种Raw Input技术与传统的键盘记录场景相比非常优雅。
实现所有这些的难点在于需要正确的结构和参数来执行各种Raw Input本地调用。保持事物的最小化和正确性很重要;大多数托管Raw Input库无法正确编译为64位版本的原因是因为不当的结构变得臃肿(偏移被抛出)或者四个字节太小而无法存储指针。64位的指针是8个字节;32位的指针是4个字节。这就是为什么在参数和结构以及整数的正确位置使用指针(System.IntPtr)很重要,不要混淆两者。通常可能会在32位版本中混淆两者,因为它们都是四个字节。但是当编译64位版本时,调用变得不可预测。
事实上,可以在这里写上亿段文字;但通过使用代码并围绕源代码自己思考会学到更多。设置断点,亲眼看看发生了什么。更好的是;改变它,修改它,改进它,添加到它,评论它,玩得开心。
下面的代码演示了初学者可能想要自己移植代码时面临的最大挑战之一。即如何Marshal一个 "hInstance",这是 "WNDCLASS" 所需的,同时注意 "New IntPtr(-3)" 是 "HWND_MESSAGE"(告诉 "CreateWindowEx" 创建一个仅消息窗口)。
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);
可下载的演示应用程序是整个项目的完整示例。
使用Raw Input很有趣,但它不提供一种方法来检测哪个应用程序或控件有输入焦点。换句话说,知道一个键被按下或释放,但哪个窗口(应用程序)是原始消息发送到的?对于这个决定,有一些技术可用。使用的技术是使用 "SetWinEventHook",但不要将其与 "SetWinEventHookEx" 混淆。如果能完成这篇文章,会发布一个关于如何在托管应用程序中直接使用 "SetWinEventHook" 监听系统事件的提示(这应该是 "不可能的")。