-----------------------------Cryking原创------------------------------
-----------------------转载请注明出处,谢谢!------------------------
昨天没事又玩了下仙剑4(俺是仙剑迷), 由于仙4已经玩了好几次,于是准备写个VBS脚本来实现一些自动打怪和自动行走功能,结果发现除了发送F1到F12有效外,其他按键对游戏完全没有效果。(在打怪界面使用keybd_event还有点效果) 经过了解和查看了仙4的安装要求,确定应是DirectX的原因,(仙4要求DirectX9.0以上) DirectX为了提高游戏的响应速度直接是读端口的输入,也难怪对我的模拟按键不理睬。由于网上已经有使用Winio.dll实现驱动级按键,我这就直接拿来在C#中使用了(标题有点骗人,其实都是C++的功劳),全部代码如下:
WinIo.cs类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Runtime.InteropServices; namespace csWg01 { public enum VKKey { // mouse movements move = 0x0001, leftdown = 0x0002, leftup = 0x0004, rightdown = 0x0008, rightup = 0x0010, middledown = 0x0020, //keyboard stuff VK_LBUTTON = 1, VK_RBUTTON = 2, VK_CANCEL = 3, VK_MBUTTON = 4, VK_BACK = 8, VK_TAB = 9, VK_CLEAR = 12, VK_RETURN = 13, VK_SHIFT = 16, VK_CONTROL = 17, VK_MENU = 18, VK_PAUSE = 19, VK_CAPITAL = 20, VK_ESCAPE = 27, VK_SPACE = 32, VK_PRIOR = 33, VK_NEXT = 34, VK_END = 35, VK_HOME = 36, VK_LEFT = 37, VK_UP = 38, VK_RIGHT = 39, VK_DOWN = 40, VK_SELECT = 41, VK_PRINT = 42, VK_EXECUTE = 43, VK_SNAPSHOT = 44, VK_INSERT = 45, VK_DELETE = 46, VK_HELP = 47, VK_NUM0 = 48, //0 VK_NUM1 = 49, //1 VK_NUM2 = 50, //2 VK_NUM3 = 51, //3 VK_NUM4 = 52, //4 VK_NUM5 = 53, //5 VK_NUM6 = 54, //6 VK_NUM7 = 55, //7 VK_NUM8 = 56, //8 VK_NUM9 = 57, //9 VK_A = 65, //A VK_B = 66, //B VK_C = 67, //C VK_D = 68, //D VK_E = 69, //E VK_F = 70, //F VK_G = 71, //G VK_H = 72, //H VK_I = 73, //I VK_J = 74, //J VK_K = 75, //K VK_L = 76, //L VK_M = 77, //M VK_N = 78, //N VK_O = 79, //O VK_P = 80, //P VK_Q = 81, //Q VK_R = 82, //R VK_S = 83, //S VK_T = 84, //T VK_U = 85, //U VK_V = 86, //V VK_W = 87, //W VK_X = 88, //X VK_Y = 89, //Y VK_Z = 90, //Z VK_NUMPAD0 = 96, //0 VK_NUMPAD1 = 97, //1 VK_NUMPAD2 = 98, //2 VK_NUMPAD3 = 99, //3 VK_NUMPAD4 = 100, //4 VK_NUMPAD5 = 101, //5 VK_NUMPAD6 = 102, //6 VK_NUMPAD7 = 103, //7 VK_NUMPAD8 = 104, //8 VK_NUMPAD9 = 105, //9 VK_NULTIPLY = 106, VK_ADD = 107, VK_SEPARATOR = 108, VK_SUBTRACT = 109, VK_DECIMAL = 110, VK_DIVIDE = 111, VK_F1 = 112, VK_F2 = 113, VK_F3 = 114, VK_F4 = 115, VK_F5 = 116, VK_F6 = 117, VK_F7 = 118, VK_F8 = 119, VK_F9 = 120, VK_F10 = 121, VK_F11 = 122, VK_F12 = 123, VK_NUMLOCK = 144, VK_SCROLL = 145, middleup = 0x0040, xdown = 0x0080, xup = 0x0100, wheel = 0x0800, virtualdesk = 0x4000, absolute = 0x8000 } public class WinIo { public const int KBC_KEY_CMD = 0x64; public const int KBC_KEY_DATA = 0x60; [DllImport("WinIo64.dll")] public static extern bool InitializeWinIo(); [DllImport("WinIo64.dll")] public static extern bool GetPortVal(IntPtr wPortAddr, out int pdwPortVal,byte bSize); [DllImport("WinIo64.dll")] public static extern bool SetPortVal(uint wPortAddr, IntPtr dwPortVal,byte bSize); [DllImport("WinIo64.dll")] public static extern byte MapPhysToLin(byte pbPhysAddr, uint dwPhysSize,IntPtr PhysicalMemoryHandle); [DllImport("WinIo64.dll")] public static extern bool UnmapPhysicalMemory(IntPtr PhysicalMemoryHandle,byte pbLinAddr); [DllImport("WinIo64.dll")] public static extern bool GetPhysLong(IntPtr pbPhysAddr, byte pdwPhysVal); [DllImport("WinIo64.dll")] public static extern bool SetPhysLong(IntPtr pbPhysAddr, byte dwPhysVal); [DllImport("WinIo64.dll")] public static extern void ShutdownWinIo(); [DllImport("user32.dll")] public static extern int MapVirtualKey(uint Ucode, uint uMapType); private static bool IsInitialize { get; set; } public static void Initialize() { if (InitializeWinIo()) { KBCWait4IBE(); IsInitialize = true; } else System.Windows.Forms.MessageBox.Show("failed"); } public static void Shutdown() { if (IsInitialize) ShutdownWinIo(); IsInitialize = false; } ///Wait for Buffer gets empty private static void KBCWait4IBE() { int dwVal = 0; do { bool flag = GetPortVal((IntPtr)0x64, out dwVal, 1); } while ((dwVal & 0x2) > 0); } /// key down public static void MykeyDown(VKKey vKeyCoad) { if (!IsInitialize) return; int btScancode = 0; btScancode = MapVirtualKey((uint)vKeyCoad, 0); KBCWait4IBE(); SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)0x60, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)btScancode, 1); } /// Key up public static void MykeyUp(VKKey vKeyCoad) { if (!IsInitialize) return; int btScancode = 0; btScancode = MapVirtualKey((uint)vKeyCoad, 0); KBCWait4IBE(); SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)0x60, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)(btScancode | 0x80), 1); } /// Simulate mouse down public static void MyMouseDown(int vKeyCoad) { int btScancode = 0; btScancode = MapVirtualKey((byte)vKeyCoad, 0); KBCWait4IBE(); // 'wait for buffer gets empty SetPortVal(KBC_KEY_CMD, (IntPtr)0xD3, 1);// 'send write command KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)(btScancode | 0x80), 1);// 'write in io } /// Simulate mouse up public static void MyMouseUp(int vKeyCoad) { int btScancode = 0; btScancode = MapVirtualKey((byte)vKeyCoad, 0); KBCWait4IBE(); // 'wait for buffer gets empty SetPortVal(KBC_KEY_CMD, (IntPtr)0xD3, 1); //'send write command KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)(btScancode | 0x80), 1);// 'write in io } } }
在button事件中实现模拟按键如下:
private void button1_Click(object sender, EventArgs e) { int hwnd = FindWindow(null, "PAL4-Application"); IntPtr p = new IntPtr(hwnd); if (p == IntPtr.Zero) return; SetActiveWindow(p); //设置为活动窗体,防止被其他窗口挡住 SetForegroundWindow(p); Thread.Sleep(1000); int i = 0; while (i < 5) { WinIo.MykeyDown(VKKey.VK_SPACE); Thread.Sleep(100); WinIo.MykeyUp(VKKey.VK_SPACE); Thread.Sleep(100); WinIo.MykeyDown(VKKey.VK_S); Thread.Sleep(100); WinIo.MykeyUp(VKKey.VK_S); Thread.Sleep(500); WinIo.MykeyDown(VKKey.VK_D); Thread.Sleep(100); WinIo.MykeyUp(VKKey.VK_D); Thread.Sleep(500); WinIo.MykeyDown(VKKey.VK_A); Thread.Sleep(100); WinIo.MykeyUp(VKKey.VK_A); i++; } }
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public static extern int FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr SetActiveWindow(IntPtr hWnd);
64位的WinIo包括源码下载地址: http://www.internals.com/
如果出现WinIo.Initialize()初始化驱动失败的情况,有几点就要注意:
1.确保你当前用户的权限为管理员权限;
2.使用命令“bcdedit /set testsigning on”进入WIN7测试模式,然后重启电脑后再运行上面的代码。
3.64位系统应加载WinIo64.dll,32位系统加载WinIo32.dll.(我的环境是WIN7 64位),PS:不需要引用dll的,PInvoke模式是使用DllImport特性或LoadLibrary来加载非托管代码的,所以无论你怎么引用都会报"未能加载***.dll"的错误的。
接下来就自己写自动行走和打怪的WG吧。(注意:很多网游保护都会检测这种驱动级按键,自己小心被封号吧)