使用C#+socket實現用行動裝置控制的虛擬手柄

來源:互聯網
上載者:User

標籤:c#   socket   虛擬手柄   類比鍵盤   dll   

近期在和同學玩死神vs火影,以懷念小時候,突然覺得用鍵盤玩的不夠暢快,因此萌生了寫一個虛擬手柄的念頭。

我的思路是在行動裝置(iOS、Android)上實現手柄,在電腦上監聽,利用socket建立持久串連,通過行動裝置向電腦上的監聽軟體發送作業碼,通過作業碼來處理事件。

有關socket的服務端,建立在一個伺服器上,讓行動裝置和電腦分別串連,建立通道,在伺服器上使用python建立socket用戶端與在行動裝置上使用socket十分便利,這裡不講述,本文的重點是實現電腦上根據鍵值實現的按鍵事件,包括按鍵組合的處理。

我們假設虛擬手柄有4+6個鍵,分別是上下左右,1-6功能鍵,發送的作業碼碼分別為0~9,當所有按鍵鬆開,發送的作業碼為-1。

為了實現按鍵動作,需要藉助USER32.DLL的keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo)函數,第一個參數是鍵碼,第二個和第四個填0即可,第三個代表是按下按鍵還是鬆開,0表示按下,2表示鬆開。

由於C#無法直接調用那些宏,因此鍵碼輸入數字來實現,鍵碼對應表如下:

<span style="font-size:18px;">虛擬鍵碼   對應值  對應鍵VK_LBUTTON 1 滑鼠左鍵VK_RBUTTON 2 滑鼠右鍵VK_CANCEL 3 CancelVK_MBUTTON 4 滑鼠中鍵VK_XBUTTON1 5 VK_XBUTTON2 6 VK_BACK 8 BackspaceVK_TAB 9 TabVK_CLEAR 12 ClearVK_RETURN 13 EnterVK_SHIFT 16 ShiftVK_CONTROL 17 CtrlVK_MENU 18 AltVK_PAUSE 19 PauseVK_CAPITAL 20 Caps LockVK_KANA 21 VK_HANGUL 21 VK_JUNJA 23 VK_FINAL 24 VK_HANJA 25 VK_KANJI 25* VK_ESCAPE 27 EscVK_CONVERT 28 VK_NONCONVERT 29 VK_ACCEPT 30 VK_MODECHANGE 31 VK_SPACE 32 SpaceVK_PRIOR 33 Page UpVK_NEXT  34 Page DownVK_END                 35 EndVK_HOME 36 HomeVK_LEFT   37 Left ArrowVK_UP                 38 Up ArrowVK_RIGHT 39 Right ArrowVK_DOWN 40 Down ArrowVK_SELECT 41 SelectVK_PRINT 42 PrintVK_EXECUTE 43 ExecuteVK_SNAPSHOT 44 SnapshotVK_INSERT 45 InsertVK_DELETE 46 DeleteVK_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 ZVK_LWIN 91 VK_RWIN 92 VK_APPS 93 VK_SLEEP 95 VK_NUMPAD0 96 小鍵盤 0VK_NUMPAD1 97 小鍵盤 1VK_NUMPAD2 98 小鍵盤 2VK_NUMPAD3 99 小鍵盤 3VK_NUMPAD4 100 小鍵盤 4VK_NUMPAD5 101 小鍵盤 5VK_NUMPAD6 102 小鍵盤 6VK_NUMPAD7 103 小鍵盤 7VK_NUMPAD8 104 小鍵盤 8VK_NUMPAD9 105 小鍵盤 9VK_MULTIPLY 106 小鍵盤 *VK_ADD 107 小鍵盤 +VK_SEPARATOR 108 小鍵盤 EnterVK_SUBTRACT 109 小鍵盤 -VK_DECIMAL 110 小鍵盤 .VK_DIVIDE 111 小鍵盤 /VK_F1 112 F1VK_F2 113 F2VK_F3 114 F3VK_F4 115 F4VK_F5 116 F5VK_F6 117 F6VK_F7 118 F7VK_F8 119 F8VK_F9 120 F9VK_F10 121 F10VK_F11 122 F11VK_F12 123 F12VK_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 LockVK_SCROLL 145 ScrollVK_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 VolumeMuteVK_VOLUME_DOWN 174 VolumeDownVK_VOLUME_UP 175 VolumeUpVK_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</span>

為了實現按鍵組合,對於每一個要處理的按鍵,都應該調用函數實現該鍵的按下,並且注意已經按下的鍵不能重複按下,當所有鍵鬆開始,要清空所有鍵的按下情況,為了實現這個目的,使用動態數組ArrayList來記錄已經按下的鍵。

要使用ArrayList,要引用:

<span style="font-size:18px;">using System.Collections;</span>
ArrayList主要的方法是Contains判斷元素是否在數組內,Clear刪除所有元素,Add添加元素。

我們在每個鍵按下時先判斷ArrayList是否包含該元素,不包含則調用函數讓該鍵按下,並且把鍵碼添加到數組內,否則不動作。

當按鍵全部釋放時,應當遍曆ArrayList數組,讓所有按下的鍵釋放,然後清空ArrayList。

通過這樣的邏輯,我們就可以實現任何按鍵事件了。

下面具體講解各個模組的實現方法:

【按鍵的按下與釋放】

因為要引入DLL,因此添加引用:

<span style="font-size:18px;">using System.Runtime.InteropServices;</span>
然後引用一個DLL來處理鍵盤:

<span style="font-size:18px;">[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);

<span style="font-size:18px;">static void pressKey(byte keycode){      if (!al.Contains(keycode))      {         al.Add(keycode);         keybd_event(keycode, 0, 0, 0);      }}</span>

這樣就實現了按鍵的按下,並且避免了重複按下。

當按鍵全部釋放時,要釋放所有已經按下的按鍵,並且清空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()        {            //設定伺服器IP地址            IPAddress ip = IPAddress.Parse("<ip地址>");            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            try            {                clientSocket.Connect(new IPEndPoint(ip, <連接埠號碼>)); //設定管理員IP與連接埠                Console.WriteLine("串連伺服器成功");            }            catch            {                Console.WriteLine("串連伺服器失敗,請按斷行符號鍵退出!");                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()        {            //設定伺服器IP地址            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)); //設定管理員IP與連接埠                Console.WriteLine("串連伺服器成功");            }            catch            {                Console.WriteLine("串連伺服器失敗,請按斷行符號鍵退出!");                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實現用行動裝置控制的虛擬手柄

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.