轉:https://www.cnblogs.com/qyit/archive/2011/11/21/2257687.html
一個手柄/鍵盤映射程序,無外乎就四部分:一、界面;二、接收;三、處理;四、輸出。
界面就不多說了。
接收,就是接收手柄的輸出。這部分有多種方法,比如windows API和DirectX,這里我們選前者。
處理,就是將接收到的數據映射為輸出數據。
輸出,就是向操作系統發送假的鍵盤事件,從而完成映射過程。
接收部分:
那么,我們先來進行知識的准備。為了完成接收部分,我們需要了解和手柄相關的windows API。其中常用的較重要的函數如下:
joyGetDevCaps 查詢指定的游戲桿設備以確定其性能 joyGetNumDevs 返回系統支持的游戲桿設備的數量 joyGetPos 查詢指定的游戲桿設備的位置和活動性 joyGetPosEx 查詢一個游戲桿設備的位置和它的按扭狀態 joyGetThreshold 查詢指定的游戲桿設備的當前移動閾值 joyReleaseCapture 釋放由JoySetCapture函數設置的在指定游戲桿設備上的捕獲 joySetCapture 發送一個游戲桿消息到指定的窗口 joySetThreshold 設置指定的游戲桿設備的移動閾值
要使用這幾個API,需要連接winmm.lib,包含mmsystem.h頭文件。
如果僅制作基本的映射功能,那么我們並不需要用到全部的函數。主要使用的是這個:
MMRESULT joyGetPosEx(UINT uJoyID, LPJOYINFOEX pji)
這個函數可以主動取得游戲桿信息。
參數uJoyID是指手柄的ID,對於單手柄編程,填寫JOYSTICKID1即可。
參數pji是一個指向JOYINFOEX型結構體的指針。JOYINFOEX的定義如下:
typedef struct joyinfoex_tag { DWORD dwSize; /* size of structure */ DWORD dwFlags; /* flags to indicate what to return */ DWORD dwXpos; /* x position */ DWORD dwYpos; /* y position */ DWORD dwZpos; /* z position */ DWORD dwRpos; /* rudder/4th axis position */ DWORD dwUpos; /* 5th axis position */ DWORD dwVpos; /* 6th axis position */ DWORD dwButtons; /* button states */ DWORD dwButtonNumber; /* current button number pressed */ DWORD dwPOV; /* point of view state */ DWORD dwReserved1; DWORD dwReserved2; } JOYINFOEX, *PJOYINFOEX, NEAR *NPJOYINFOEX, FAR *LPJOYINFOEX;
它包含了指定手柄當前的狀態信息。我們主要用到的是其中的dwFlags,dwXpos,dwYpos以及dwButtons。這四個成員依次表示:獲取狀態,十字鍵X軸當前狀態,十字鍵Y軸當前狀態,功能鍵當前狀態。
我們在使用joyGetPosEx獲得手柄狀態前,先要把dwFlags設置為JOY_RETURNALL,即返回全部按鍵狀態,這樣才能同時獲得十字鍵和功能鍵的信息。
dwXpos和dwYpos的值分別代表了X軸和Y軸當前的狀態。對於使用windows默認的自帶手柄驅動的,按鍵情況和對應值如下:
注意:如果安裝了手柄驅動盤,值會隨驅動不同而改變,請自行測定
而dwButtons的每一位對應手柄的一個功能鍵狀態,0表示抬起狀態,1表示按下狀態。(注意,是狀態,不是動作)對應關系是從低位到高位對應功能鍵1至手柄功能鍵最高數。
joyGetPosEx函數的返回值可能是以下幾種:
/* joystick error return values */ #define JOYERR_NOERROR (0) /* no error */ #define JOYERR_PARMS (JOYERR_BASE+5) /* bad parameters */ #define JOYERR_NOCANDO (JOYERR_BASE+6) /*request not completed */ #define JOYERR_UNPLUGGED (JOYERR_BASE+7) /*joystick is unplugged */
根據它,我們可以判斷電腦當前否有手柄連接。
輸出部分:
鍵盤模擬部們,我們只需要使用一個很簡單的函數keyevent
void keybd_event(BYTE bVk,BYTE bScan,DWORD dwFlags,DWORD dwExtralnfo);
參數:
bVk:定義一個虛擬鍵碼。鍵碼值必須在1~254之間。
bScan:定義該鍵的硬件掃描碼。
dwFlags:定義函數操作的名個方面的一個標志位集。應用程序可使用如下一些預定義常數的組合設置標志位。
KEYEVENTF_EXETENDEDKEY:若指定該值,則掃描碼前一個值為OXEO(224)的前綴字節。
KEYEVENTF_KEYUP:若指定該值,該鍵將被釋放;若未指定該值,該鍵交被接下。 (其實就是一個是0一個是2,0表示鍵按下,1表示鍵彈起)
dwExtralnfo:定義與擊鍵相關的附加的32位值。
表面上看着好像很點亂,其實沒那么復雜。
給一個使用實例:
#define KEY 要模擬的鍵的ASCII碼 keybd_event(KEY,MapVirtualKey(KEY, 0),0,0);
注意,這里使用MapVirtualKey()函數是有必要的。對於很多游戲,單純的使用keybd_event(KEY,0,0,0)的模式是不能被正確識別的,
換句話說,游戲程序會把那些偽造的鍵盤信息過濾掉或根本不接受。但使用MapVirtualKey()后,大部分游戲就會識別到我們發送的鍵盤信息了。
簡單demo
#include<stdio.h> #include<stdlib.h> #include<conio.h> #include<Windows.h> //添加joystick操作api的支持庫 #include<MMSystem.h> #pragma comment(lib, "Winmm.lib") int main(int argc, char* argv[]) { JOYINFO joyinfo; //定義joystick信息結構體 JOYINFOEX joyinfoex;//定義joystick信息結構體 joyinfoex.dwSize = sizeof(JOYINFOEX); joyinfoex.dwFlags = JOY_RETURNALL; while(1) { //讀取手柄信息 UINT joyNums; joyNums = joyGetNumDevs(); // printf("當前手柄數量:%d \n",joyNums); if (joyNums>=1) { MMRESULT joyreturn = joyGetPosEx(JOYSTICKID1, &joyinfoex); if(joyreturn == JOYERR_NOERROR) { printf("當前X坐標:%d \n", joyinfoex.dwXpos); printf("當前Y坐標:%d \n", joyinfoex.dwYpos); printf("當前Z坐標:%d \n", joyinfoex.dwZpos); printf("視點控制的當前位置:%d \n", joyinfoex.dwPOV); printf("32個操縱桿按鈕的當前狀態:%d \n", joyinfoex.dwButtons); printf("按下的當前按鈕編號:%d\n ", joyinfoex.dwButtonNumber);; printf("\n"); }else { switch(joyreturn) { case JOYERR_PARMS : printf("bad parameters\n"); break; case JOYERR_NOCANDO : printf("request not completed\n"); break; case JOYERR_UNPLUGGED : printf("joystick is unplugged\n"); break; default: printf("未知錯誤\n"); break; } } } if(kbhit()) break; Sleep(300); } return 0; }