|
@@ -3,13 +3,255 @@
|
|
|
|
|
|
|
|
#include "../helper/CComHelper.h"
|
|
#include "../helper/CComHelper.h"
|
|
|
|
|
|
|
|
-namespace
|
|
|
|
|
|
|
+// 在文件顶部(现有 include 之后)添加
|
|
|
|
|
+#include <devpkey.h>
|
|
|
|
|
+#include <devpropdef.h>
|
|
|
|
|
+
|
|
|
|
|
+#pragma comment(lib, "setupapi.lib")
|
|
|
|
|
+#pragma comment(lib, "winspool.lib")
|
|
|
|
|
+
|
|
|
|
|
+// 本地定义 DEVPROPKEY,等价于 DEVPKEY_Device_BusReportedDeviceDesc (fmtid + pid=4)
|
|
|
|
|
+const DEVPROPKEY PKEY_Device_BusReportedDeviceDesc_local =
|
|
|
|
|
+{
|
|
|
|
|
+ { 0x540b947e, 0x8b40, 0x45bc, { 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2 } },
|
|
|
|
|
+ 4
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// Helper: case-insensitive find
|
|
|
|
|
+static bool ci_find_substr(const std::wstring& hay, const std::wstring& needle)
|
|
|
{
|
|
{
|
|
|
- // ESC/POS:DLE EOT 4,查询纸状态
|
|
|
|
|
- const unsigned char ESC_POS_QUERY_PAPER_STATUS[] = { 0x10, 0x04, 0x04 };
|
|
|
|
|
|
|
+ if (needle.empty()) return false;
|
|
|
|
|
+ std::wstring h = hay, n = needle;
|
|
|
|
|
+ for (auto& c : h) c = towlower(c);
|
|
|
|
|
+ for (auto& c : n) c = towlower(c);
|
|
|
|
|
+ return h.find(n) != std::wstring::npos;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 读取系统已安装打印机(PRINTER_INFO_2),返回 vector of printer names and port names
|
|
|
|
|
+static std::vector<std::pair<std::wstring, std::wstring>> EnumerateInstalledPrinters()
|
|
|
|
|
+{
|
|
|
|
|
+ std::vector<std::pair<std::wstring, std::wstring>> res;
|
|
|
|
|
+
|
|
|
|
|
+ DWORD needed = 0, returned = 0;
|
|
|
|
|
+ EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, NULL, 0, &needed, &returned);
|
|
|
|
|
+ if (needed == 0) return res;
|
|
|
|
|
+
|
|
|
|
|
+ std::vector<BYTE> buffer(needed);
|
|
|
|
|
+ if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, buffer.data(), needed, &needed, &returned))
|
|
|
|
|
+ {
|
|
|
|
|
+ LOG_INFO("EnumerateInstalledPrinters: EnumPrinters failed err=" << GetLastError());
|
|
|
|
|
+ return res;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ PRINTER_INFO_2W* p = reinterpret_cast<PRINTER_INFO_2W*>(buffer.data());
|
|
|
|
|
+ for (DWORD i = 0; i < returned; ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ std::wstring name = p[i].pPrinterName ? p[i].pPrinterName : L"";
|
|
|
|
|
+ std::wstring port = p[i].pPortName ? p[i].pPortName : L"";
|
|
|
|
|
+ res.emplace_back(name, port);
|
|
|
|
|
+ LOG_INFO(L"InstalledPrinter: Name=" << name << L", Port=" << port);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return res;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 从 DevicePath 提取 vid/pid
|
|
|
|
|
+static void ExtractVidPidFromDevicePath(const std::wstring& devicePath, std::wstring& outVid, std::wstring& outPid)
|
|
|
|
|
+{
|
|
|
|
|
+ outVid.clear(); outPid.clear();
|
|
|
|
|
+ size_t posVid = devicePath.find(L"vid_");
|
|
|
|
|
+ size_t posPid = devicePath.find(L"pid_");
|
|
|
|
|
+ if (posVid != std::wstring::npos && posVid + 4 + 4 <= devicePath.length())
|
|
|
|
|
+ outVid = devicePath.substr(posVid + 4, 4);
|
|
|
|
|
+ if (posPid != std::wstring::npos && posPid + 4 + 4 <= devicePath.length())
|
|
|
|
|
+ outPid = devicePath.substr(posPid + 4, 4);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 给定 DevicePath,尝试读取该设备的 BusReportedDeviceDesc、FriendlyName/DeviceDesc、Location 信息等
|
|
|
|
|
+static void QueryUsbDeviceDetailsByPath(const std::wstring& targetDevicePath,
|
|
|
|
|
+ std::wstring& outBusReported, std::wstring& outRegName, std::wstring& outLocation)
|
|
|
|
|
+{
|
|
|
|
|
+ outBusReported.clear();
|
|
|
|
|
+ outRegName.clear();
|
|
|
|
|
+ outLocation.clear();
|
|
|
|
|
+
|
|
|
|
|
+ // 枚举 GUID 为 USB GUID 的设备并查找与 targetDevicePath 匹配的那个
|
|
|
|
|
+ HDEVINFO hDevInfoSet = ::SetupDiGetClassDevs((LPGUID)&USB_GUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
|
|
|
|
+ if (hDevInfoSet == INVALID_HANDLE_VALUE) return;
|
|
|
|
|
+
|
|
|
|
|
+ PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)::GlobalAlloc(LMEM_ZEROINIT, INTERFACE_DETAIL_SIZE);
|
|
|
|
|
+ if (!pDetail)
|
|
|
|
|
+ {
|
|
|
|
|
+ SetupDiDestroyDeviceInfoList(hDevInfoSet);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
|
|
|
+
|
|
|
|
|
+ SP_DEVINFO_DATA devInfo = {};
|
|
|
|
|
+ SP_DEVICE_INTERFACE_DATA ifData = {};
|
|
|
|
|
+ devInfo.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
|
|
|
+ ifData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
|
|
|
|
+
|
|
|
|
|
+ for (DWORD idx = 0; ; ++idx)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!SetupDiEnumDeviceInfo(hDevInfoSet, idx, &devInfo)) break;
|
|
|
|
|
|
|
|
- // 常见 Epson 兼容定义:bit6=1 表示缺纸
|
|
|
|
|
- const unsigned char ESC_POS_PAPER_OUT_MASK = 0x40;
|
|
|
|
|
|
|
+ ifData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
|
|
|
|
+ if (!SetupDiEnumDeviceInterfaces(hDevInfoSet, NULL, (LPGUID)&USB_GUID, idx, &ifData))
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ if (!SetupDiGetInterfaceDeviceDetail(hDevInfoSet, &ifData, pDetail, INTERFACE_DETAIL_SIZE, NULL, &devInfo))
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ std::wstring devPath = pDetail->DevicePath;
|
|
|
|
|
+ if (devPath != targetDevicePath) continue; // 未匹配则继续
|
|
|
|
|
+
|
|
|
|
|
+ // 找到目标设备,读取注册表描述
|
|
|
|
|
+ DWORD propType = 0, req = 0;
|
|
|
|
|
+ WCHAR buf[MAX_PATH] = { 0 };
|
|
|
|
|
+ if (SetupDiGetDeviceRegistryProperty(hDevInfoSet, &devInfo, SPDRP_FRIENDLYNAME, &propType, (PBYTE)buf, sizeof(buf), &req))
|
|
|
|
|
+ {
|
|
|
|
|
+ outRegName = buf;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (SetupDiGetDeviceRegistryProperty(hDevInfoSet, &devInfo, SPDRP_DEVICEDESC, &propType, (PBYTE)buf, sizeof(buf), &req))
|
|
|
|
|
+ {
|
|
|
|
|
+ outRegName = buf;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试读取 BusReportedDeviceDesc(Product string)
|
|
|
|
|
+ {
|
|
|
|
|
+ DEVPROPTYPE devPropType = 0;
|
|
|
|
|
+ DWORD required = 0;
|
|
|
|
|
+ WCHAR smallBuf[256] = { 0 };
|
|
|
|
|
+ if (SetupDiGetDevicePropertyW(hDevInfoSet, &devInfo, &PKEY_Device_BusReportedDeviceDesc_local, &devPropType,
|
|
|
|
|
+ reinterpret_cast<PBYTE>(smallBuf), sizeof(smallBuf), &required, 0))
|
|
|
|
|
+ {
|
|
|
|
|
+ outBusReported = smallBuf;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && required > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ std::vector<BYTE> bigBuf(required);
|
|
|
|
|
+ if (SetupDiGetDevicePropertyW(hDevInfoSet, &devInfo, &PKEY_Device_BusReportedDeviceDesc_local, &devPropType,
|
|
|
|
|
+ bigBuf.data(), required, &required, 0))
|
|
|
|
|
+ {
|
|
|
|
|
+ outBusReported.assign(reinterpret_cast<WCHAR*>(bigBuf.data()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 读取位置/端口信息(LocationInformation)
|
|
|
|
|
+ {
|
|
|
|
|
+ DWORD required = 0;
|
|
|
|
|
+ WCHAR locBuf[256] = { 0 };
|
|
|
|
|
+ if (SetupDiGetDeviceRegistryProperty(hDevInfoSet, &devInfo, SPDRP_LOCATION_INFORMATION, &propType, (PBYTE)locBuf, sizeof(locBuf), &required))
|
|
|
|
|
+ {
|
|
|
|
|
+ outLocation = locBuf;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ break; // 找到并读取后退出
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ::GlobalFree(pDetail);
|
|
|
|
|
+ ::SetupDiDestroyDeviceInfoList(hDevInfoSet);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 主映射函数实现
|
|
|
|
|
+std::vector<UsbPrinterMapping> CPosPrinterWorker::MapUsbDevicesToInstalledPrinters()
|
|
|
|
|
+{
|
|
|
|
|
+ std::vector<UsbPrinterMapping> result;
|
|
|
|
|
+
|
|
|
|
|
+ // 1) 枚举已安装的打印机(名字 + 端口),用于后续匹配
|
|
|
|
|
+ auto printers = EnumerateInstalledPrinters();
|
|
|
|
|
+
|
|
|
|
|
+ // 2) 取得系统检测到的 USB 设备 DevicePath 列表(使用现有接口)
|
|
|
|
|
+ std::vector<std::wstring> usbDevices = CPosPrinterWorker::GetInstance()->getUsbDevices();
|
|
|
|
|
+
|
|
|
|
|
+ LOG_INFO("MapUsbDevicesToInstalledPrinters: usbDevices count=" << usbDevices.size());
|
|
|
|
|
+
|
|
|
|
|
+ // 3) 对每个 DevicePath 查询更多信息并尝试匹配
|
|
|
|
|
+ for (const auto& dp : usbDevices)
|
|
|
|
|
+ {
|
|
|
|
|
+ UsbPrinterMapping item;
|
|
|
|
|
+ item.devicePath = dp;
|
|
|
|
|
+ ExtractVidPidFromDevicePath(dp, item.vid, item.pid);
|
|
|
|
|
+
|
|
|
|
|
+ // 读取 BusReported, regName, location
|
|
|
|
|
+ QueryUsbDeviceDetailsByPath(dp, item.busReportedName, item.regName, item.vid /* reuse var not needed */);
|
|
|
|
|
+ // QueryUsbDeviceDetailsByPath fills busReportedName and regName (location ignored here)
|
|
|
|
|
+
|
|
|
|
|
+ LOG_INFO(L"DevicePath=" << dp);
|
|
|
|
|
+ LOG_INFO(L" busReported=" << item.busReportedName << L", regName=" << item.regName << L", vid=" << item.vid << L", pid=" << item.pid);
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试多种匹配策略(逐一尝试)
|
|
|
|
|
+ for (const auto& pr : printers)
|
|
|
|
|
+ {
|
|
|
|
|
+ const std::wstring& printerName = pr.first;
|
|
|
|
|
+ const std::wstring& portName = pr.second;
|
|
|
|
|
+
|
|
|
|
|
+ bool matched = false;
|
|
|
|
|
+
|
|
|
|
|
+ // 1) 如果 busReported 在打印机名称中(不区分大小写)
|
|
|
|
|
+ if (!item.busReportedName.empty() && ci_find_substr(printerName, item.busReportedName))
|
|
|
|
|
+ {
|
|
|
|
|
+ matched = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2) 如果注册表名称在打印机名称中
|
|
|
|
|
+ if (!matched && !item.regName.empty() && ci_find_substr(printerName, item.regName))
|
|
|
|
|
+ {
|
|
|
|
|
+ matched = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3) 如果打印机端口名包含 VID 或 PID(有些端口字符串会包含硬件id或序号)
|
|
|
|
|
+ if (!matched && !item.vid.empty() && ci_find_substr(portName, item.vid))
|
|
|
|
|
+ {
|
|
|
|
|
+ matched = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!matched && !item.pid.empty() && ci_find_substr(portName, item.pid))
|
|
|
|
|
+ {
|
|
|
|
|
+ matched = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 4) 如果端口名本身等于 USB001/USB002 且注册表 location 包含相同信息,则匹配(二者需要更多自定义逻辑)
|
|
|
|
|
+ // 这里把端口名直接当作可能匹配项的一部分:若端口名出现在 regName/location 中则认为是匹配
|
|
|
|
|
+ if (!matched && !portName.empty() && (!item.regName.empty() && ci_find_substr(item.regName, portName) || !item.busReportedName.empty() && ci_find_substr(item.busReportedName, portName)))
|
|
|
|
|
+ {
|
|
|
|
|
+ matched = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (matched)
|
|
|
|
|
+ {
|
|
|
|
|
+ item.matchedPrinterNames.push_back(printerName);
|
|
|
|
|
+ LOG_INFO(L" Matched printer: " << printerName << L" (port=" << portName << L")");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ result.push_back(item);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 输出没有被匹配到的打印机(便于调试)
|
|
|
|
|
+ for (const auto& pr : printers)
|
|
|
|
|
+ {
|
|
|
|
|
+ bool used = false;
|
|
|
|
|
+ for (const auto& it : result)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (const auto& m : it.matchedPrinterNames)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (m == pr.first)
|
|
|
|
|
+ {
|
|
|
|
|
+ used = true; break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (used) break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!used)
|
|
|
|
|
+ {
|
|
|
|
|
+ LOG_INFO(L"Installed printer not matched to any USB device: " << pr.first << L", port=" << pr.second);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
CPosPrinterWorker::CPosPrinterWorker()
|
|
CPosPrinterWorker::CPosPrinterWorker()
|
|
@@ -253,12 +495,6 @@ bool CPosPrinterWorker::SendDataToShouyinPirnter(std::string data)
|
|
|
{
|
|
{
|
|
|
HANDLE hPort = *it;
|
|
HANDLE hPort = *it;
|
|
|
|
|
|
|
|
- if (!WaitPrinterReady(hPort))
|
|
|
|
|
- {
|
|
|
|
|
- bSuccess = false;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
//同步写数据
|
|
//同步写数据
|
|
|
DWORD dwWrite = 0;
|
|
DWORD dwWrite = 0;
|
|
|
bool ret = WriteWithTimeout(hPort, data.c_str(), (DWORD)data.length(), 5000, dwWrite);
|
|
bool ret = WriteWithTimeout(hPort, data.c_str(), (DWORD)data.length(), 5000, dwWrite);
|
|
@@ -272,12 +508,6 @@ bool CPosPrinterWorker::SendDataToShouyinPirnter(std::string data)
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- if (!WaitPrinterReady(m_socket))
|
|
|
|
|
- {
|
|
|
|
|
- m_socket.close();
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
//网口走这里
|
|
//网口走这里
|
|
|
try
|
|
try
|
|
|
{
|
|
{
|
|
@@ -363,12 +593,6 @@ bool CPosPrinterWorker::SendDataToBiaoqianPirnter(std::string data)
|
|
|
for (std::vector<HANDLE>::iterator it = m_hBiaoqianPorts.begin(); it != m_hBiaoqianPorts.end(); it++)
|
|
for (std::vector<HANDLE>::iterator it = m_hBiaoqianPorts.begin(); it != m_hBiaoqianPorts.end(); it++)
|
|
|
{
|
|
{
|
|
|
HANDLE hPort = *it;
|
|
HANDLE hPort = *it;
|
|
|
-
|
|
|
|
|
- if (!WaitPrinterReady(hPort))
|
|
|
|
|
- {
|
|
|
|
|
- bSuccess = false;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
//同步写数据
|
|
//同步写数据
|
|
|
DWORD dwWrite = 0;
|
|
DWORD dwWrite = 0;
|
|
@@ -383,12 +607,6 @@ bool CPosPrinterWorker::SendDataToBiaoqianPirnter(std::string data)
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- if (!WaitPrinterReady(m_socket))
|
|
|
|
|
- {
|
|
|
|
|
- m_socket.close();
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
//网口走这里
|
|
//网口走这里
|
|
|
try
|
|
try
|
|
|
{
|
|
{
|
|
@@ -448,12 +666,6 @@ bool CPosPrinterWorker::SendDataToChufangPirnter(ChufangPrinterContent chufang_d
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (!WaitPrinterReady(m_socket))
|
|
|
|
|
- {
|
|
|
|
|
- m_socket.close();
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
//网口走这里
|
|
//网口走这里
|
|
|
try
|
|
try
|
|
|
{
|
|
{
|
|
@@ -681,132 +893,151 @@ void CPosPrinterWorker::InitCom()
|
|
|
**/
|
|
**/
|
|
|
int CPosPrinterWorker::GetDevicePath(LPGUID lpGuid, std::vector<std::wstring>& usb_devices)
|
|
int CPosPrinterWorker::GetDevicePath(LPGUID lpGuid, std::vector<std::wstring>& usb_devices)
|
|
|
{
|
|
{
|
|
|
- HDEVINFO hDevInfoSet;
|
|
|
|
|
- SP_DEVINFO_DATA spDevInfoData;
|
|
|
|
|
- SP_DEVICE_INTERFACE_DATA ifData;
|
|
|
|
|
- PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail;
|
|
|
|
|
- int nCount;
|
|
|
|
|
- int nTotle;
|
|
|
|
|
- BOOL bResult;
|
|
|
|
|
-
|
|
|
|
|
- //这2个字符串,用于根据usb的名字对比是否为打印机设备
|
|
|
|
|
- std::wstring strUSBPrint = TEXT("USB 打印支持");
|
|
|
|
|
-
|
|
|
|
|
- //xp上是英文
|
|
|
|
|
- std::wstring strUSBPrint_EN = L"USB Printing Support";
|
|
|
|
|
-
|
|
|
|
|
- // 取得一个该GUID相关的设备信息集句柄
|
|
|
|
|
- hDevInfoSet = ::SetupDiGetClassDevs(lpGuid, // class GUID
|
|
|
|
|
- NULL, // 无关键字
|
|
|
|
|
- NULL, // 不指定父窗口句柄
|
|
|
|
|
- DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); // 目前存在的设备
|
|
|
|
|
|
|
+ // 返回找到的设备数量
|
|
|
|
|
+ int nCount = 0;
|
|
|
|
|
|
|
|
-// 失败...
|
|
|
|
|
|
|
+ // GUID 相关设备信息集句柄
|
|
|
|
|
+ HDEVINFO hDevInfoSet = ::SetupDiGetClassDevs(lpGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
|
|
if (hDevInfoSet == INVALID_HANDLE_VALUE)
|
|
if (hDevInfoSet == INVALID_HANDLE_VALUE)
|
|
|
{
|
|
{
|
|
|
- LOG_INFO("SetupDiGetClassDevs failed \r\n");
|
|
|
|
|
|
|
+ LOG_INFO("SetupDiGetClassDevs failed");
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 申请设备接口数据空间
|
|
|
|
|
- pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)::GlobalAlloc(LMEM_ZEROINIT, INTERFACE_DETAIL_SIZE);
|
|
|
|
|
|
|
+ // 为接口细节分配缓冲
|
|
|
|
|
+ PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)::GlobalAlloc(LMEM_ZEROINIT, INTERFACE_DETAIL_SIZE);
|
|
|
|
|
+ if (pDetail == NULL)
|
|
|
|
|
+ {
|
|
|
|
|
+ LOG_INFO("GlobalAlloc pDetail failed");
|
|
|
|
|
+ ::SetupDiDestroyDeviceInfoList(hDevInfoSet);
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
|
- nTotle = -1;
|
|
|
|
|
- nCount = 0;
|
|
|
|
|
- bResult = TRUE;
|
|
|
|
|
|
|
|
|
|
- // 设备序号=0,1,2... 逐一测试设备接口,到失败为止
|
|
|
|
|
- while (bResult)
|
|
|
|
|
- {
|
|
|
|
|
- nTotle++;
|
|
|
|
|
- spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
|
|
|
|
|
+ // 用于枚举设备信息
|
|
|
|
|
+ SP_DEVINFO_DATA spDevInfoData = {};
|
|
|
|
|
+ SP_DEVICE_INTERFACE_DATA ifData = {};
|
|
|
|
|
+ spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
|
|
|
+ ifData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
|
|
|
|
|
|
|
- // 枚举符合该GUID的设备接口
|
|
|
|
|
- bResult = ::SetupDiEnumDeviceInfo(
|
|
|
|
|
- hDevInfoSet, // 设备信息集句柄
|
|
|
|
|
- (ULONG)nTotle, // 设备信息集里的设备序号
|
|
|
|
|
- &spDevInfoData); // 设备接口信息
|
|
|
|
|
|
|
+ // 用于识别通用 USB 打印类的字符串(中文/英文)
|
|
|
|
|
+ std::wstring strUSBPrint = L"USB 打印支持";
|
|
|
|
|
+ std::wstring strUSBPrint_EN = L"USB Printing Support";
|
|
|
|
|
|
|
|
- if (bResult)
|
|
|
|
|
|
|
+ // 枚举设备信息条目(按索引)
|
|
|
|
|
+ for (DWORD idx = 0; ; ++idx)
|
|
|
|
|
+ {
|
|
|
|
|
+ // 枚举设备信息项
|
|
|
|
|
+ BOOL ok = ::SetupDiEnumDeviceInfo(hDevInfoSet, idx, &spDevInfoData);
|
|
|
|
|
+ if (!ok)
|
|
|
{
|
|
{
|
|
|
- DWORD DataT;
|
|
|
|
|
- TCHAR buf[MAX_PATH] = { 0 };
|
|
|
|
|
- DWORD nSize = 0;
|
|
|
|
|
|
|
+ // 结束或出错(当没有更多项时返回 FALSE)
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // get Friendly Name or Device Description
|
|
|
|
|
- if (SetupDiGetDeviceRegistryProperty(hDevInfoSet, &spDevInfoData,
|
|
|
|
|
- SPDRP_FRIENDLYNAME, &DataT, (PBYTE)buf, sizeof(buf), &nSize))
|
|
|
|
|
- {
|
|
|
|
|
- }
|
|
|
|
|
- else if (SetupDiGetDeviceRegistryProperty(hDevInfoSet, &spDevInfoData,
|
|
|
|
|
- SPDRP_DEVICEDESC, &DataT, (PBYTE)buf, sizeof(buf), &nSize))
|
|
|
|
|
|
|
+ // 先尝试从注册表获取 FriendlyName / DeviceDesc 作为回退名字
|
|
|
|
|
+ TCHAR regBuf[MAX_PATH] = { 0 };
|
|
|
|
|
+ DWORD propType = 0;
|
|
|
|
|
+ DWORD requiredSize = 0;
|
|
|
|
|
+ std::wstring regName;
|
|
|
|
|
+ if (SetupDiGetDeviceRegistryProperty(hDevInfoSet, &spDevInfoData, SPDRP_FRIENDLYNAME, &propType,
|
|
|
|
|
+ (PBYTE)regBuf, sizeof(regBuf), &requiredSize))
|
|
|
|
|
+ {
|
|
|
|
|
+ regName = regBuf;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (SetupDiGetDeviceRegistryProperty(hDevInfoSet, &spDevInfoData, SPDRP_DEVICEDESC, &propType,
|
|
|
|
|
+ (PBYTE)regBuf, sizeof(regBuf), &requiredSize))
|
|
|
|
|
+ {
|
|
|
|
|
+ regName = regBuf;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ regName = L"Unknown";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试读取总线报告的设备描述(通常为 USB Product string)
|
|
|
|
|
+ std::wstring busReported;
|
|
|
|
|
+ {
|
|
|
|
|
+ DEVPROPTYPE devPropType = 0;
|
|
|
|
|
+ DWORD req = 0;
|
|
|
|
|
+ WCHAR smallBuf[256] = { 0 };
|
|
|
|
|
+ BOOL got = FALSE;
|
|
|
|
|
+
|
|
|
|
|
+ // 使用本地定义的 DEVPROPKEY:PKEY_Device_BusReportedDeviceDesc_local(需在文件顶部定义)
|
|
|
|
|
+ if (SetupDiGetDevicePropertyW(hDevInfoSet, &spDevInfoData, &PKEY_Device_BusReportedDeviceDesc_local, &devPropType,
|
|
|
|
|
+ reinterpret_cast<PBYTE>(smallBuf), sizeof(smallBuf), &req, 0))
|
|
|
{
|
|
{
|
|
|
|
|
+ busReported = smallBuf;
|
|
|
|
|
+ got = TRUE;
|
|
|
}
|
|
}
|
|
|
- else
|
|
|
|
|
|
|
+ else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && req > 0)
|
|
|
{
|
|
{
|
|
|
- lstrcpy(buf, _T("Unknown"));
|
|
|
|
|
|
|
+ std::vector<BYTE> bigBuf(req);
|
|
|
|
|
+ if (SetupDiGetDevicePropertyW(hDevInfoSet, &spDevInfoData, &PKEY_Device_BusReportedDeviceDesc_local, &devPropType,
|
|
|
|
|
+ bigBuf.data(), req, &req, 0))
|
|
|
|
|
+ {
|
|
|
|
|
+ // bigBuf 存储为 WCHAR 字符串
|
|
|
|
|
+ busReported.assign(reinterpret_cast<WCHAR*>(bigBuf.data()));
|
|
|
|
|
+ got = TRUE;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- std::wstring ws_buf = buf;
|
|
|
|
|
-
|
|
|
|
|
- //是否是要找的设备类型
|
|
|
|
|
- if (ws_buf != strUSBPrint && ws_buf != strUSBPrint_EN)
|
|
|
|
|
|
|
+ if (got)
|
|
|
{
|
|
{
|
|
|
- LOG_INFO(L"not good usb, buf:" << ws_buf);
|
|
|
|
|
- continue;
|
|
|
|
|
|
|
+ LOG_INFO(L"BusReportedDeviceDesc: " << busReported);
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- LOG_INFO(L"good usb, buf:" << ws_buf);
|
|
|
|
|
- ifData.cbSize = sizeof(ifData);
|
|
|
|
|
-
|
|
|
|
|
- // 枚舉符合該GUID的設備接口
|
|
|
|
|
- bResult = ::SetupDiEnumDeviceInterfaces(
|
|
|
|
|
- hDevInfoSet, // 設備信息集句柄
|
|
|
|
|
- NULL, // 不需額外的設備描述
|
|
|
|
|
- lpGuid, // GUID
|
|
|
|
|
- (ULONG)nTotle, // 設備信息集里的設備序號
|
|
|
|
|
- &ifData); // 設備接口信息
|
|
|
|
|
-
|
|
|
|
|
- if (bResult)
|
|
|
|
|
- {
|
|
|
|
|
- // 取得该设备接口的细节(设备路径)
|
|
|
|
|
- bResult = SetupDiGetInterfaceDeviceDetail(
|
|
|
|
|
- hDevInfoSet, // 设备信息集句柄
|
|
|
|
|
- &ifData, // 设备接口信息
|
|
|
|
|
- pDetail, // 设备接口细节(设备路径)
|
|
|
|
|
- INTERFACE_DETAIL_SIZE, // 输出缓冲区大小
|
|
|
|
|
- NULL, // 不需计算输出缓冲区大小(直接用设定值)
|
|
|
|
|
- NULL); // 不需额外的设备描述
|
|
|
|
|
-
|
|
|
|
|
- if (bResult)
|
|
|
|
|
- {
|
|
|
|
|
- std::wstring DevicePath = pDetail->DevicePath;
|
|
|
|
|
|
|
+ // 如果既不是通用打印类也没有 busReported 描述,跳过该设备
|
|
|
|
|
+ if (regName != strUSBPrint && regName != strUSBPrint_EN && busReported.empty())
|
|
|
|
|
+ {
|
|
|
|
|
+ LOG_INFO(L"not good usb, regName:" << regName);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- std::wstring vid = DevicePath.substr(DevicePath.find(_T("vid_"), 0) + 4, 4);
|
|
|
|
|
- std::wstring pid = DevicePath.substr(DevicePath.find(_T("pid_"), 0) + 4, 4);
|
|
|
|
|
|
|
+ // 枚举该设备的信息接口以取得 DevicePath
|
|
|
|
|
+ ifData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
|
|
|
|
+ if (!::SetupDiEnumDeviceInterfaces(hDevInfoSet, NULL, lpGuid, idx, &ifData))
|
|
|
|
|
+ {
|
|
|
|
|
+ LOG_INFO("SetupDiEnumDeviceInterfaces failed for idx=" << idx);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- LOG_INFO("Vid:" << vid.c_str() << ", Pid:" << pid.c_str());
|
|
|
|
|
|
|
+ // 取得接口细节(包含 DevicePath)
|
|
|
|
|
+ if (!SetupDiGetInterfaceDeviceDetail(hDevInfoSet, &ifData, pDetail, INTERFACE_DETAIL_SIZE, NULL, NULL))
|
|
|
|
|
+ {
|
|
|
|
|
+ LOG_INFO("SetupDiGetInterfaceDeviceDetail failed for idx=" << idx);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 复制设备路径到输出缓冲区
|
|
|
|
|
- usb_devices.push_back(DevicePath);
|
|
|
|
|
|
|
+ std::wstring DevicePath = pDetail->DevicePath;
|
|
|
|
|
|
|
|
- // 调整计数值
|
|
|
|
|
- nCount++;
|
|
|
|
|
|
|
+ // 从 DevicePath 中提取 vid/pid 以便后续判断或使用 WinUSB
|
|
|
|
|
+ std::wstring vid = L"";
|
|
|
|
|
+ std::wstring pid = L"";
|
|
|
|
|
+ size_t posVid = DevicePath.find(L"vid_");
|
|
|
|
|
+ size_t posPid = DevicePath.find(L"pid_");
|
|
|
|
|
+ if (posVid != std::wstring::npos && posVid + 4 + 4 <= DevicePath.length()) vid = DevicePath.substr(posVid + 4, 4);
|
|
|
|
|
+ if (posPid != std::wstring::npos && posPid + 4 + 4 <= DevicePath.length()) pid = DevicePath.substr(posPid + 4, 4);
|
|
|
|
|
+ LOG_INFO("Vid:" << vid.c_str() << ", Pid:" << pid.c_str());
|
|
|
|
|
|
|
|
- LOG_INFO("Cnt = " << nCount << ",pDetail->DevicePath =" << pDetail->DevicePath);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 输出检测到的名称(优先 busReported,其次注册表名)
|
|
|
|
|
+ if (!busReported.empty())
|
|
|
|
|
+ {
|
|
|
|
|
+ LOG_INFO(L"Detected printer name: " << busReported);
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- //LOG_INFO("SetupDiEnumDeviceInfo FAILED!@");
|
|
|
|
|
|
|
+ LOG_INFO(L"Detected registry name: " << regName);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 将 DevicePath 添加到结果集合
|
|
|
|
|
+ usb_devices.push_back(DevicePath);
|
|
|
|
|
+ ++nCount;
|
|
|
|
|
+ LOG_INFO("Cnt = " << nCount << ", pDetail->DevicePath =" << pDetail->DevicePath);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 释放设备接口数据空间
|
|
|
|
|
|
|
+ // 清理并返回
|
|
|
::GlobalFree(pDetail);
|
|
::GlobalFree(pDetail);
|
|
|
-
|
|
|
|
|
- // 关闭设备信息集句柄
|
|
|
|
|
::SetupDiDestroyDeviceInfoList(hDevInfoSet);
|
|
::SetupDiDestroyDeviceInfoList(hDevInfoSet);
|
|
|
return nCount;
|
|
return nCount;
|
|
|
}
|
|
}
|
|
@@ -820,104 +1051,6 @@ std::vector<std::wstring> CPosPrinterWorker::getUsbDevices()
|
|
|
return usb_devices;
|
|
return usb_devices;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-bool CPosPrinterWorker::IsPaperOut(HANDLE hPort)
|
|
|
|
|
-{
|
|
|
|
|
- if (hPort == NULL || hPort == INVALID_HANDLE_VALUE)
|
|
|
|
|
- {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- DWORD dwWrite = 0;
|
|
|
|
|
- if (!WriteWithTimeout(hPort, ESC_POS_QUERY_PAPER_STATUS, sizeof(ESC_POS_QUERY_PAPER_STATUS), 500, dwWrite) ||
|
|
|
|
|
- dwWrite != sizeof(ESC_POS_QUERY_PAPER_STATUS))
|
|
|
|
|
- {
|
|
|
|
|
- LOG_INFO("查询打印机缺纸状态失败,WriteWithTimeout Failed!, hPort:" << hPort);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- unsigned char status = 0;
|
|
|
|
|
- DWORD dwRead = 0;
|
|
|
|
|
- if (!ReadWithTimeout(hPort, &status, 1, 500, dwRead) || dwRead != 1)
|
|
|
|
|
- {
|
|
|
|
|
- LOG_INFO("查询打印机缺纸状态超时或无返回,hPort:" << hPort);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return (status & ESC_POS_PAPER_OUT_MASK) == ESC_POS_PAPER_OUT_MASK;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-bool CPosPrinterWorker::IsPaperOut(boost::asio::ip::tcp::socket& socket)
|
|
|
|
|
-{
|
|
|
|
|
- try
|
|
|
|
|
- {
|
|
|
|
|
- boost::asio::write(socket, boost::asio::buffer(ESC_POS_QUERY_PAPER_STATUS, sizeof(ESC_POS_QUERY_PAPER_STATUS)));
|
|
|
|
|
-
|
|
|
|
|
- boost::system::error_code ec;
|
|
|
|
|
- bool oldNonBlocking = socket.non_blocking();
|
|
|
|
|
- socket.non_blocking(true, ec);
|
|
|
|
|
- if (ec)
|
|
|
|
|
- {
|
|
|
|
|
- LOG_INFO("设置网口打印机 non_blocking 失败,错误信息:" << ec.message().c_str());
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- unsigned char status = 0;
|
|
|
|
|
- DWORD startTick = GetTickCount();
|
|
|
|
|
-
|
|
|
|
|
- while (GetTickCount() - startTick < 500)
|
|
|
|
|
- {
|
|
|
|
|
- size_t len = socket.read_some(boost::asio::buffer(&status, 1), ec);
|
|
|
|
|
- if (!ec && len == 1)
|
|
|
|
|
- {
|
|
|
|
|
- socket.non_blocking(oldNonBlocking, ec);
|
|
|
|
|
- return (status & ESC_POS_PAPER_OUT_MASK) == ESC_POS_PAPER_OUT_MASK;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (ec == boost::asio::error::would_block || ec == boost::asio::error::try_again)
|
|
|
|
|
- {
|
|
|
|
|
- ec.clear();
|
|
|
|
|
- Sleep(20);
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- LOG_INFO("查询网口打印机缺纸状态失败,错误信息:" << ec.message().c_str());
|
|
|
|
|
- socket.non_blocking(oldNonBlocking, ec);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- socket.non_blocking(oldNonBlocking, ec);
|
|
|
|
|
- LOG_INFO("查询网口打印机缺纸状态超时");
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- catch (const std::exception& e)
|
|
|
|
|
- {
|
|
|
|
|
- LOG_INFO("查询网口打印机缺纸状态异常,错误信息:" << e.what());
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-bool CPosPrinterWorker::WaitPrinterReady(HANDLE hPort)
|
|
|
|
|
-{
|
|
|
|
|
- while (m_is_work && IsPaperOut(hPort))
|
|
|
|
|
- {
|
|
|
|
|
- LOG_INFO("打印机缺纸,1秒后继续检测...");
|
|
|
|
|
- Sleep(1000);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return m_is_work;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-bool CPosPrinterWorker::WaitPrinterReady(boost::asio::ip::tcp::socket& socket)
|
|
|
|
|
-{
|
|
|
|
|
- while (m_is_work && IsPaperOut(socket))
|
|
|
|
|
- {
|
|
|
|
|
- LOG_INFO("网口打印机缺纸,1秒后继续检测...");
|
|
|
|
|
- Sleep(1000);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return m_is_work;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
bool CPosPrinterWorker::WriteWithTimeout(HANDLE hPort, const void* buffer, DWORD bufferSize, DWORD timeoutMs, DWORD& dwWrite)
|
|
bool CPosPrinterWorker::WriteWithTimeout(HANDLE hPort, const void* buffer, DWORD bufferSize, DWORD timeoutMs, DWORD& dwWrite)
|
|
|
{
|
|
{
|
|
|
dwWrite = 0;
|
|
dwWrite = 0;
|