c++代码寻找USB00端口并添加打印机

USB00*端口的背景

插入USB端口的打印机,安装打印机驱动,在控制面板设备与打印机处的打印机对象上右击,可以看到打印机端口。对于不少型号,这个端口是USB001USB002之类的。

经观察,这些USB00*端口并不是打印机驱动所创建的。即使不安装打印机驱动,插入此型号的打印机也会创建或者复用USB00*端口。从setupapi.dev.log中可知,端口是在C:\Windows\INF\usbprint.inf的指示下创建的。所谓复用,指的是,若USB001端口已存在并且没有关联上打印机,那么此时插入相关型号打印机,此打印机设备会跟USB001端口绑定起来。

在设备管理器中可以看到USB00*端口和设备的对应关系。这里的USB打印支持的设备还具有硬件ID属性vid、pid。若vid、pid均相同,我认为它们都是同一型号的打印机。

USB00*端口的背景

插入USB端口的打印机,安装打印机驱动,在控制面板设备与打印机处的打印机对象上右击,可以看到打印机端口。对于不少型号,这个端口是USB001USB002之类的。

经观察,这些USB00*端口并不是打印机驱动所创建的。即使不安装打印机驱动,插入此型号的打印机也会创建或者复用USB00*端口。从setupapi.dev.log中可知,端口是在C:\Windows\INF\usbprint.inf的指示下创建的。所谓复用,指的是,若USB001端口已存在并且没有关联上打印机,那么此时插入相关型号打印机,此打印机设备会跟USB001端口绑定起来。

在设备管理器中可以看到USB00*端口和设备的对应关系。这里的USB打印支持的设备还具有硬件ID属性vid、pid。若vid、pid均相同,我认为它们都是同一型号的打印机。

c++代码寻找USB00*所在的设备

类似于设备管理器,本节的目标是:遍历设备管理器里的设备大类,再找每一个设备,再找设备里的各种属性。直到找到我们关注的vid、pid,然后查看其总线关系里的USB00*编号。

相关概念

  • 设备安装类:HKLM\SYSTEM\CurrentControlSet\Control\Class里的每一个key都是设备安装类。其中的{36fc9e60-c465-11cf-8056-444553540000}就是设备管理器中的通用串行总线控制器

  • 设备接口类:HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses里的每一个key都是设备接口类。

  • 获取设备属性的两类api:一类是SetupDiGetDeviceRegistryProperty,参数一来自SetupDiGetClassDevs。一类是CM_Get_DevNode_PropertyW,参数三来自SetupDiEnumDeviceInfo。我们关注的总线关系需通过CM_Get_DevNode_PropertyW获取。

c++ demo

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Cfgmgr32.h>
#pragma comment(lib, "Cfgmgr32.lib")
#include <SetupAPI.h>
#pragma comment(lib, "SetupAPI.lib")
#include <memory>
using namespace std;

