最近在和同学玩死神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 Help48 049 150 251 352 453 554 655 756 857 965 A66 B67 C68 D69 E70 F71 G72 H73 I74 J75 K76 L77 M78 N79 O80 P81 Q82 R83 S84 T85 U86 V87 W88 X89 Y90 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与portConsole.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 = 87pressKey(87);break;case 1: // S = 83pressKey(83);break;case 2: // A = 65pressKey(65);break;case 3: // D = 68pressKey(68);break;case 4: // J = 74pressKey(74);break;case 5: // K = 75pressKey(75);break;case 6: // L = 76pressKey(76);break;case 7: // U = 85pressKey(85);break;case 8: // I = 73pressKey(73);break;case 9: // O = 79pressKey(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;}}}
}

查了无数资料才实现了这么几个功能。实属不易,希望对须要的各位有所帮助。

转载于:https://www.cnblogs.com/zhchoutai/p/6829930.html

使用C#+socket实现用移动设备控制的虚拟手柄相关推荐

  1. 基于socket实现虚拟手柄使用手机控制电脑游戏(下)-手机端虚拟手柄

    基于socket实现虚拟手柄使用手机控制电脑游戏(下)-手机端虚拟手柄 这个是关于利用socket套接字实现手机控制电脑按键的一个实例,完成这个项目可以实现用手机控制狂野飙车等游戏,就是一个简易的手机 ...

  2. 基于socket实现虚拟手柄使用手机控制电脑游戏(上)-电脑服务端

    基于socket实现虚拟手柄使用手机控制电脑游戏(上)-电脑服务端 这个是关于利用socket套接字实现手机控制电脑按键的一个实例,完成这个项目可以实现用手机控制狂野飙车等游戏,就是一个简易的手机虚拟 ...

  3. scrcpy源码阅读及在Ubuntu上的实现(一)——了解原理

    那开篇就问问为什么需要研究这个源码吧: 在移动互联网的时代下,手机的功能是日益增加的,要使工作变得更加的高效,那么键盘鼠标其实是必不可少的.在许多软件的架构中,其实并没有提供对应的桌面版本,也不兼容基 ...

  4. linux+postfix+extmail+dovecot搭建邮件服务器

    一.我们可以重新搭建服务器,也可以利用我前面的搭建方法编译安装,地址: http://wangzan18.blog.51cto.com/8021085/1605480,本次我们使用yum的方法来安装h ...

  5. Java基础部分笔记----------网络编程

    2019独角兽企业重金招聘Python工程师标准>>> 1.Java网络基本支持 1.1 InetAddress Java中的InetAddress是一个代表IP地址的对象.IP地址 ...

  6. 虚拟化对VMcpu分配的理解

    我这里以vSphere 5.5来举例说明 在我们虚拟化的环境中,VM的cpu数量如何分配,影响着VM的计算性能,更影响着ESXi主机的性能. 如果虚拟机少,能做到cpu不超分,那肯定是没问题,但是这种 ...

  7. DPDK support for vhost-user(十四)

    X86体系早期没有在硬件设计上对虚拟化提供支持,因此虚拟化完全通过软件实现.一个典型的做法是通过优先级压缩(Ring Compression)和二进制代码翻译(Binary Translation)相 ...

  8. rsync的配置文件模板及简单介绍,命令及参数

    必须知道推送有一个限速参数.--bwlimit=100 工作总必须要加. 有三种模式, 1.本地的模拟cp命令,在一个服务器 2.远程的两个服务器之间,模拟scp 3.以socket进程监听的方式启动 ...

  9. c++ websocket客户端_阿里面经WebSocket实时通信

    一.websocket概述 1.随着互联网的快速发展,程序员需要处理的业务场景也越来越多了,ajax对于一些常链接实时刷新数据特殊的场景的处理就显的很无力,于是webSocket出现了. 2.HTTP ...

最新文章

  1. Facebook Docusaurus 中文文档 安装指南
  2. 小程序---canvas画图,生成分享图片,画图文字换行
  3. Lua的upvalue和闭包
  4. Android之网络请求通过协程+okhttp的没有做网络异常处理导致程序奔溃问题
  5. jquery自定义banner图滚动插件---(解决最后一张图片倒回第一张图片的bug)
  6. Python项目:用所有朋友微信头像做云图图
  7. 整数的素因子分解:Pollard rho method
  8. php使用gd库合并图片,php使用GD库合并简单图片并变动部分颜色
  9. 多频子量子计算机,量子计算机研究:纠错和容错计算
  10. 苏州外包php,【苏州IT外包经验】Ubuntu 17.10系统下配置PHP+Apache+Mysql
  11. 用Python实现辞职信
  12. 用瑞利准则研究显微镜物镜的分辨率
  13. 快手小程序常用事件绑定使用场景总结
  14. FlashDB数据库+Norflash移植和使用
  15. CT医学影像的窗高窗位、CT值(Hu值)
  16. 万年历SQL Server中实现
  17. C语言程序设计作业07:第十六周作业
  18. 创意小项目:单片机锂电池容量测试仪设计制作 附C语言源代码
  19. AI也能作曲?谁来定义AI的freestyle?
  20. Unity 自带光晕组件学习

热门文章

  1. 爬虫实践(二)--掌阅书城
  2. 自然资源综合执法案件管理系统(新规程)
  3. 用户体验的分类----产品经理深入浅出课程
  4. 解决cc1plus.exe: error: out of memory allocating
  5. 2016《互联网周刊》年度人物候选人公示(8)
  6. 柯洁仍然是中国围棋冠军,向科学认输、底头不是耻辱!
  7. 泛微全程数字化售后管理方案:统一信息渠道,提高服务效率
  8. 用proteus来玩数字电路与门和其它门
  9. topshelf使用教程
  10. 这个星期一直在努力的——微博系统