原理:逆向了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); } } }