C#实现驱动级模拟按键

简介: -----------------------------Cryking原创-----------------------------------------------------转载请注明出处,谢谢!------------------------ 昨天没事又玩了下仙剑4(俺是仙剑迷), 由于仙4已经玩了好几次,于是准备写个VBS脚本来实现一些自动打怪和自动行走功能,结果发现除了发送F1到F12有效外,其他按键对游戏完全没有效果。

-----------------------------Cryking原创------------------------------
-----------------------转载请注明出处,谢谢!------------------------ 


昨天没事又玩了下仙剑4(俺是仙剑迷大笑), 由于仙4已经玩了好几次,于是准备写个VBS脚本来实现一些自动打怪和自动行走功能,结果发现除了发送F1到F12有效外,其他按键对游戏完全没有效果。(在打怪界面使用keybd_event还有点效果) 经过了解和查看了仙4的安装要求,确定应是DirectX的原因,(仙4要求DirectX9.0以上) DirectX为了提高游戏的响应速度直接是读端口的输入,也难怪对我的模拟按键不理睬。由于网上已经有使用Winio.dll实现驱动级按键,我这就直接拿来在C#中使用了(标题有点骗人,其实都是C++的功劳),全部代码如下:

WinIo.cs类

[csharp]  view plain  copy
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading.Tasks;  
  6. using System.Runtime.InteropServices;  
  7.   
  8. namespace csWg01  
  9. {  
  10.     public enum VKKey  
  11.     {  
  12.         // mouse movements  
  13.         move = 0x0001,  
  14.         leftdown = 0x0002,  
  15.         leftup = 0x0004,  
  16.         rightdown = 0x0008,  
  17.         rightup = 0x0010,  
  18.         middledown = 0x0020,  
  19.         //keyboard stuff  
  20.         VK_LBUTTON = 1,  
  21.         VK_RBUTTON = 2,  
  22.         VK_CANCEL = 3,  
  23.         VK_MBUTTON = 4,  
  24.         VK_BACK = 8,  
  25.         VK_TAB = 9,  
  26.         VK_CLEAR = 12,  
  27.         VK_RETURN = 13,  
  28.         VK_SHIFT = 16,  
  29.         VK_CONTROL = 17,  
  30.         VK_MENU = 18,  
  31.         VK_PAUSE = 19,  
  32.         VK_CAPITAL = 20,  
  33.         VK_ESCAPE = 27,  
  34.         VK_SPACE = 32,  
  35.         VK_PRIOR = 33,  
  36.         VK_NEXT = 34,  
  37.         VK_END = 35,  
  38.         VK_HOME = 36,  
  39.         VK_LEFT = 37,  
  40.         VK_UP = 38,  
  41.         VK_RIGHT = 39,  
  42.         VK_DOWN = 40,  
  43.         VK_SELECT = 41,  
  44.         VK_PRINT = 42,  
  45.         VK_EXECUTE = 43,  
  46.         VK_SNAPSHOT = 44,  
  47.         VK_INSERT = 45,  
  48.         VK_DELETE = 46,  
  49.         VK_HELP = 47,  
  50.         VK_NUM0 = 48, //0  
  51.         VK_NUM1 = 49, //1  
  52.         VK_NUM2 = 50, //2  
  53.         VK_NUM3 = 51, //3  
  54.         VK_NUM4 = 52, //4  
  55.         VK_NUM5 = 53, //5  
  56.         VK_NUM6 = 54, //6  
  57.         VK_NUM7 = 55, //7  
  58.         VK_NUM8 = 56, //8  
  59.         VK_NUM9 = 57, //9  
  60.         VK_A = 65, //A  
  61.         VK_B = 66, //B  
  62.         VK_C = 67, //C  
  63.         VK_D = 68, //D  
  64.         VK_E = 69, //E  
  65.         VK_F = 70, //F  
  66.         VK_G = 71, //G  
  67.         VK_H = 72, //H  
  68.         VK_I = 73, //I  
  69.         VK_J = 74, //J  
  70.         VK_K = 75, //K  
  71.         VK_L = 76, //L  
  72.         VK_M = 77, //M  
  73.         VK_N = 78, //N  
  74.         VK_O = 79, //O  
  75.         VK_P = 80, //P  
  76.         VK_Q = 81, //Q  
  77.         VK_R = 82, //R  
  78.         VK_S = 83, //S  
  79.         VK_T = 84, //T  
  80.         VK_U = 85, //U  
  81.         VK_V = 86, //V  
  82.         VK_W = 87, //W  
  83.         VK_X = 88, //X  
  84.         VK_Y = 89, //Y  
  85.         VK_Z = 90, //Z  
  86.         VK_NUMPAD0 = 96, //0  
  87.         VK_NUMPAD1 = 97, //1  
  88.         VK_NUMPAD2 = 98, //2  
  89.         VK_NUMPAD3 = 99, //3  
  90.         VK_NUMPAD4 = 100, //4  
  91.         VK_NUMPAD5 = 101, //5  
  92.         VK_NUMPAD6 = 102, //6  
  93.         VK_NUMPAD7 = 103, //7  
  94.         VK_NUMPAD8 = 104, //8  
  95.         VK_NUMPAD9 = 105, //9  
  96.         VK_NULTIPLY = 106,  
  97.         VK_ADD = 107,  
  98.         VK_SEPARATOR = 108,  
  99.         VK_SUBTRACT = 109,  
  100.         VK_DECIMAL = 110,  
  101.         VK_DIVIDE = 111,  
  102.         VK_F1 = 112,  
  103.         VK_F2 = 113,  
  104.         VK_F3 = 114,  
  105.         VK_F4 = 115,  
  106.         VK_F5 = 116,  
  107.         VK_F6 = 117,  
  108.         VK_F7 = 118,  
  109.         VK_F8 = 119,  
  110.         VK_F9 = 120,  
  111.         VK_F10 = 121,  
  112.         VK_F11 = 122,  
  113.         VK_F12 = 123,  
  114.         VK_NUMLOCK = 144,  
  115.         VK_SCROLL = 145,  
  116.         middleup = 0x0040,  
  117.         xdown = 0x0080,  
  118.         xup = 0x0100,  
  119.         wheel = 0x0800,  
  120.         virtualdesk = 0x4000,  
  121.         absolute = 0x8000  
  122.     }  
  123.     public class WinIo  
  124.     {  
  125.         public const int KBC_KEY_CMD = 0x64;  
  126.         public const int KBC_KEY_DATA = 0x60;  
  127.           
  128.         [DllImport("WinIo64.dll")]  
  129.         public static extern bool InitializeWinIo();  
  130.   
  131.         [DllImport("WinIo64.dll")]  
  132.         public static extern bool GetPortVal(IntPtr wPortAddr, out int pdwPortVal,byte bSize);  
  133.   
  134.         [DllImport("WinIo64.dll")]  
  135.         public static extern bool SetPortVal(uint wPortAddr, IntPtr dwPortVal,byte bSize);  
  136.   
  137.         [DllImport("WinIo64.dll")]  
  138.         public static extern byte MapPhysToLin(byte pbPhysAddr, uint dwPhysSize,IntPtr PhysicalMemoryHandle);  
  139.   
  140.         [DllImport("WinIo64.dll")]  
  141.         public static extern bool UnmapPhysicalMemory(IntPtr PhysicalMemoryHandle,byte pbLinAddr);  
  142.   
  143.         [DllImport("WinIo64.dll")]  
  144.         public static extern bool GetPhysLong(IntPtr pbPhysAddr, byte pdwPhysVal);  
  145.   
  146.         [DllImport("WinIo64.dll")]  
  147.         public static extern bool SetPhysLong(IntPtr pbPhysAddr, byte dwPhysVal);  
  148.   
  149.         [DllImport("WinIo64.dll")]  
  150.         public static extern void ShutdownWinIo();  
  151.   
  152.         [DllImport("user32.dll")]  
  153.         public static extern int MapVirtualKey(uint Ucode, uint uMapType);  
  154.   
  155.         private static bool IsInitialize { getset; }  
  156.   
  157.         public static void Initialize()  
  158.         {  
  159.             if (InitializeWinIo())  
  160.             {  
  161.                 KBCWait4IBE(); IsInitialize = true;  
  162.             }  
  163.             else  
  164.                 System.Windows.Forms.MessageBox.Show("failed");  
  165.   
  166.         }  
  167.         public static void Shutdown()  
  168.         {  
  169.             if (IsInitialize)  
  170.                 ShutdownWinIo();  
  171.             IsInitialize = false;  
  172.         }  
  173.         ///Wait for Buffer gets empty  
  174.         private static void KBCWait4IBE()  
  175.         {  
  176.             int dwVal = 0;  
  177.             do  
  178.             {  
  179.                 bool flag = GetPortVal((IntPtr)0x64, out dwVal, 1);  
  180.             }  
  181.             while ((dwVal & 0x2) > 0);  
  182.         }  
  183.         /// key down  
  184.         public static void MykeyDown(VKKey vKeyCoad)  
  185.         {  
  186.             if (!IsInitialize) return;  
  187.   
  188.             int btScancode = 0;  
  189.             btScancode = MapVirtualKey((uint)vKeyCoad, 0);  
  190.             KBCWait4IBE();  
  191.             SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);  
  192.             KBCWait4IBE();  
  193.             SetPortVal(KBC_KEY_DATA, (IntPtr)0x60, 1);  
  194.             KBCWait4IBE();  
  195.             SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);  
  196.             KBCWait4IBE();  
  197.             SetPortVal(KBC_KEY_DATA, (IntPtr)btScancode, 1);  
  198.         }  
  199.         /// Key up  
  200.         public static void MykeyUp(VKKey vKeyCoad)  
  201.         {  
  202.             if (!IsInitialize) return;  
  203.   
  204.             int btScancode = 0;  
  205.             btScancode = MapVirtualKey((uint)vKeyCoad, 0);  
  206.             KBCWait4IBE();  
  207.             SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);  
  208.             KBCWait4IBE();  
  209.             SetPortVal(KBC_KEY_DATA, (IntPtr)0x60, 1);  
  210.             KBCWait4IBE();  
  211.             SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);  
  212.             KBCWait4IBE();  
  213.             SetPortVal(KBC_KEY_DATA, (IntPtr)(btScancode | 0x80), 1);  
  214.         }  
  215.   
  216.         /// Simulate mouse down  
  217.         public static void MyMouseDown(int vKeyCoad)  
  218.         {  
  219.             int btScancode = 0;  
  220.             btScancode = MapVirtualKey((byte)vKeyCoad, 0);  
  221.             KBCWait4IBE(); // 'wait for buffer gets empty  
  222.             SetPortVal(KBC_KEY_CMD, (IntPtr)0xD3, 1);// 'send write command  
  223.             KBCWait4IBE();  
  224.             SetPortVal(KBC_KEY_DATA, (IntPtr)(btScancode | 0x80), 1);// 'write in io  
  225.         }  
  226.         /// Simulate mouse up  
  227.         public static void MyMouseUp(int vKeyCoad)  
  228.         {  
  229.             int btScancode = 0;  
  230.             btScancode = MapVirtualKey((byte)vKeyCoad, 0);  
  231.             KBCWait4IBE(); // 'wait for buffer gets empty  
  232.             SetPortVal(KBC_KEY_CMD, (IntPtr)0xD3, 1); //'send write command  
  233.             KBCWait4IBE();  
  234.             SetPortVal(KBC_KEY_DATA, (IntPtr)(btScancode | 0x80), 1);// 'write in io  
  235.         }  
  236.     }  
  237. }  

