昨天没事又玩了下仙剑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
- }
- }
- }
然后新建一个窗体应用,在Form1_Load事件中添加WinIo.Initialize();来初始化驱动,在Form1_FormClosed事件中使用WinIo.Shutdown();卸载驱动。
在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://url.cn/55xfxz0 (地址失效请联系博主))
如果出现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吧。(注意:很多网游保护都会检测这种驱动级按键,自己小心被封号吧)