CPosPrinterWorker.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  1. #include "pch.h"
  2. #include "CPosPrinterWorker.h"
  3. #include "../helper/CComHelper.h"
  4. #include <devpkey.h>
  5. #include <devpropdef.h>
  6. #pragma comment(lib, "setupapi.lib")
  7. //SetupDiGetInterfaceDeviceDetail所需要的输出长度,定义足够大
  8. #define INTERFACE_DETAIL_SIZE 1024
  9. //USB类的GUID,通过这个去找USB打印机
  10. const GUID USB_GUID = { 0x28d78fad, 0x5a12, 0x11d1, {0xae, 0x5b, 0x00, 0x00, 0xf8, 0x03, 0xa8, 0xc2 } };
  11. // 本地定义 DEVPROPKEY,等价于 DEVPKEY_Device_BusReportedDeviceDesc (fmtid + pid=4)
  12. const DEVPROPKEY PKEY_Device_BusReportedDeviceDesc_local =
  13. {
  14. { 0x540b947e, 0x8b40, 0x45bc, { 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2 } },
  15. 4
  16. };
  17. CPosPrinterWorker::CPosPrinterWorker()
  18. {
  19. }
  20. void CPosPrinterWorker::Start()
  21. {
  22. m_is_work = true;
  23. //处理收银打印
  24. std::thread(&CPosPrinterWorker::HandleShouyinPrinter, this).detach();
  25. //处理标签打印
  26. std::thread(&CPosPrinterWorker::HandleBiaoqianPrinter, this).detach();
  27. //处理厨房打印
  28. std::thread(&CPosPrinterWorker::HandleChufangPrinter, this).detach();
  29. }
  30. void CPosPrinterWorker::Stop()
  31. {
  32. m_is_work = false;
  33. }
  34. void CPosPrinterWorker::AddShouyinPrinter(std::string chufang_data)
  35. {
  36. std::lock_guard<std::mutex> lock(m_shouyin_printer_mutex);
  37. m_shouyin_printer_queue.push(chufang_data);
  38. }
  39. void CPosPrinterWorker::AddBiaoqianPrinter(std::string data)
  40. {
  41. std::lock_guard<std::mutex> lock(m_biaoqian_printer_mutex);
  42. m_biaoqian_printer_queue.push(data);
  43. }
  44. void CPosPrinterWorker::AddChufangPrinter(ChufangPrinterContent data)
  45. {
  46. std::lock_guard<std::mutex> lock(m_chufang_printer_mutex);
  47. m_chufang_printer_queue.push(data);
  48. }
  49. void CPosPrinterWorker::HandleShouyinPrinter()
  50. {
  51. while (m_is_work)
  52. {
  53. std::string data;
  54. {
  55. std::lock_guard<std::mutex> lock(m_shouyin_printer_mutex);
  56. if (m_shouyin_printer_queue.empty())
  57. {
  58. data.clear();
  59. }
  60. else
  61. {
  62. data = m_shouyin_printer_queue.front();
  63. }
  64. }
  65. if (data.empty())
  66. {
  67. Sleep(100);
  68. continue;
  69. }
  70. if (SendDataToShouyinPirnter(data))
  71. {
  72. std::lock_guard<std::mutex> lock(m_shouyin_printer_mutex);
  73. if (!m_shouyin_printer_queue.empty() && m_shouyin_printer_queue.front() == data)
  74. {
  75. m_shouyin_printer_queue.pop();
  76. }
  77. }
  78. else
  79. {
  80. Sleep(1000);
  81. }
  82. }
  83. }
  84. void CPosPrinterWorker::HandleBiaoqianPrinter()
  85. {
  86. while (m_is_work)
  87. {
  88. std::string data;
  89. {
  90. std::lock_guard<std::mutex> lock(m_biaoqian_printer_mutex);
  91. if (m_biaoqian_printer_queue.empty())
  92. {
  93. data.clear();
  94. }
  95. else
  96. {
  97. data = m_biaoqian_printer_queue.front();
  98. }
  99. }
  100. if (data.empty())
  101. {
  102. Sleep(100);
  103. continue;
  104. }
  105. if (SendDataToBiaoqianPirnter(data))
  106. {
  107. std::lock_guard<std::mutex> lock(m_biaoqian_printer_mutex);
  108. if (!m_biaoqian_printer_queue.empty() && m_biaoqian_printer_queue.front() == data)
  109. {
  110. m_biaoqian_printer_queue.pop();
  111. }
  112. }
  113. else
  114. {
  115. Sleep(1000);
  116. }
  117. }
  118. }
  119. void CPosPrinterWorker::HandleChufangPrinter()
  120. {
  121. while (m_is_work)
  122. {
  123. ChufangPrinterContent chufang_data;
  124. bool hasData = false;
  125. {
  126. std::lock_guard<std::mutex> lock(m_chufang_printer_mutex);
  127. if (!m_chufang_printer_queue.empty())
  128. {
  129. chufang_data = m_chufang_printer_queue.front();
  130. hasData = true;
  131. }
  132. }
  133. if (!hasData)
  134. {
  135. Sleep(100);
  136. continue;
  137. }
  138. if (SendDataToChufangPirnter(chufang_data))
  139. {
  140. std::lock_guard<std::mutex> lock(m_chufang_printer_mutex);
  141. if (!m_chufang_printer_queue.empty())
  142. {
  143. m_chufang_printer_queue.pop();
  144. }
  145. }
  146. else
  147. {
  148. Sleep(1000);
  149. }
  150. }
  151. }
  152. bool CPosPrinterWorker::SendDataToShouyinPirnter(std::string data)
  153. {
  154. bool bSuccess = false;
  155. //默认是句柄模式
  156. int nType = 1;
  157. //用于厨房打印机和网口类型的收银小票打印
  158. boost::asio::io_context m_io;
  159. boost::asio::ip::tcp::socket m_socket(m_io);
  160. std::string printer_leixing = CSetting::GetInstance()->GetParam("setting_printer_leixing");
  161. if (printer_leixing == "auto")
  162. {
  163. InitUsb();
  164. InitBingkou();
  165. InitCom();
  166. if (m_hShouyinPorts.size() == 0)
  167. {
  168. return false;
  169. }
  170. }
  171. else if (printer_leixing == "usb")
  172. {
  173. std::string printer_usb = CSetting::GetInstance()->GetParam("setting_printer_usb");
  174. std::wstring ws_printer_usb = CLewaimaiString::UTF8ToUnicode(printer_usb);
  175. bool ret = InitOneUsb(ws_printer_usb, 1);
  176. if (ret == false)
  177. {
  178. return false;
  179. }
  180. }
  181. else if (printer_leixing == "bingkou")
  182. {
  183. bool ret = InitBingkou();
  184. if (ret == false)
  185. {
  186. return false;
  187. }
  188. }
  189. else if (printer_leixing == "chuankou")
  190. {
  191. InitCom();
  192. }
  193. else if (printer_leixing == "wangkou")
  194. {
  195. //改为网口模式
  196. nType = 2;
  197. std::string wangkou_ip = CSetting::GetInstance()->GetParam("setting_printer_wangkou_ip");
  198. //初始化连接
  199. try
  200. {
  201. boost::asio::ip::tcp::endpoint ep(boost::asio::ip::make_address(wangkou_ip.c_str()), 9100);
  202. m_socket.connect(ep);
  203. }
  204. catch (std::exception& e)
  205. {
  206. std::string err = e.what();
  207. LOG_INFO("网口打印机连接失败,IP地址:" << wangkou_ip.c_str() << ",错误信息:" << err.c_str());
  208. MessageBoxW(NULL, (L"小票网口打印机连接失败,IP地址:" + CLewaimaiString::UTF8ToUnicode(wangkou_ip)).c_str(), L"打印机连接失败", MB_OK);
  209. return false;
  210. }
  211. }
  212. //开始发送数据
  213. if (nType == 1)
  214. {
  215. bSuccess = true;
  216. //USB、串口、并口,走这里
  217. for (std::vector<HANDLE>::iterator it = m_hShouyinPorts.begin(); it != m_hShouyinPorts.end(); it++)
  218. {
  219. HANDLE hPort = *it;
  220. //同步写数据
  221. DWORD dwWrite = 0;
  222. bool ret = WriteWithTimeout(hPort, data.c_str(), (DWORD)data.length(), 5000, dwWrite);
  223. if (!ret || dwWrite != (DWORD)data.length())
  224. {
  225. LOG_INFO("WriteWithTimeout Failed or partial write!, hPort:" << hPort << ", dwWrite:" << dwWrite);
  226. bSuccess = false;
  227. break;
  228. }
  229. }
  230. }
  231. else
  232. {
  233. //网口走这里
  234. try
  235. {
  236. boost::asio::write(m_socket, boost::asio::buffer(data.c_str(), data.length()));
  237. bSuccess = true;
  238. }
  239. catch (const std::exception& e)
  240. {
  241. LOG_INFO("网口打印机发送消息失败,错误信息:" << e.what());
  242. bSuccess = false;
  243. }
  244. }
  245. //关闭设备
  246. for (std::vector<HANDLE>::iterator it = m_hShouyinPorts.begin(); it != m_hShouyinPorts.end(); it++)
  247. {
  248. CloseHandle(*it);
  249. }
  250. //删除之前旧的句柄
  251. m_hShouyinPorts.clear();
  252. if (nType == 2)
  253. {
  254. m_socket.close();
  255. }
  256. return bSuccess;
  257. }
  258. bool CPosPrinterWorker::SendDataToBiaoqianPirnter(std::string data)
  259. {
  260. bool bSuccess = false;
  261. //默认是句柄模式
  262. int nType = 1;
  263. //用于厨房打印机和网口类型的收银小票打印
  264. boost::asio::io_context m_io;
  265. boost::asio::ip::tcp::socket m_socket(m_io);
  266. std::string printer_leixing = CSetting::GetInstance()->GetParam("setting_biaoqian_printer_leixing");
  267. if (printer_leixing == "usb")
  268. {
  269. std::string printer_usb = CSetting::GetInstance()->GetParam("setting_biaoqian_printer_usb");
  270. std::wstring ws_printer_usb = CLewaimaiString::UTF8ToUnicode(printer_usb);
  271. //连接usb端口
  272. bool ret = InitOneUsb(ws_printer_usb, 2);
  273. if (ret == false)
  274. {
  275. return false;
  276. }
  277. }
  278. else if (printer_leixing == "wangkou")
  279. {
  280. //改为网口模式
  281. nType = 2;
  282. std::string wangkou_ip = CSetting::GetInstance()->GetParam("setting_biaoqian_printer_wangkou_ip");
  283. //初始化连接
  284. try
  285. {
  286. boost::asio::ip::tcp::endpoint ep(boost::asio::ip::make_address(wangkou_ip.c_str()), 9100);
  287. m_socket.connect(ep);
  288. }
  289. catch (std::exception& e)
  290. {
  291. std::string err = e.what();
  292. LOG_INFO("网口打印机连接失败,IP地址:" << wangkou_ip.c_str() << ",错误信息:" << err.c_str());
  293. MessageBoxW(NULL, (L"标签网口打印机连接失败,IP地址:" + CLewaimaiString::UTF8ToUnicode(wangkou_ip)).c_str(), L"打印机连接失败", MB_OK);
  294. return false;
  295. }
  296. }
  297. //开始发送数据
  298. if (nType == 1)
  299. {
  300. //USB、串口、并口,走这里
  301. for (std::vector<HANDLE>::iterator it = m_hBiaoqianPorts.begin(); it != m_hBiaoqianPorts.end(); it++)
  302. {
  303. HANDLE hPort = *it;
  304. //同步写数据
  305. DWORD dwWrite = 0;
  306. bool ret = WriteWithTimeout(hPort, data.c_str(), (DWORD)data.length(), 5000, dwWrite);
  307. if (!ret || dwWrite != (DWORD)data.length())
  308. {
  309. LOG_INFO("WriteWithTimeout Failed or partial write!, hPort:" << hPort << ", dwWrite:" << dwWrite);
  310. bSuccess = false;
  311. break;
  312. }
  313. }
  314. }
  315. else
  316. {
  317. //网口走这里
  318. try
  319. {
  320. boost::asio::write(m_socket, boost::asio::buffer(data.c_str(), data.length()));
  321. bSuccess = true;
  322. }
  323. catch (const std::exception& e)
  324. {
  325. LOG_INFO("网口打印机发送消息失败,错误信息:" << e.what());
  326. bSuccess = false;
  327. }
  328. }
  329. //关闭设备
  330. for (std::vector<HANDLE>::iterator it = m_hBiaoqianPorts.begin(); it != m_hBiaoqianPorts.end(); it++)
  331. {
  332. CloseHandle(*it);
  333. }
  334. m_hBiaoqianPorts.clear();
  335. if (nType == 2)
  336. {
  337. m_socket.close();
  338. }
  339. return bSuccess;
  340. }
  341. bool CPosPrinterWorker::SendDataToChufangPirnter(ChufangPrinterContent chufang_data)
  342. {
  343. bool bSuccess = false;
  344. boost::asio::io_context m_io;
  345. boost::asio::ip::tcp::socket m_socket(m_io);
  346. //读取厨房打印机信息
  347. std::vector<ChufangPrinter> total_printers = CSetting::GetInstance()->getChufangPrints();
  348. std::string ip = chufang_data.ip;
  349. std::string data = chufang_data.data;
  350. //初始化连接
  351. try
  352. {
  353. boost::asio::ip::tcp::endpoint ep(boost::asio::ip::make_address(ip.c_str()), 9100);
  354. m_socket.connect(ep);
  355. }
  356. catch (std::exception& e)
  357. {
  358. std::string err = e.what();
  359. LOG_INFO("厨房网口打印机连接失败,IP地址:" << ip.c_str() << ",错误信息:" << err.c_str());
  360. MessageBoxW(NULL, (L"厨房网口打印机连接失败,IP地址:" + CLewaimaiString::UTF8ToUnicode(ip)).c_str(), L"打印机连接失败", MB_OK);
  361. //连接失败了,处理下一个厨房打印机
  362. return false;
  363. }
  364. //网口走这里
  365. try
  366. {
  367. boost::asio::write(m_socket, boost::asio::buffer(data.c_str(), data.length()));
  368. bSuccess = true;
  369. }
  370. catch (const std::exception& e)
  371. {
  372. LOG_INFO("网口打印机发送消息失败,错误信息:" << e.what());
  373. bSuccess = false;
  374. }
  375. m_socket.close();
  376. return bSuccess;
  377. }
  378. //自动识别用于收银小票的USB打印机列表
  379. void CPosPrinterWorker::InitUsb()
  380. {
  381. //设置中文字符
  382. setlocale(LC_CTYPE, "chs");
  383. //取设备路径
  384. m_usb_devices.clear();
  385. int nDevice = GetDevicePath((LPGUID)&USB_GUID, m_usb_devices);
  386. LOG_INFO("可用的USB打印机数量:" << nDevice);
  387. //添加usb端口
  388. int i = 0;
  389. while (i < nDevice)
  390. {
  391. std::string setting_biaoqian_printer_usb = CSetting::GetInstance()->GetParam("setting_biaoqian_printer_usb");
  392. std::wstring ws_setting_biaoqian_printer_usb = CLewaimaiString::UTF8ToUnicode(setting_biaoqian_printer_usb);
  393. if (CSetting::GetInstance()->GetParam("setting_is_new_waimai_biaoqian_printer") == "1" && ws_setting_biaoqian_printer_usb == m_usb_devices[i])
  394. {
  395. //碰到了一个在设置中心明确设置的标签打印机,过滤掉
  396. i++;
  397. continue;
  398. }
  399. LOG_INFO("准备打开端口 Port = " << m_usb_devices[i].c_str());
  400. HANDLE hPort = CreateFile(m_usb_devices[i].c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
  401. if (hPort == INVALID_HANDLE_VALUE)
  402. {
  403. LOG_INFO("打开USB端口失败, Port = " << m_usb_devices[i].c_str());
  404. // 打开端口失败
  405. DWORD error = GetLastError();
  406. if (error == 2)
  407. {
  408. //没有指定的文件
  409. LOG_INFO("没有找对对应的usb端口");
  410. }
  411. else if (error == 5)
  412. {
  413. LOG_INFO("usb端口被占用!");
  414. }
  415. i++;
  416. continue;
  417. }
  418. LOG_INFO("打开usb端口成功,准备进行打印机检测! hPort:" << hPort);
  419. m_hShouyinPorts.push_back(hPort);
  420. i++;
  421. }
  422. //清空usb集合,下一次调用这个函数的时候会重新设置
  423. m_usb_devices.clear();
  424. }
  425. bool CPosPrinterWorker::InitOneUsb(std::wstring usb_path, int nType)
  426. {
  427. if (usb_path == L"")
  428. {
  429. return false;
  430. }
  431. HANDLE hPort = CreateFile(usb_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
  432. if (hPort == INVALID_HANDLE_VALUE)
  433. {
  434. LOG_INFO("打开USB端口失败, Port = " << usb_path.c_str());
  435. // 打开端口失败
  436. DWORD error = GetLastError();
  437. if (error == 2)
  438. {
  439. //没有指定的文件
  440. LOG_INFO("没有找对对应的usb端口");
  441. }
  442. else if (error == 5)
  443. {
  444. LOG_INFO("usb端口被占用!");
  445. }
  446. return false;
  447. }
  448. LOG_INFO("打开usb端口,准备进行打印机检测! hPort:" << hPort);
  449. if (nType == 1)
  450. {
  451. m_hShouyinPorts.push_back(hPort);
  452. }
  453. else
  454. {
  455. m_hBiaoqianPorts.push_back(hPort);
  456. }
  457. return true;
  458. }
  459. bool CPosPrinterWorker::InitBingkou()
  460. {
  461. std::wstring LptStr = L"lpt1";
  462. HANDLE hPort = CreateFile(LptStr.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
  463. if (hPort == INVALID_HANDLE_VALUE)
  464. {
  465. LOG_INFO("打开并口失败, Port = " << LptStr.c_str());
  466. DWORD error = GetLastError();
  467. if (error == 2)
  468. {
  469. //没有指定的文件
  470. LOG_INFO("本机器没有并口!");
  471. }
  472. else if (error == 5)
  473. {
  474. LOG_INFO("并口被占用!");
  475. }
  476. return false;
  477. }
  478. else
  479. {
  480. //这个表示并口可以使用
  481. LOG_INFO("找到并口,准备进行打印机检测! hPort:" << hPort);
  482. m_hShouyinPorts.push_back(hPort);
  483. }
  484. return true;
  485. }
  486. void CPosPrinterWorker::InitCom()
  487. {
  488. CComHelper helper;
  489. std::vector<std::wstring> comVector = helper.getComPort();
  490. for (std::vector<std::wstring>::iterator it = comVector.begin(); it != comVector.end(); it++)
  491. {
  492. std::wstring com2Str = *it;
  493. HANDLE hPort = CreateFile(com2Str.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
  494. if (hPort == INVALID_HANDLE_VALUE)
  495. {
  496. DWORD error = GetLastError();
  497. if (error == 2)
  498. {
  499. //没有指定的文件
  500. LOG_INFO("没有找到对应的串口 " << com2Str.c_str());
  501. }
  502. else if (error == 5)
  503. {
  504. LOG_INFO("串口被占用 " << com2Str.c_str());
  505. }
  506. }
  507. else
  508. {
  509. LOG_INFO(com2Str.c_str() << " success, handle:" << hPort);
  510. //扎到了可用的串口,设置串口波特率
  511. DCB dcb;
  512. dcb.DCBlength = sizeof(dcb);
  513. GetCommState(hPort, &dcb);
  514. //佳博的串口打印机,默认是这个波特率
  515. dcb.BaudRate = 19200;
  516. if (!SetCommState(hPort, &dcb))
  517. {
  518. LOG_INFO("set baudRate failed!");
  519. CloseHandle(hPort);
  520. continue;
  521. }
  522. //设定通讯端口超时参数
  523. COMMTIMEOUTS tmouts;
  524. tmouts.ReadIntervalTimeout = 100;
  525. tmouts.ReadTotalTimeoutMultiplier = 100;
  526. tmouts.ReadTotalTimeoutConstant = 100;
  527. tmouts.WriteTotalTimeoutConstant = 100;
  528. tmouts.WriteTotalTimeoutMultiplier = 100;
  529. SetCommTimeouts(hPort, &tmouts);
  530. //设置端口缓冲
  531. SetupComm(hPort, 1024, 1024);
  532. //清除通讯端口缓存
  533. PurgeComm(hPort, PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_RXABORT);
  534. m_hShouyinPorts.push_back(hPort);
  535. }
  536. }
  537. }
  538. /*
  539. *获取CreateFile的USB端口号
  540. **/
  541. int CPosPrinterWorker::GetDevicePath(LPGUID lpGuid, std::vector<std::wstring>& usb_devices)
  542. {
  543. // 返回找到的设备数量
  544. int nCount = 0;
  545. // GUID 相关设备信息集句柄
  546. HDEVINFO hDevInfoSet = ::SetupDiGetClassDevs(lpGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
  547. if (hDevInfoSet == INVALID_HANDLE_VALUE)
  548. {
  549. LOG_INFO("SetupDiGetClassDevs failed");
  550. return 0;
  551. }
  552. // 为接口细节分配缓冲
  553. PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)::GlobalAlloc(LMEM_ZEROINIT, INTERFACE_DETAIL_SIZE);
  554. if (pDetail == NULL)
  555. {
  556. LOG_INFO("GlobalAlloc pDetail failed");
  557. ::SetupDiDestroyDeviceInfoList(hDevInfoSet);
  558. return 0;
  559. }
  560. pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  561. // 用于枚举设备信息
  562. SP_DEVINFO_DATA spDevInfoData = {};
  563. SP_DEVICE_INTERFACE_DATA ifData = {};
  564. spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  565. ifData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  566. // 用于识别通用 USB 打印类的字符串(中文/英文)
  567. std::wstring strUSBPrint = L"USB 打印支持";
  568. std::wstring strUSBPrint_EN = L"USB Printing Support";
  569. // 枚举设备信息条目(按索引)
  570. for (DWORD idx = 0; ; ++idx)
  571. {
  572. // 枚举设备信息项
  573. BOOL ok = ::SetupDiEnumDeviceInfo(hDevInfoSet, idx, &spDevInfoData);
  574. if (!ok)
  575. {
  576. // 结束或出错(当没有更多项时返回 FALSE)
  577. break;
  578. }
  579. // 先尝试从注册表获取 FriendlyName / DeviceDesc 作为回退名字
  580. TCHAR regBuf[MAX_PATH] = { 0 };
  581. DWORD propType = 0;
  582. DWORD requiredSize = 0;
  583. std::wstring regName;
  584. if (SetupDiGetDeviceRegistryProperty(hDevInfoSet, &spDevInfoData, SPDRP_FRIENDLYNAME, &propType,
  585. (PBYTE)regBuf, sizeof(regBuf), &requiredSize))
  586. {
  587. regName = regBuf;
  588. }
  589. else if (SetupDiGetDeviceRegistryProperty(hDevInfoSet, &spDevInfoData, SPDRP_DEVICEDESC, &propType,
  590. (PBYTE)regBuf, sizeof(regBuf), &requiredSize))
  591. {
  592. regName = regBuf;
  593. }
  594. else
  595. {
  596. regName = L"Unknown";
  597. }
  598. // 尝试读取总线报告的设备描述(通常为 USB Product string)
  599. std::wstring busReported;
  600. {
  601. DEVPROPTYPE devPropType = 0;
  602. DWORD req = 0;
  603. WCHAR smallBuf[256] = { 0 };
  604. BOOL got = FALSE;
  605. // 使用本地定义的 DEVPROPKEY:PKEY_Device_BusReportedDeviceDesc_local(需在文件顶部定义)
  606. if (SetupDiGetDevicePropertyW(hDevInfoSet, &spDevInfoData, &PKEY_Device_BusReportedDeviceDesc_local, &devPropType,
  607. reinterpret_cast<PBYTE>(smallBuf), sizeof(smallBuf), &req, 0))
  608. {
  609. busReported = smallBuf;
  610. got = TRUE;
  611. }
  612. else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && req > 0)
  613. {
  614. std::vector<BYTE> bigBuf(req);
  615. if (SetupDiGetDevicePropertyW(hDevInfoSet, &spDevInfoData, &PKEY_Device_BusReportedDeviceDesc_local, &devPropType,
  616. bigBuf.data(), req, &req, 0))
  617. {
  618. // bigBuf 存储为 WCHAR 字符串
  619. busReported.assign(reinterpret_cast<WCHAR*>(bigBuf.data()));
  620. got = TRUE;
  621. }
  622. }
  623. if (got)
  624. {
  625. LOG_INFO(L"BusReportedDeviceDesc: " << busReported);
  626. }
  627. }
  628. // 如果既不是通用打印类也没有 busReported 描述,跳过该设备
  629. if (regName != strUSBPrint && regName != strUSBPrint_EN && busReported.empty())
  630. {
  631. LOG_INFO(L"not good usb, regName:" << regName);
  632. continue;
  633. }
  634. // 枚举该设备的信息接口以取得 DevicePath
  635. ifData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  636. if (!::SetupDiEnumDeviceInterfaces(hDevInfoSet, NULL, lpGuid, idx, &ifData))
  637. {
  638. LOG_INFO("SetupDiEnumDeviceInterfaces failed for idx=" << idx);
  639. continue;
  640. }
  641. // 取得接口细节(包含 DevicePath)
  642. if (!SetupDiGetInterfaceDeviceDetail(hDevInfoSet, &ifData, pDetail, INTERFACE_DETAIL_SIZE, NULL, NULL))
  643. {
  644. LOG_INFO("SetupDiGetInterfaceDeviceDetail failed for idx=" << idx);
  645. continue;
  646. }
  647. std::wstring DevicePath = pDetail->DevicePath;
  648. // 从 DevicePath 中提取 vid/pid 以便后续判断或使用 WinUSB
  649. std::wstring vid = L"";
  650. std::wstring pid = L"";
  651. size_t posVid = DevicePath.find(L"vid_");
  652. size_t posPid = DevicePath.find(L"pid_");
  653. if (posVid != std::wstring::npos && posVid + 4 + 4 <= DevicePath.length()) vid = DevicePath.substr(posVid + 4, 4);
  654. if (posPid != std::wstring::npos && posPid + 4 + 4 <= DevicePath.length()) pid = DevicePath.substr(posPid + 4, 4);
  655. LOG_INFO("Vid:" << vid.c_str() << ", Pid:" << pid.c_str());
  656. // 输出检测到的名称(优先 busReported,其次注册表名)
  657. if (!busReported.empty())
  658. {
  659. LOG_INFO(L"Detected printer name: " << busReported);
  660. }
  661. else
  662. {
  663. LOG_INFO(L"Detected registry name: " << regName);
  664. }
  665. // 将 DevicePath 添加到结果集合
  666. usb_devices.push_back(DevicePath);
  667. ++nCount;
  668. LOG_INFO("Cnt = " << nCount << ", pDetail->DevicePath =" << pDetail->DevicePath);
  669. }
  670. // 清理并返回
  671. ::GlobalFree(pDetail);
  672. ::SetupDiDestroyDeviceInfoList(hDevInfoSet);
  673. return nCount;
  674. }
  675. std::vector<std::wstring> CPosPrinterWorker::getUsbDevices()
  676. {
  677. std::vector<std::wstring> usb_devices;
  678. int nDevice = GetDevicePath((LPGUID)&USB_GUID, usb_devices);
  679. return usb_devices;
  680. }
  681. bool CPosPrinterWorker::WriteWithTimeout(HANDLE hPort, const void* buffer, DWORD bufferSize, DWORD timeoutMs, DWORD& dwWrite)
  682. {
  683. dwWrite = 0;
  684. OVERLAPPED ov = {};
  685. ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  686. if (ov.hEvent == NULL)
  687. {
  688. return false;
  689. }
  690. BOOL ret = WriteFile(hPort, buffer, bufferSize, NULL, &ov);
  691. if (!ret)
  692. {
  693. DWORD err = GetLastError();
  694. if (err == ERROR_IO_PENDING)
  695. {
  696. DWORD waitRet = WaitForSingleObject(ov.hEvent, timeoutMs);
  697. if (waitRet == WAIT_OBJECT_0)
  698. {
  699. ret = GetOverlappedResult(hPort, &ov, &dwWrite, FALSE);
  700. }
  701. else
  702. {
  703. CancelIoEx(hPort, &ov);
  704. ret = FALSE;
  705. }
  706. }
  707. else
  708. {
  709. ret = FALSE;
  710. }
  711. }
  712. else
  713. {
  714. ret = GetOverlappedResult(hPort, &ov, &dwWrite, TRUE);
  715. }
  716. CloseHandle(ov.hEvent);
  717. return ret == TRUE;
  718. }
  719. bool CPosPrinterWorker::ReadWithTimeout(HANDLE hPort, void* buffer, DWORD bufferSize, DWORD timeoutMs, DWORD& dwRead)
  720. {
  721. dwRead = 0;
  722. OVERLAPPED ov = {};
  723. ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  724. if (ov.hEvent == NULL)
  725. {
  726. return false;
  727. }
  728. BOOL ret = ReadFile(hPort, buffer, bufferSize, NULL, &ov);
  729. if (!ret)
  730. {
  731. DWORD err = GetLastError();
  732. if (err == ERROR_IO_PENDING)
  733. {
  734. DWORD waitRet = WaitForSingleObject(ov.hEvent, timeoutMs);
  735. if (waitRet == WAIT_OBJECT_0)
  736. {
  737. ret = GetOverlappedResult(hPort, &ov, &dwRead, FALSE);
  738. }
  739. else
  740. {
  741. CancelIoEx(hPort, &ov);
  742. ret = FALSE;
  743. }
  744. }
  745. else
  746. {
  747. ret = FALSE;
  748. }
  749. }
  750. else
  751. {
  752. ret = GetOverlappedResult(hPort, &ov, &dwRead, TRUE);
  753. }
  754. CloseHandle(ov.hEvent);
  755. return ret == TRUE;
  756. }