//keyseq=1        type=2012       buf=USB\VID_0471&PID_0055&REV_0100
bool jobForThePrinter1(const wchar_t* str, const wchar_t*vid, const wchar_t*pid) {
        if (_wcsnicmp(vid, str+8, 4)) return false;
        if (_wcsnicmp(pid, str+17, 4)) return false;
        return true;
}
//keyseq = 26       type = 2012       buf=USBPRINT\UnknownPrinter\6&1cc6481b&0&USB002
bool jobForThePrinterN(int &usb00n, const wchar_t* str) {
        if (_wcsnicmp(L"USBPRINT", str, 8)) return false;
        auto lenStr = wcslen(str);
        if (_wcsnicmp(L"USB", str+lenStr-6, 3)) return false;
        usb00n = wcstol(str + lenStr - 3, NULL, 10);
        return true;
}
int getPropertyFromDevinstWhereVidpid(DEVINST dnDevInst, const wchar_t*vid, const wchar_t*pid) {
#define LENBUF 8192
        ULONG lenBuf = LENBUF;
        static BYTE buf[LENBUF];
        ULONG cnt = 0;
        auto ret = CM_Get_DevNode_Property_Keys(dnDevInst, NULL, &cnt, 0);
        if (0 == cnt) return 0;
        std::unique_ptr<DEVPROPKEY[]> keys(new DEVPROPKEY[cnt]);
        ret = CM_Get_DevNode_Property_Keys(dnDevInst, keys.get()
                , &cnt, 0);
        if (ret != CR_SUCCESS) {
                fprintf(stderr, "CM_Get_DevNode_Property_Keys FAIL %d\n", ret);
                return 0;
        }
        DEVPROPTYPE type;
        DWORD i = 0;
        ret = CM_Get_DevNode_PropertyW(dnDevInst, keys.get()
                + i, &type, buf, &lenBuf, 0);
        if (ret != CR_SUCCESS) {
                fprintf(stderr, "CM_Get_DevNode_PropertyW FAIL %d\n", ret);
                return 0;
        }
        //如果不是"USB打印支持",则也跳过
        if (type != DEVPROP_TYPE_STRING) return 0;
        wprintf(L"%s\n", buf);
        if (wcscmp(PTCHAR(buf), L"USB 打印支持")) return 0;
        ++i;
        for (; i < cnt; ++i) {
                lenBuf = LENBUF;
                ret = CM_Get_DevNode_PropertyW(dnDevInst, keys.get()
                        + i, &type, buf, &lenBuf, 0);
                printf("keyseq=%d\ttype=%x\t", i, type);
                //keyseq=1        type=2012       buf=USB\VID_0471&PID_0055&REV_0100
                //keyseq = 26       type = 2012       buf = USBPRINT\UnknownPrinter\6 & 1cc6481b & 0 & USB002
                switch (type) {
                case DEVPROP_TYPE_EMPTY:
                case DEVPROP_TYPE_NULL:
                        continue;
                case DEVPROP_TYPE_STRING:
                        wprintf(L"buf=%s\n", buf);
                        break;
                case DEVPROP_TYPE_UINT32:
                case DEVPROP_TYPE_UINT64:
                        printf("buf=%uld\n", UINT32(buf));
                        break;
                case DEVPROP_TYPE_GUID: {
                        GUID guidtmp;
                        memcpy(&guidtmp, buf, sizeof(guidtmp));
                        printf("buf={%x-%x-%x-%s}\n", guidtmp.Data1, guidtmp.Data2, guidtmp.Data3, guidtmp.Data4);
                }
                break;
                case DEVPROP_TYPE_BOOLEAN:
                        if (!buf[0]) printf("buf=false\n");
                        else printf("buf=true\n");
                        break;
                case 0x2012://DEVPROP_TYPEMOD_LIST & DEVPROP_TYPE_STRING
                {
#if 1
                        LPTSTR strTmp = LPTSTR(buf);
                        unsigned lenTmp = 0;
                        do {
                                lenTmp = wcslen(strTmp);
                                if (!lenTmp) break;
                                wprintf(L"buf=%s\n", strTmp);
                                strTmp = strTmp + lenTmp + 1;
                        } while (1);
#endif
                        if (1 == i) {
                                if (!jobForThePrinter1((const wchar_t*)buf, vid, pid)) return 0;
                        }
                        int usb00n = 0;
                        if (jobForThePrinterN(usb00n, (const wchar_t*)buf)) {
                                return usb00n;
                        }
                }
                break;
                case 0x1003://DEVPROP_TYPEMOD_ARRAY& DEVPROP_TYPE_BYTE
                        //printf("type=%x\n", type);
                        break;
                default:
                        //wprintf(L"type=%x\tbuf=%s\n", type, buf);
                        break;
                }
        }
        return 0;
}

