admin 管理员组

文章数量: 887021

-----------------------------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
        }
    }
}

然后新建一个窗体应用,在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++;
            }
        }


注意上面的代码是运行在unsafe模式下的,其中FindWindow、SetForegroundWindow、 SetActiveWindow导入如下:

        [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/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吧。(注意:很多网游保护都会检测这种驱动级按键,自己小心被封号吧)



本文标签: 按键