首页 > 代码库 > 使用C#+socket实现用移动设备控制的虚拟手柄
使用C#+socket实现用移动设备控制的虚拟手柄
最近在和同学玩死神vs火影。以怀念小时候,突然认为用键盘玩的不够畅快,因此萌生了写一个虚拟手柄的念头。
我的思路是在移动设备(iOS、Android)上实现手柄,在电脑上监听,利用socket建立持久连接,通过移动设备向电脑上的监听软件发送操作码,通过操作码来处理事件。
有关socket的服务端,建立在一个server上,让移动设备和电脑分别连接,建立信道,在server上使用python建立socketclient与在移动设备上使用socket十分便利。这里不讲述。本文的重点是实现电脑上依据键值实现的按键事件,包含组合键的处理。
我们如果虚拟手柄有4+6个键。各自是上下左右。1-6功能键,发送的操作码码分别为0~9。当全部按键松开。发送的操作码为-1。
为了实现按键操作,须要借助USER32.DLL的keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo)函数,第一个參数是键码,第二个和第四个填0就可以,第三个代表是按下按键还是松开,0表示按下,2表示松开。
因为C#无法直接调用那些宏,因此键码输入数字来实现。键码相应表例如以下:
虚拟键码 相应值 相应键 VK_LBUTTON 1 鼠标左键 VK_RBUTTON 2 鼠标右键 VK_CANCEL 3 Cancel VK_MBUTTON 4 鼠标中键 VK_XBUTTON1 5 VK_XBUTTON2 6 VK_BACK 8 Backspace VK_TAB 9 Tab VK_CLEAR 12 Clear VK_RETURN 13 Enter VK_SHIFT 16 Shift VK_CONTROL 17 Ctrl VK_MENU 18 Alt VK_PAUSE 19 Pause VK_CAPITAL 20 Caps Lock VK_KANA 21 VK_HANGUL 21 VK_JUNJA 23 VK_FINAL 24 VK_HANJA 25 VK_KANJI 25* VK_ESCAPE 27 Esc VK_CONVERT 28 VK_NONCONVERT 29 VK_ACCEPT 30 VK_MODECHANGE 31 VK_SPACE 32 Space VK_PRIOR 33 Page Up VK_NEXT 34 Page Down VK_END 35 End VK_HOME 36 Home VK_LEFT 37 Left Arrow VK_UP 38 Up Arrow VK_RIGHT 39 Right Arrow VK_DOWN 40 Down Arrow VK_SELECT 41 Select VK_PRINT 42 Print VK_EXECUTE 43 Execute VK_SNAPSHOT 44 Snapshot VK_INSERT 45 Insert VK_DELETE 46 Delete VK_HELP 47 Help 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 56 8 57 9 65 A 66 B 67 C 68 D 69 E 70 F 71 G 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W 88 X 89 Y 90 Z VK_LWIN 91 VK_RWIN 92 VK_APPS 93 VK_SLEEP 95 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_MULTIPLY 106 小键盘 * VK_ADD 107 小键盘 + VK_SEPARATOR 108 小键盘 Enter VK_SUBTRACT 109 小键盘 - VK_DECIMAL 110 小键盘 . VK_DIVIDE 111 小键盘 / VK_F1 112 F1 VK_F2 113 F2 VK_F3 114 F3 VK_F4 115 F4 VK_F5 116 F5 VK_F6 117 F6 VK_F7 118 F7 VK_F8 119 F8 VK_F9 120 F9 VK_F10 121 F10 VK_F11 122 F11 VK_F12 123 F12 VK_F13 124 VK_F14 125 VK_F15 126 VK_F16 127 VK_F17 128 VK_F18 129 VK_F19 130 VK_F20 131 VK_F21 132 VK_F22 133 VK_F23 134 VK_F24 135 VK_NUMLOCK 144 Num Lock VK_SCROLL 145 Scroll VK_LSHIFT 160 VK_RSHIFT 161 VK_LCONTROL 162 VK_RCONTROL 163 VK_LMENU 164 VK_RMENU 165 VK_BROWSER_BACK 166 VK_BROWSER_FORWARD 167 VK_BROWSER_REFRESH 168 VK_BROWSER_STOP 169 VK_BROWSER_SEARCH 170 VK_BROWSER_FAVORITES 171 VK_BROWSER_HOME 172 VK_VOLUME_MUTE 173 VolumeMute VK_VOLUME_DOWN 174 VolumeDown VK_VOLUME_UP 175 VolumeUp VK_MEDIA_NEXT_TRACK 176 VK_MEDIA_PREV_TRACK 177 VK_MEDIA_STOP 178 VK_MEDIA_PLAY_PAUSE 179 VK_LAUNCH_MAIL 180 VK_LAUNCH_MEDIA_SELECT 181 VK_LAUNCH_APP1 182 VK_LAUNCH_APP2 183 VK_OEM_1 186 ; : VK_OEM_PLUS 187 = + VK_OEM_COMMA 188 VK_OEM_MINUS 189 - _ VK_OEM_PERIOD 190 VK_OEM_2 191 / ? VK_OEM_3 192 ` ~ VK_OEM_4 219 [ { VK_OEM_5 220 \ | VK_OEM_6 221 ] } VK_OEM_7 222 ‘ " VK_OEM_8 223 VK_OEM_102 226 VK_PACKET 231 VK_PROCESSKEY 229 VK_ATTN 246 VK_CRSEL 247 VK_EXSEL 248 VK_EREOF 249 VK_PLAY 250 VK_ZOOM 251 VK_NONAME 252 VK_PA1 253 VK_OEM_CLEAR 254
为了实现组合键,对于每个要处理的按键,都应该调用函数实现该键的按下,而且注意已经按下的键不能反复按下,当全部键松開始,要清空全部键的按下情况,为了实现这个目的。使用动态数组ArrayList来记录已经按下的键。
要使用ArrayList,要引用:
using System.Collections;ArrayList基本的方法是Contains推断元素是否在数组内。Clear删除全部元素。Add加入元素。
我们在每一个键按下时先推断ArrayList是否包括该元素,不包括则调用函数让该键按下。而且把键码加入到数组内。否则不动作。
当按键所有释放时。应当遍历ArrayList数组,让所有按下的键释放,然后清空ArrayList。
通过这种逻辑,我们就能够实现不论什么按键事件了。
以下详细解说各个模块的实现方法:
【按键的按下与释放】
由于要引入DLL。因此加入引用:
using System.Runtime.InteropServices;然后引用一个DLL来处理键盘:
[DllImport("USER32.DLL")] public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);</span>
这个函数就是上面介绍过的按键处理函数。直接调用就可以模拟键盘操作,为了实现上面的业务逻辑,在按键按下时先推断是否已经被按下。然后处理,封装一个函数处理按键:
当中al是一个动态数组。定义例如以下:
static ArrayList al = new ArrayList(0);
static void pressKey(byte keycode) { if (!al.Contains(keycode)) { al.Add(keycode); keybd_event(keycode, 0, 0, 0); } }
这样就实现了按键的按下,而且避免了反复按下。
当按键所有释放时。要释放所有已经按下的按键,而且清空al:
foreach (byte key in al) { keybd_event(key, 0, 2, 0); } al.Clear();
【socket的实现】
首先引用:
using System.Net; using System.Net.Sockets;然后使用一个函数实现socket的监听
static void runSocket() { //设定serverIP地址 IPAddress ip = IPAddress.Parse("<ip地址>"); Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { clientSocket.Connect(new IPEndPoint(ip, <port号>)); //配置serverIP与port Console.WriteLine("连接server成功"); } catch { Console.WriteLine("连接server失败,请按回车键退出!"); return; } //通过clientSocket接收数据 while (true) { int receiveLength = clientSocket.Receive(result); string str = Encoding.ASCII.GetString(result, 0, receiveLength); // Console.WriteLine("<" + str + ">"); int option = -1; try { option = int.Parse(str); } catch { } // Console.WriteLine("option = " + option); state = option; } }
之所以定义一个函数,是为了在子线程中监听socket,socket收到的操作码进行解析。解析成功后则赋值为state。state就是主线程要处理的操作码。代表着按键的按下。
开启socket线程的代码,写在main函数中:
Thread t = new Thread(runSocket); t.Start();
接下来的部分就是针对不同的按键进行处理了,以下贴出完整的源代码,这是一个C#控制台程序:
为了安全。我把自己的ip和port都去掉了,假设要使用这个源代码,须要注意下面事项:
①socket服务端可以依据手柄的动作发送字符0~9、-1。
②电脑端的程序保持开启状态,调试与执行状态皆可。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net; using System.Net.Sockets; using System.Threading; using System.Runtime.InteropServices; using System.Collections; namespace SocketClient { class Program { static int state = 0; static ArrayList al = new ArrayList(0); private static byte[] result = new byte[1024]; [DllImport("USER32.DLL")] public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); static void pressKey(byte keycode) { if (!al.Contains(keycode)) { al.Add(keycode); keybd_event(keycode, 0, 0, 0); } } static void Main(string[] args) { Thread t = new Thread(runSocket); t.Start(); al.Clear(); state = -1; while (true) { if (state != -1) { switch (state) { case 0: // W = 87 pressKey(87); break; case 1: // S = 83 pressKey(83); break; case 2: // A = 65 pressKey(65); break; case 3: // D = 68 pressKey(68); break; case 4: // J = 74 pressKey(74); break; case 5: // K = 75 pressKey(75); break; case 6: // L = 76 pressKey(76); break; case 7: // U = 85 pressKey(85); break; case 8: // I = 73 pressKey(73); break; case 9: // O = 79 pressKey(79); break; } } else { foreach (byte key in al) { keybd_event(key, 0, 2, 0); } al.Clear(); } } } static void runSocket() { //设定serverIP地址 IPAddress ip = IPAddress.Parse("42.96.168.162"); Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { clientSocket.Connect(new IPEndPoint(ip, 12345)); //配置serverIP与端口 Console.WriteLine("连接server成功"); } catch { Console.WriteLine("连接server失败,请按回车键退出!"); return; } //通过clientSocket接收数据 while (true) { int receiveLength = clientSocket.Receive(result); string str = Encoding.ASCII.GetString(result, 0, receiveLength); // Console.WriteLine("<" + str + ">"); int option = -1; try { option = int.Parse(str); } catch { } // Console.WriteLine("option = " + option); state = option; } } } }
查了无数资料才实现了这么几个功能。实属不易,希望对须要的各位有所帮助。
使用C#+socket实现用移动设备控制的虚拟手柄