|
|
@@ -3,12 +3,16 @@
|
|
|
|
|
|
#include "../helper/CComHelper.h"
|
|
|
|
|
|
-// 在文件顶部(现有 include 之后)添加
|
|
|
#include <devpkey.h>
|
|
|
#include <devpropdef.h>
|
|
|
|
|
|
#pragma comment(lib, "setupapi.lib")
|
|
|
-#pragma comment(lib, "winspool.lib")
|
|
|
+
|
|
|
+//SetupDiGetInterfaceDeviceDetail所需要的输出长度,定义足够大
|
|
|
+#define INTERFACE_DETAIL_SIZE 1024
|
|
|
+
|
|
|
+//USB类的GUID,通过这个去找USB打印机
|
|
|
+const GUID USB_GUID = { 0x28d78fad, 0x5a12, 0x11d1, {0xae, 0x5b, 0x00, 0x00, 0xf8, 0x03, 0xa8, 0xc2 } };
|
|
|
|
|
|
// 本地定义 DEVPROPKEY,等价于 DEVPKEY_Device_BusReportedDeviceDesc (fmtid + pid=4)
|
|
|
const DEVPROPKEY PKEY_Device_BusReportedDeviceDesc_local =
|
|
|
@@ -17,243 +21,6 @@ const DEVPROPKEY PKEY_Device_BusReportedDeviceDesc_local =
|
|
|
4
|
|
|
};
|
|
|
|
|
|
-// Helper: case-insensitive find
|
|
|
-static bool ci_find_substr(const std::wstring& hay, const std::wstring& needle)
|
|
|
-{
|
|
|
- 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;
|
|
|
-
|
|
|
- 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()
|
|
|
{
|
|
|
}
|