原理:逆向了ETDDeviceInformation.exe中取内核原始PS2数据并计算出多点触摸坐标的部分,然后使用Microsoft.Ink墨迹库来做文字识别。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
namespace InkTest
{
class ETD
{
[DllImport("kernel32")]
static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, byte[] lpInBuffer, uint nInBufferSize, byte[] lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);
[DllImport("kernel32")]
static extern IntPtr CreateFileA(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32")]
static extern IntPtr OpenEventA(uint dwDesiredAccess, bool bInheritHandle, string lpName);
[DllImport("kernel32")]
static extern bool ResetEvent(IntPtr hEvent);
[DllImport("kernel32")]
static extern uint WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);
static public void Init()
{
var t = new Thread(ETDThread);
t.Start();
}
static public int X;
static public int Y;
static void ETDThread()
{
IntPtr hDevice = CreateFileA(@"\\.\ETD", 0xC0000000, 0, IntPtr.Zero, 3, 0, IntPtr.Zero);
IntPtr hEvent = OpenEventA(0x1f0003, false, @"Global\ETDOther_GetKernelData");
byte[] ioctlBuf = new byte[124814];
uint nRet = (uint)ioctlBuf.Length;
Array.Copy(BitConverter.GetBytes((short)0x608), ioctlBuf, 2);
DeviceIoControl(hDevice, 0x9c412000, ioctlBuf, nRet, ioctlBuf, nRet, out nRet, IntPtr.Zero);
int bufIndex = BitConverter.ToInt32(ioctlBuf, 0x406);
byte[] data = new byte[6];
while (true)
{
WaitForSingleObject(hEvent, -1);
ResetEvent(hEvent);
Array.Clear(ioctlBuf, 0, ioctlBuf.Length);
nRet = (uint)ioctlBuf.Length;
Array.Copy(BitConverter.GetBytes((short)0x708), ioctlBuf, 2);
Array.Copy(BitConverter.GetBytes(bufIndex), 0, ioctlBuf, 2, 4);
DeviceIoControl(hDevice, 0x9c412000, ioctlBuf, nRet, ioctlBuf, nRet, out nRet, IntPtr.Zero);
int count = BitConverter.ToInt32(ioctlBuf, 10);
bufIndex = BitConverter.ToInt32(ioctlBuf, 0x480e);
for (int i = 0; i < count; ++i)
{
byte d1 = ioctlBuf[14 + i * 18 + 4];
int d4 = BitConverter.ToInt32(ioctlBuf, 14 + i * 18 + 13);
byte d5 = ioctlBuf[14 + i * 18 + 17];
data[d4] = d5;
if (d4 == 5)
{
if (d1 == 16 && data[1] == 0) //release
{
X = Y = 0;
}
else if (d1 == 17)
{
X = (data[1] & 0xf) * 0x100 + data[2];
Y = (data[4] & 0xf) * 0x100 + data[5];
}
}
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Ink;
using System.IO;
using System.Runtime.InteropServices;
namespace InkTest
{
public partial class Form1 : Form
{
List<Point> mPoints = new List<Point>();
Ink mInk = new Ink();
int mLastX, mLastY;
DateTime mLastTime = DateTime.MaxValue;
[DllImport("user32")]
static extern int GetKeyState(int nVirtKey);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ETD.Init();
}
private void timer1_Tick(object sender, EventArgs e)
{
//this.Hide();
if (GetKeyState(0xA0) > 0)
return;
if (ETD.X == 0 && ETD.Y == 0)
{
if (mLastX != 0 && mLastY != 0)
{
mInk.Strokes.Add(mInk.CreateStroke(mPoints.ToArray()));
mPoints.Clear();
mLastTime = DateTime.Now;
}
else if (DateTime.Now - mLastTime > TimeSpan.FromSeconds(0.5))
{
string str = mInk.Strokes.ToString();
mInk.DeleteStrokes();
mLastTime = DateTime.MaxValue;
Clipboard.SetText(str);
SendKeys.Send("^V");
}
}
else
{
mPoints.Add(new Point(ETD.X, -ETD.Y));
}
mLastX = ETD.X;
mLastY = ETD.Y;
this.Text = string.Format("{0},{1}", ETD.X, ETD.Y);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Environment.Exit(0);
}
}
}