本文介绍AutoHotInterception的一些改进,在AutoHotInterception-3.4中生成了AutoHotInterception.dll,但用起来发现鼠标响应有问题,它是用C#编写的,看了源代码之后,愿愿推测,一方面是鼠标的处理过程太长,sleep时间设置太长。所以,想把它改进下。
首先设置,循环查询设备的数量定为8个,而不是20个,想法很简单,循环次数越小,那么鼠标的响应就越可靠。还有,把sleep时间设置为4ms,而不是原来的10ms,设置过短,占用cpu资源太多,设置过长鼠标响应会变得不可靠。所以,设置为4。
修改它的代码,本人安装了vs2010,由于AutoHotInterception是用C#6,编写的所以对语法稍作修改。具体:修改代码如下:
ManagedWrapper.cs文件不变,与原版相同。
DeviceIds目标是设置键盘在1到4,鼠标设备在11到14。
注意紫色代码:
public void SetContextCallback(int id, dynamic callback)
{
SetFilterState(false);
if (id < 1 || id > 14||(id>4&&id<11))
{
throw new ArgumentOutOfRangeException("id ", "DeviceIds must be between 1 and 4 or 11 and 14");
}
_contextCallbacks[id] = callback;
_filteredDevices[id] = true;
SetFilterState(true);
SetThreadState(true);
}
名字空间由于C#版本不对,所以做如下改动:
using static AutoHotInterception.Helpers.HelperFunctions;
变为,在调用方法前加入HelperFunctions,把上句变为using AutoHotInterception.Helpers;
HelperFunctions.IsValidDeviceId(false, id);
等等吧。
继续改动:
public int GetDeviceId(bool isMouse, int vid, int pid, int instance = 1)
{
var start = isMouse ? 11 : 1;
var max = isMouse ? 15 : 5;
for (var i = start; i < max; i++)
{
var handle = ManagedWrapper.GetHardwareStr(_deviceContext, i, 1000);
int foundVid = 0, foundPid = 0;
HelperFunctions.GetVidPid(handle, ref foundVid, ref foundPid);
if (foundVid != vid || foundPid != pid) continue;
if (instance == 1)
{
return i;
}
instance--;
}
//ToDo: Should throw here?
return 0;
}
继续改动:
private void PollThread()
{
ManagedWrapper.Stroke stroke = new ManagedWrapper.Stroke();
while (true)
{
// Iterate through all Keyboards
for (var i = 1; i < 5; i++)
{
var isMonitoredKeyboard = IsMonitoredDevice(i) == 1;
var hasSubscription = false;
var hasContext = _contextCallbacks.ContainsKey(i);
// Process any waiting input for this keyboard
while (ManagedWrapper.Receive(_deviceContext, i, ref stroke, 1) > 0)
{
var block = false;
// If this is not a monitored keyboard, skip.
// This check should not really be needed as the IsMonitoredDevice() predicate should only match monitored keyboards...
// ... but in case it does, we want to ignore this bit and pass the input through
if (isMonitoredKeyboard && _keyboardMappings.ContainsKey(i))
{
// Process Subscription Mode
var code = stroke.key.code;
var state = stroke.key.state;
#region KeyCode, State, Extended Flag translation
// Begin translation of incoming key code, state, extended flag etc...
var processMappings = true;
if (code == 54)
{
// Interception seems to report Right Shift as 54 / 0x36...
// ... this code is normally unused (Alt-SysRq according to linked page) ...
// ... and AHK uses 54 + 256 = 310 (0x36 + 0x100 = 0x136)...
// ... so just keep the key code and behave as if the extended flag was set
state += 2;
}
// If state is shifted up by 2 (1 or 2 instead of 0 or 1), then this is an "Extended" key code
if (state > 1)
{
if (code == 42)
{
// Shift (42/0x2a) with extended flag = the key after this one is extended.
// Example case is Delete (The one above the arrow keys, not on numpad)...
// ... this generates a stroke of 0x2a (Shift) with *extended flag set* (Normal shift does not do this)...
// ... followed by 0x53 with extended flag set.
// We do not want to fire subsriptions for the extended shift, but *do* want to let the key flow through...
// ... so that is handled here.
// When the extended key (Delete in the above example) subsequently comes through...
// ... it will have code 0x53, which we shift to 0x153 (Adding 256 Dec) to signify extended version...
// ... as this is how AHK behaves with GetKeySC()
// Set flag to stop Context Mode from firing
hasSubscription = true;
// Set flag to indicate disable mapping processing
processMappings = false;
}
else
{
// Extended flag set
// Shift code up by 256 (0x100) to signify extended code
code += 256;
state -= 2;
}
}
#endregion
// Code and state now normalized, proceed with checking for subscriptions...
if (processMappings && _keyboardMappings[i].ContainsKey(code))
{
hasSubscription = true;
var mapping = _keyboardMappings[i][code];
if (mapping.Block)
{
block = true;
}
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(1 - state));
}
}
// If the key was blocked by Subscription Mode, then move on to next key...
if (block) continue;
// If this key had no subscriptions, but Context Mode is set for this keyboard...
// ... then set the Context before sending the key
if (!hasSubscription && hasContext)
{
_contextCallbacks[i](1);
}
// Pass the key through to the OS.
ManagedWrapper.Send(_deviceContext, i, ref stroke, 1);
// If we are processing Context Mode, then Unset the context variable after sending the key
if (!hasSubscription && hasContext)
{
_contextCallbacks[i](0);
}
}
}
// Process Mice
for (var i = 11; i < 15; i++)
{
var isMontioredMouse = IsMonitoredDevice(i) == 1;
var hasSubscription = false;
var hasContext = _contextCallbacks.ContainsKey(i);
while (ManagedWrapper.Receive(_deviceContext, i, ref stroke, 1) > 0)
{
//Debug.WriteLine($"AHK| Mouse {i} seen - flags: {stroke.mouse.flags}, raw state: {stroke.mouse.state}");
var block = false;
if (isMontioredMouse)
{
if (stroke.mouse.state != 0 && _mouseButtonMappings.ContainsKey(i))
{
// Mouse Button
//Debug.WriteLine($"AHK| Mouse {i} seen - flags: {stroke.mouse.flags}, raw state: {stroke.mouse.state}");
var btnState = HelperFunctions.StrokeStateToButtonState(stroke);
if (_mouseButtonMappings[i].ContainsKey(btnState.Button))
{
hasSubscription = true;
var mapping = _mouseButtonMappings[i][btnState.Button];
if (mapping.Block)
{
block = true;
}
var state = btnState;
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state.State));
}
//Console.WriteLine($"AHK| Mouse {i} seen - button {btnState.Button}, state: {stroke.mouse.state}, rolling: {stroke.mouse.rolling}");
}
else if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveAbsolute) ==
(ushort) ManagedWrapper.MouseFlag.MouseMoveAbsolute
&& _mouseMoveAbsoluteMappings.ContainsKey(i))
{
// Absolute Mouse Move
hasSubscription = true;
var mapping = _mouseMoveAbsoluteMappings[i];
if (mapping.Block)
{
block = true;
}
var x = stroke.mouse.x;
var y = stroke.mouse.y;
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y));
}
else if ((stroke.mouse.flags & (ushort)ManagedWrapper.MouseFlag.MouseMoveRelative) ==
(ushort)ManagedWrapper.MouseFlag.MouseMoveRelative
&& _mouseMoveRelativeMappings.ContainsKey(i))
{
// Relative Mouse Move
hasSubscription = true;
var mapping = _mouseMoveRelativeMappings[i];
if (mapping.Block)
{
block = true;
}
var x = stroke.mouse.x;
var y = stroke.mouse.y;
ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y));
}
}
// If this key had no subscriptions, but Context Mode is set for this mouse...
// ... then set the Context before sending the button
if (!hasSubscription && hasContext)
{
// Set Context
_contextCallbacks[i](1);
}
if (!(block))
{
ManagedWrapper.Send(_deviceContext, i, ref stroke, 1);
}
// If we are processing Context Mode, then Unset the context variable after sending the button
if (!hasSubscription && hasContext)
{
// Unset Context
_contextCallbacks[i](0);
}
}
}
Thread.Sleep(4);
}
}
继续改动:
public static void IsValidDeviceId(bool isMouse, int id)
{
var start = isMouse ? 11 : 1;
var end = start + 3;
if (id < start || id > end)
{
var s =string.Format("Invalid id ID: {0} for device type {1}. Device IDs for this type should be between {2} and {3}",id,(isMouse ? "Mouse" : "Keyboard"),start,end);
throw new ArgumentOutOfRangeException("id", s);
}
}
public static DeviceInfo[] GetDeviceList(IntPtr deviceContext)
{
var ret = new List<DeviceInfo>();
var j = 1;
for (var i = 1; i < 9 ; i++)
{
if (i > 4) { j = i + 6; }
else {j=i;}
var handle = ManagedWrapper.GetHardwareStr(deviceContext, j, 1000);
if (handle == "") continue;
int foundVid = 0, foundPid = 0;
GetVidPid(handle, ref foundVid, ref foundPid);
if (foundVid == 0 || foundPid == 0) continue;
ret.Add(new DeviceInfo { Id = j, Vid = foundVid, Pid = foundPid, IsMouse = j > 4 });
}
return ret.ToArray();
}
大致如此,剩下的细枝末节就不说了,幸运的话代码可以生成了,将新的AutoHotInterception.dll,.lib拷入自己的程序库文件夹中,
由于本人完成这个修改已经是前几天了,可能还有报错,请自行解决。