int enum36fcEachDevProperty(LPCWSTR vid, LPCWSTR pid) {
        static const GUID guid36fc = { 0x36fc9e60, 0xc465, 0x11cf,
                {0x80, 0x56, 0x44, 0x45, 0x53, 0x54, 0, 0} };
        HDEVINFO hDevInfo = SetupDiGetClassDevs(&guid36fc, 0, 0, DIGCF_PRESENT);
        if (hDevInfo == INVALID_HANDLE_VALUE) return 1;
        SP_DEVINFO_DATA deviceInfoData{ sizeof(SP_DEVINFO_DATA) };
        for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &deviceInfoData); ++i) {
                printf("dev=%d\n", i);
                int usb00n = getPropertyFromDevinstWhereVidpid(deviceInfoData.DevInst, vid, pid);
                if (usb00n) {
                        SetupDiDestroyDeviceInfoList(hDevInfo);
                        return usb00n;
                }
        }
        SetupDiDestroyDeviceInfoList(hDevInfo);
        return 0;
}

int WaitUsbPrinterDev(const wchar_t* vid, const wchar_t* pid, int minutes) {
        int usb00n;
        int i = 0;
        do {
                usb00n = enum36fcEachDevProperty(vid, pid);
                if (usb00n) return usb00n;
                Sleep(30000);
                ++i;
        } while (i <= 4 * minutes);
        return 0;
}

线索

USB00*端口存在时,可以从注册表中搜寻到,诸如HKLM\SYSTEM\CurrentControlSet\Enum\USBPRINT\UnknownPrinter\8&73672f4&0&USB002\Device Parameters里的数据Portname="USB002",数据ClassGUID={36fc9e60-c465-11cf-8056-444553540000}。所以我们要用设备安装类相关api来遍历设备。

添加USB00*为端口的打印机对象

原理

主要使用apiAddPrinter。参数三是个PRINTER_INFO_2结构体的指针。根据经验,info2.pPrintProcessor一般都是"winprint"

c++ demo

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <winspool.h>
#include <memory>
using namespace std;
#include <cstdio>

void addprinter(int usb00n, const wchar_t* pDrivername) {
        WCHAR proc[] = L"winprint";
        PRINTER_INFO_2W info2 = { 0 };
        info2.pPrintProcessor = proc;
        auto len = wcslen(pDrivername);
        unique_ptr<WCHAR[]> drvname(new WCHAR[len+1]);
        wcscpy_s(drvname.get(), len+1, pDrivername);
        info2.pDriverName = drvname.get();
        WCHAR portname[7];
        swprintf_s(portname, L"USB%03d", usb00n);
        info2.pPortName = portname;
        WCHAR printername[] = L"printername";
        info2.pPrinterName = printername;
        auto h = AddPrinter(NULL, 2, reinterpret_cast<LPBYTE>(&info2));
        if (!h) {
                const auto err = GetLastError();
                fwprintf(stderr, L"AddPrinter FAIL %d\n", err);
                return;
        }
        wprintf(L"打印机对象添加成功,名为%s\n", printername);
}

main.cpp和部署脚本

main.cpp

#include <clocale>
#include <cstdio>

int WaitUsbPrinterDev(const wchar_t* vid, const wchar_t* pid, int minutes = 3);
void addprinter(int usb00n, const wchar_t* pDrivername);

int wmain(int argc, wchar_t** argv) {
        setlocale(LC_CTYPE, "");
        if (4 != argc) {
                wprintf(L"请输入:vid pid \"打印机型号\"\n如:6868 0200 \"GP-58130 Series\"\n");
        }
        const wchar_t* vid = argv[1];
        const wchar_t* pid = argv[2];
        const wchar_t* model = argv[3];
        wprintf(L"正在扫描设备管理器,请确保相关打印机连入并开机……\n");
        int usb00n = WaitUsbPrinterDev(vid, pid);
        if (!usb00n) {
                wprintf(L"未见相关打印机连入。请检查vidpid是否正确,打印机是否开机\n");
                return 0;
        }
        wprintf(L"此打印机占用端口USB%03d\n正在添加打印机对象……\n", usb00n);
        addprinter(usb00n, model);
        return 0;
}

部署脚本

自动添加打印机对象.vbs

Set ws = CreateObject("Wscript.Shell")
ws.run "C:\AutoAddPrinter.exe 6868 0200 "&chr(34)&"GP-58130 Series"&chr(34)&"",vbhide

