Windows上的應(yīng)用軟件如何接收諾為翻頁筆的APP通道數(shù)據(jù)
諾為翻頁筆對(duì)電腦來說,就是一個(gè)和鼠標(biāo)、鍵盤一樣的USB HID設(shè)備(Human Interface Device)。其所用的技術(shù),和無線鍵盤、無線鼠標(biāo)是一樣的。
目前,如果想在電腦上獲得翻頁筆的按鍵信息,或者說想用翻頁筆來控制電腦上的軟件,有兩種方法。一種是將翻頁筆的按鍵功能自定義為電腦中的應(yīng)用軟件的快捷鍵,應(yīng)用軟件通過獲得USB HID通道傳來的快捷鍵,來觸發(fā)相應(yīng)的功能。這種自定義按鍵的方法,請(qǐng)參見諾為官方網(wǎng)站上的Norwii Presenter軟件的說明書,有比較詳細(xì)的介紹。另一種是先自定義翻頁筆按鍵的APP通道的數(shù)據(jù),應(yīng)用軟件通過獲取APP通道的數(shù)據(jù)來接受控制。本文講述的是Windows上的應(yīng)用軟件如何自定義按鍵的APP通道數(shù)據(jù)、讀取諾為翻頁筆的按鍵的APP通道數(shù)據(jù),主要是程序?qū)崿F(xiàn)。
這里的APP通道指的是一個(gè)用來和APP通信的諾為定義的HID通道。
可以把按鍵的短按、長按、雙擊自定義為APP通道的數(shù)據(jù)。本文所述的短按是按鍵抬起時(shí)觸發(fā),只要沒有超過長按計(jì)時(shí)器的按鍵抬起,都是短按。長按是持續(xù)按住按鍵超過長按計(jì)時(shí)器時(shí)觸發(fā)。長按計(jì)時(shí)器一般為1秒,不同的產(chǎn)品或按鍵可能會(huì)有差異。
一、諾為設(shè)備使用Norwii Presenter軟件設(shè)置APP通道
1. 在Norwii Presenter軟件的【功能自定義】頁面,點(diǎn)擊軟件界面上顯示的翻頁筆按鍵圖標(biāo)或這個(gè)圖標(biāo)對(duì)應(yīng)的藍(lán)色文字,會(huì)打開按鍵自定義窗口。翻頁筆的按鍵模式有3種或者4種,翻頁筆的按鍵模式通過同時(shí)長按翻頁筆的上、下翻頁鍵進(jìn)行切換,下圖中有4種模式。用戶可以選擇按鍵模式中的一種進(jìn)行自定義,自定義的結(jié)果也只在這種模式下有效。
2. 點(diǎn)擊“短按功能”處“下拉框箭頭”-“APP通道”進(jìn)入APP通道窗口,在編輯框輸入十進(jìn)制數(shù)字1~255的一個(gè)數(shù)值作為APP通道的值,點(diǎn)擊“確定”后,在上個(gè)窗口點(diǎn)擊“保存”。
3. 如何確定將翻頁筆按鍵的值自定義為APP通道的數(shù)據(jù)是成功的?
在【對(duì)碼】頁面,短按已經(jīng)把短按功能自定義為“APP通道”的按鍵,窗口右下方白色框中出現(xiàn)“APP通道+數(shù)字”,如“APP通道 1”,說明翻頁筆按鍵的APP通道的自定義設(shè)置成功了。按照這種方法,也可能對(duì)按鍵的長按功能進(jìn)行自定義。
4. 如何獲取諾為翻頁筆的VID和PID。
在【關(guān)于】頁面,找到顯示“固件信息”的行,右側(cè)顯示諾為翻頁筆的VID和PID,這兩個(gè)值在USB HID通信中會(huì)使用到。頁面顯示的這兩個(gè)值是省略了前綴0x的16進(jìn)制數(shù)值,在程序中使用的時(shí)候記得加上前綴0x,下圖中VID是16進(jìn)制數(shù)值0x3243,PID是16進(jìn)制數(shù)值 0x0341,諾為翻頁筆的VID都是相同的,不同型號(hào)的產(chǎn)品的PID是不同的,同一型號(hào)的產(chǎn)品的PID一般是相同的,但同一型號(hào)的產(chǎn)品、不同的主控芯片也會(huì)有不同的PID。
二、USB HID通信
1. 引用到的文件和庫。
1. #include <windows.h>
2. #include <vector>
3. #include <string> // Includes the string functions.
4. #include <setupapi.h> // Includes the SetupAPI.
5. #include "hidapi.h"
6. using namespace std;
7. #pragma comment (lib, "setupapi.lib")
2. 通過VID、PID獲取設(shè)備的數(shù)據(jù)通道的HID地址關(guān)鍵詞;如VID:0x3243,PID:0x0341,它的HID地址關(guān)鍵詞是“#vid_3243&pid_0341&mi_02&col01#”。
1. string GetNorwiiKeyPath(const int vid, const int pid)
2. {
3. if (vid == 0x023243 & pid == 0x0352)
4. return "_dev_vid&023243_pid&0352_rev&0001_";
5. if (vid == 0x023243 & pid == 0x0361)
6. return "_dev_vid&023243_pid&0361_rev&0001_";
7. std::vector<string> vtHidPathKey;
8. vtHidPathKey.push_back("vid_3243&pid_0111&mi_00&col03");
9. vtHidPathKey.push_back("vid_3243&pid_0121&mi_01&col02");
10. vtHidPathKey.push_back("vid_3243&pid_0221&mi_01&col02");
11. vtHidPathKey.push_back("#vid_3243&pid_0131&mi_00&col03#");
12. vtHidPathKey.push_back("#vid_3243&pid_1234&mi_00&col03#");
13. vtHidPathKey.push_back("#vid_3243&pid_0112&mi_00&col03#");
14. vtHidPathKey.push_back("#vid_3243&pid_0212&mi_00&col03#");
15. vtHidPathKey.push_back("#vid_3243&pid_0122&mi_01&col03#");
16. vtHidPathKey.push_back("#vid_3243&pid_0122&mi_02#");
17. vtHidPathKey.push_back("#vid_3243&pid_0222&mi_01&col03#");
18. vtHidPathKey.push_back("#vid_3243&pid_0341&mi_02&col01#");
19. vtHidPathKey.push_back("#vid_3243&pid_0342&mi_02&col01#");
20. vtHidPathKey.push_back("#vid_3243&pid_0381&mi_02&col01#");
21. vtHidPathKey.push_back("#vid_3243&pid_0382&mi_02&col01#");
22.
23. string sKeyPath;
24. char temp_char[20] = { 0 };
25. sprintf(temp_char, "&pid_%04x&", pid);
26. std::vector<string>::iterator iter = vtHidPathKey.begin();
27. for (; iter != vtHidPathKey.end(); iter++)
28. {
29. string sTempKeyPath = *iter;
30. if (sTempKeyPath.find(temp_char) != string::npos)
31. {
32. sKeyPath = sTempKeyPath;
33. break;
34. }
35. }
36. return sKeyPath;
37. }
3. 遍歷Windows系統(tǒng)所有USB和藍(lán)牙設(shè)備的HID設(shè)備接口的路徑,來收集所有的設(shè)備接口路徑。
1. string GetDevicePath(HDEVINFO deviceInfoSet, SP_DEVICE_INTERFACE_DATA deviceInterfaceData)
2. {
3. DWORD bufferSize = 0;
4. SetupDiGetDeviceInterfaceDetailA(deviceInfoSet, &deviceInterfaceData, NULL, 0, &bufferSize, NULL);
5.
6. SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;
7. device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*)malloc(bufferSize);
8. device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
9.
10. string sDevicePath =
11. SetupDiGetDeviceInterfaceDetailA(deviceInfoSet, &deviceInterfaceData, device_interface_detail_data, bufferSize, &bufferSize, NULL) ?
12. device_interface_detail_data->DevicePath : "";
13. free(device_interface_detail_data);
14. return sDevicePath;
15. }
16.
17. vector<string> EnumeratePath()
18. {
19. vector<string> devices;
20. GUID InterfaceClassGuid = { 0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
21. //GUID InterfaceClassGuid = { 0xa5dcbf10L, 0x6530, 0x11d2,{0x90, 0x1f, 0x00, 0xc0, 0x4f, 0xb9, 0x51, 0xed} };
22. HDEVINFO device_info_set = INVALID_HANDLE_VALUE;
23. //Get information for all the devices belonging to the HID class.
24. device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
25. if (device_info_set != INVALID_HANDLE_VALUE)
26. {
27. int device_index = 0;
28. SP_DEVINFO_DATA devinfo_data;
29. memset(&devinfo_data, 0x0, sizeof(devinfo_data));
30. devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
31.
32. while (SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data))
33. {
34. device_index += 1;
35.
36. SP_DEVICE_INTERFACE_DATA device_interface_data;
37. device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
38. int deviceInterfaceIndex = 0;
39.
40. while (SetupDiEnumDeviceInterfaces(device_info_set, &devinfo_data, &InterfaceClassGuid, deviceInterfaceIndex, &device_interface_data))
41. {
42. deviceInterfaceIndex++;
43. string devicePath = GetDevicePath(device_info_set, device_interface_data);
44.
45. devices.push_back(devicePath);
46. }
47. }
48. SetupDiDestroyDeviceInfoList(device_info_set);
49. }
50. return devices;
51. }
4. 獲取諾為翻頁筆的VID、PID通信的HID地址。
遍歷Windows系統(tǒng)所有USB和藍(lán)牙設(shè)備HID設(shè)備接口的路徑,檢查是否有一條地址含有前面介紹的HID設(shè)備接口的路徑關(guān)鍵詞。如果有,該條HID設(shè)備接口的路徑就是諾為翻頁筆的VID、PID的HID設(shè)備接口的路徑。
如果有這樣一條HID設(shè)備接口的路徑\?hid#vid_3243&pid_0341&mi_02&col01#7&34f72933&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030},它里面含有HID設(shè)備接口的路徑關(guān)鍵詞“#vid_3243&pid_0341&mi_02&col01#”,這個(gè)HID設(shè)備接口的路徑就是諾為翻頁筆的VID、PID的HID通信地址。
1. string GetNorwiiHidPath(const string &sKeyPath)
2. {
3. string sCustomizedPath;
4. vector<string> vtAllPath = EnumeratePath();
5. for (int k = 0; k < vtAllPath.size(); k++)
6. {
7. string rString = vtAllPath[k];
8. if (rString.find(sKeyPath) != string::npos)
9. {
10. if (rString.find("_dev_vid&023243_pid&0352_rev&0001_") != string::npos ||
11. rString.find("_dev_vid&023243_pid&0361_rev&0001_") != string::npos)
12. {
13. if (rString.find("&col03#") == string::npos)
14. continue;
15. }
16. sCustomizedPath = rString;
17. break;
18. }
19. }
20. return sCustomizedPath;
21. }
5. 打開HID設(shè)備接口的路徑進(jìn)行通信,要調(diào)用hidapi.h文件中的類庫函數(shù)hid_open_path
1. string szHidPath = "\?hid#vid_3243&pid_0341&mi_02&col01#7&34f72933&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
2. hid_device *hidHandle = hid_open_path(szHidPath.c_str());
6. 關(guān)閉已打開的HID設(shè)備接口的路徑,要調(diào)用hidapi.h文件中的類庫函數(shù)hid_close。
1. if (hidHandle)
2. {
3. hid_close(hidHandle);
4. }
7.接收HID數(shù)據(jù),判斷收到APP通道數(shù)據(jù),要調(diào)用hidapi.h文件中的類庫函數(shù)hid_read_timeout。
1. size_t length = 8;
2. unsigned char data[8] = { 0 };
3. int res = hid_read_timeout(hidHandle, data, length, 5);
4. if (res > 0 && data[0] > 0x00)
5. {
6. if (data[0] == 0x03 && data[1] == 0x31 && data[2] == 0x00 &&
7. data[3] == 0x00 && data[4] == 0x02)
8. {
9. int appPath = data[5];//獲取到APP通道值
10. std::cout << "APP Path" << appPath << " ";
11. }
12. }
8. 下面代碼為整體通信代碼。
1. struct hid_device_ {
2. HANDLE device_handle;
3. BOOL blocking;
4. USHORT output_report_length;
5. size_t input_report_length;
6. void *last_error_str;
7. DWORD last_error_num;
8. BOOL read_pending;
9. char *read_buf;
10. OVERLAPPED ol;
11. };
12.
13. int main()
14. {
15. string szKeyPath = GetNorwiiKeyPath(0x3243, 0x341);
16. if (!szKeyPath.empty())
17. {
18. string szHidPath = GetNorwiiHidPath(szKeyPath);
19. if (!szHidPath.empty())
20. {
21. hid_device *hidHandle = hid_open_path(szHidPath.c_str());
22. if (hidHandle)
23. {
24. std::cout << "connect success! ";
25. int nPendingCount = 0;
26. size_t length = 8;
27. unsigned char data[8] = { 0 };
28. while (true)
29. {
30. int res = hid_read_timeout(hidHandle, data, length, 5);
31. if (res > 0 && data[0] > 0x00)
32. {
33. nPendingCount = 0;
34. if (data[0] == 0x03 && data[1] == 0x31 && data[2] == 0x00 &&
35. data[3] == 0x00 && data[4] == 0x02)
36. {
37. int appPath = data[5];//獲取到APP通道值
38. std::cout << "APP Path" << appPath << " ";
39. }
40. else if (!hidHandle->read_pending)
41. {
42. nPendingCount++;
43. if (nPendingCount > 100)
44. break;
45. }
46. }
47. }
48. hid_close(hidHandle);
49. }
50. }
51. }
52.
53. std::cout << "Hello World! ";
54. system("pause");
55. }
2.9. 參考代碼地址:
http://www.lygmkjx.com/downloads/presenter/windows/app_path.zip