然后新建一个窗体应用,在Form1_Load事件中添加WinIo.Initialize();来初始化驱动,在Form1_FormClosed事件中使用WinIo.Shutdown();卸载驱动。

在button事件中实现模拟按键如下:

[csharp]  view plain  copy
  1. private void button1_Click(object sender, EventArgs e)  
  2.    {  
  3.        int hwnd = FindWindow(null"PAL4-Application");  
  4.        IntPtr p = new IntPtr(hwnd);  
  5.        if (p == IntPtr.Zero)  
  6.            return;  
  7.        SetActiveWindow(p);  
  8.        //设置为活动窗体,防止被其他窗口挡住  
  9.        SetForegroundWindow(p);  
  10.        Thread.Sleep(1000);  
  11.        int i = 0;  
  12.        while (i < 5)  
  13.        {  
  14.            WinIo.MykeyDown(VKKey.VK_SPACE);  
  15.            Thread.Sleep(100);  
  16.            WinIo.MykeyUp(VKKey.VK_SPACE);  
  17.            Thread.Sleep(100);  
  18.            WinIo.MykeyDown(VKKey.VK_S);  
  19.            Thread.Sleep(100);  
  20.            WinIo.MykeyUp(VKKey.VK_S);  
  21.            Thread.Sleep(500);  
  22.            WinIo.MykeyDown(VKKey.VK_D);  
  23.            Thread.Sleep(100);  
  24.            WinIo.MykeyUp(VKKey.VK_D);  
  25.            Thread.Sleep(500);  
  26.            WinIo.MykeyDown(VKKey.VK_A);  
  27.            Thread.Sleep(100);  
  28.            WinIo.MykeyUp(VKKey.VK_A);  
  29.            i++;  
  30.        }  
  31.    }  