readme.txt

部署方法: 1. 编辑镜像时将“AutoAddPrinter.exe”放入C盘根目录。将“自动添加打印机对象.vbs”放入开始菜单的启动目录。

2. 编辑镜像时要删除那个型号的打印机对象

3. 镜像下发到终端上。

4. 虚机运行时,保证打印机已连入并开机。此程序会开机自启,扫描出打印机连上并添加打印机对象。打印机对象名称是newprinter 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/267467.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Seata 序列化问题

异常&#xff1a; com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Type id handling not implemented for type java.lang.Object (by serializer of type com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer) (through reference chain: i…

新建项目EasyUiAutotest,安装Appium-Python-Client

一、前置说明 Appium-Python-Client 是 Appium 的 Python 客户端库&#xff0c;它提供了一系列的类和方法&#xff0c;用于与 Appium 服务器进行通信&#xff0c;并执行各种移动应用测试操作&#xff0c;包括启动应用、模拟用户输入、点击等操作。 二、操作步骤 1. 启动Pych…

Exynos4412 移植Linux-6.1(九)移植tiny4412_backlight驱动的过程及问题解决

系列文章目录 Exynos4412 移植Linux-6.1&#xff08;一&#xff09;下载、配置、编译Linux-6.1 Exynos4412 移植Linux-6.1&#xff08;二&#xff09;SD卡驱动——解决无法挂载SD卡的根文件系统 Exynos4412 移植Linux-6.1&#xff08;三&#xff09;SD卡驱动——解决mmc0: Ti…

写了这么多年DateUtils,殊不知你还有这么多弯弯绕!

目录 在日常开发中&#xff0c;Date工具类使用频率相对较高&#xff0c;大家通常都会这样写&#xff1a;这很简单啊&#xff0c;有什么争议吗&#xff1f;格式化后出现的时间错乱。看看Java 8是如何解决时区问题的&#xff1a;在处理带时区的国际化时间问题&#xff0c;推荐使用…

LSTM和GRU vs 循环神经网络RNN

1、考虑下列三种情况下&#xff0c;对比一下普通RNN的表现和LSTM和GRU表现&#xff1a; &#xff08;1&#xff09;早期观测值对预测未来观测者具有非常重要的意义。 考虑一个极端情况&#xff0c;其中第一个观测值包含一个校验和&#xff0c; 目标是在序列的末尾辨别校验和是…

第十四章 集合(List)

一、集合框架体系 集合&#xff1a; &#xff08;1&#xff09;可以动态保存任意多个对象。 &#xff08;2&#xff09;提供了一系列方便的操作对象的方法&#xff1a;add、remove、set、get等。 二、Collection 1. Collection 接口常用方法 &#xff08;1&#xff09;add&a…

BP网络识别26个英文字母matlab

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;字母识别 获取完整源码源工程文件 一、 设计思想 字符识别在现代日常生活的应用越来越广泛&#xff0c;比如车辆牌照自动识别系统&#xff0c;手写识别系统&#xff0c;办公自动化等等。本文采用BP网络对26个英文字母进行…

第 377 场周赛 解题报告 | 珂学家 | Floyd + 划分型DP

前言 整体评价 天崩局&#xff0c;压哨绝杀&#xff0c;感谢天&#xff0c;感谢地&#xff0c;T_T. 感觉被T2玩惨了&#xff0c;T3和T4很像&#xff0c;无非一个贪心&#xff0c;一个是划分型DP&#xff0c;但是都需要基于floyd预处理。 T1. 最小数字游戏 思路&#xff1a; …

接口测试 — 11.logging日志模块处理流程

1、概括理解 了解了四大组件的基本定义之后&#xff0c;我们通过图示的方式来理解下信息的传递过程&#xff1a; 也就是获取的日志信息&#xff0c;进入到Logger日志器中&#xff0c;传递给处理器确定要输出到哪里&#xff0c;然后进行过滤器筛选&#xff0c;通过后再按照定义…

【LeetCode】链表精选11题