注意上面的代码是运行在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.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吧。(注意:很多网游保护都会检测这种驱动级按键,自己小心被封号吧大笑)


目录
相关文章
|
3月前
|
存储 缓存 算法
使用Mixtral-offloading在消费级硬件上运行Mixtral-8x7B
Mixtral-8x7B是最好的开放大型语言模型(LLM)之一,但它是一个具有46.7B参数的庞大模型。即使量化为4位,该模型也无法在消费级GPU上完全加载(例如,24 GB VRAM是不够的)。
100 4
|
7月前
|
芯片
到底什么是I/O的驱动能力?
到底什么是I/O的驱动能力?
|
8月前
|
监控 数据可视化 数据挖掘
这样做,你才能驱动业务
这样做,你才能驱动业务
|
9月前
|
算法 Java 程序员
思考:业务驱动技术 or 技术驱动业务
思考:业务驱动技术 or 技术驱动业务
142 0
|
IDE 前端开发 数据可视化
ZenUML与服务驱动设计
ZenUML与服务驱动设计
ZenUML与服务驱动设计
|
测试技术 程序员
我的场景驱动设计
我的场景驱动设计
我的场景驱动设计
新型SL密集型光纤连接器的设计与探讨
高密度、小型化是光纤连接器的发展趋势与方向,本文针对目前光纤通信设备主流光纤连接器的接口,设计开发了一种新型SL高密度光纤连接器,它与常用的SC和LC光纤连接器相比,连接器布线密度是SC连接器的四倍和LC连接器的两倍。光纤适配器与光纤连接器是密集波分复用(DWDM)、光分路器等光通信设备接口与连接器件,用SL光纤连接器替代目前常用的LC或SC光纤连接器,可成倍的提高光通信设备接口与光纤连接器布线的密度,更好的满足光通信设备向高密度、大容量、集成化方向发展的需要
 新型SL密集型光纤连接器的设计与探讨
|
关系型数据库 测试技术 5G
新型SL密集型光纤连接器的设计与应用
高密度、小型化是光纤连接器的发展趋势与方向,本文针对目前光纤通信设备主流光纤连接器的接口,设计开发了一种新型SL高密度光纤连接器,它与常用的SC和LC光纤连接器相比,连接器布线密度是SC连接器的四倍和LC连接器的两倍。光纤适配器与光纤连接器是密集波分复用(DWDM)、光分路器等光通信设备接口与连接器件,用SL光纤连接器替代目前常用的LC或SC光纤连接器,可成倍的提高光通信设备接口与光纤连接器布线的密度,更好的满足光通信设备向高密度、大容量、集成化方向发展的需要。
新型SL密集型光纤连接器的设计与应用
|
存储 人工智能 物联网
新数据时代:业务驱动的存储“双模式”
截然相反的两个数字,将数据与这个时代之间的“割裂”体现得淋漓尽致。
|
机器人 容器 安全
带你读《工业机器人系统及应用》之三:驱动系统
本书聚焦于工业机器人,涵盖其组成结构、电气控制及实践应用,重点从使用的角度展开介绍,不涉及数学原理分析。书中综合了机械、控制、计算机、传感器、驱动等专业的知识,包含大量新近的工业机器人产品实例,并配有丰富的图表和数据手册,为机器人的设计、生产、布置、操作和维护提供全流程的详细指南。