目录 快慢指针&#xff1a; 1. 相交链表&#xff08;简单&#xff09; 2. 环形链表&#xff08;简单&#xff09; 3. 快乐数&#xff08;简单&#xff09; 4. 环形链表 II&#xff08;中等&#xff09; 5. 删除链表的倒数第 N 个节点&#xff08;中等&#xff09; 递归迭…

量化投资策略的评估标准及其计算公式

收益率指标&#xff1a;分为策略的总收益率和策略的年化收益率 策略的总收益率&#xff1a; 策略的总收益率是评价一个策略盈利能力的最基本的指标&#xff0c;其计算方法为&#xff1a; 公式中Vt表示策略最终的股票和现金的总价值&#xff0c;V0表示策略最初的股票和现金的总…

探秘JDK 13的黑科技:新特性一览

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 探秘JDK 13的黑科技&#xff1a;新特性一览 前言switch表达式扩展Switch表达式的基本概念&#xff1a;使用Switch表达式的优势&#xff1a;示例代码&#xff1a;注意事项和最佳实践&#xff1a; Text …

Spring Cloud + Vue前后端分离-第7章 核心业务功能开发

Spring Cloud Vue前后端分离-第7章 核心业务功能开发 7-1 课程管理功能开发 课程管理页面美化 1.课程管理页面美化 demo-course.jpg 复制search.html中的部分代码 course.vue 看效果 测试一下新增修改删除效果 1.课程管理页面美化2 scoped:style下的样式只应用于当前组件…

LCT(link cut tree) 详细图解与应用

樱雪喵用时 3days 做了 ybtoj 的 3 道例题&#xff0c;真是太有效率了&#xff01;&#xff01;1 为了避免自己没学明白就瞎写东西误人子弟&#xff0c;这篇 Blog 拖到了现在。 图片基本沿用 OIwiki&#xff0c;原文跳步骤&#xff08;主要是 access 部分&#xff09;的就自己…

Spark编程实验三:Spark SQL编程

目录 一、目的与要求 二、实验内容 三、实验步骤 1、Spark SQL基本操作 2、编程实现将RDD转换为DataFrame 3、编程实现利用DataFrame读写MySQL的数据 四、结果分析与实验体会 一、目的与要求 1、通过实验掌握Spark SQL的基本编程方法&#xff1b; 2、熟悉RDD到DataFram…

如何利用flume进行日志采集

介绍 Apache Flume 是一个分布式、可靠、高可用的日志收集、聚合和传输系统。它常用于将大量日志数据从不同的源&#xff08;如Web服务器、应用程序、传感器等&#xff09;收集到中心化的存储或数据处理系统中。 基本概念 Agent&#xff08;代理&#xff09;&#xff1a; …

039、转置卷积

之——增大高宽 杂谈 通常来说&#xff0c;卷积不会增大输入的高宽&#xff0c;通常要么不变&#xff0c;要么减半&#xff1b;如果想要直接padding来增加高宽&#xff0c;在不断的卷积过程中&#xff0c;padding的0越来越多&#xff0c;最后要做像素级的判断时候&#xff0c;由…

分布式核心技术之分布式共识

文章目录 什么是分布式共识&#xff1f;分布式共识方法PoWPoSDPoS 三种分布式共识算法对比分析 选主过程就是一个分布式共识问题&#xff0c;因为每个节点在选出主节点之前都可以认为自己会成为主节点&#xff0c;也就是说集群节点“存异”&#xff1b;而通过选举的过程选出主节…

基于Java SSM框架实现医院挂号上班打卡系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现医院挂号上班打卡系统演示 摘要 在网络发展的时代&#xff0c;国家对人们的健康越来越重视&#xff0c;医院的医疗设备更加先进&#xff0c;医生的医术、服务水平也不断在提高&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个…

【leetcode100-019】【矩阵】螺旋矩阵

【题干】 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 【思路】 不难注意到&#xff0c;每进行一次转向&#xff0c;都有一行/列被输出&#xff08;并失效&#xff09;&#xff1b;既然已经失效&#xff0c;那我…