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

news2024/11/25 9:34:17

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 

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日一题——LeetCode859

方法一 个人方法&#xff1a; 首先s和goal要是长度不一样或者就只有一个字符这两种情况可以直接排除剩下的情况s和goal的长度都是一样的&#xff0c;s的长度为2也是特殊情况&#xff0c;只有s的第一位等于goal的第二位&#xff0c;s的第二位等于goal的第一位才能满足剩下的我们…

tensorboard可视化——No dashboards are active for the current data set.

No dashboards are active for the current data set. 出现问题的原因是事件的路径未用绝对路径&#xff0c;tensorboard --logdir./runs --port6007 改为tensorboard --logdirD:\Code\Python\Study\CL\hat-master\hat-master\run s\one --port6007就好了

Linux操作系统基础(一)系统和软件的安装

Linux操作系统简介 Linux是一种自由和开放源码的类Unix操作系统。该操作系统的内核由芬兰人林纳斯托瓦兹在1991年10月5日首次发布&#xff0c;再加上用户空间的应用程序之后&#xff0c;就成为了Linux操作系统。Linux也是自由软件和开放源代码软件发展中最著名的例子。 Linux…

Tomcat与Netty比较

Tomcat介绍Tomcat支持的协议Tomcat的优缺点Netty介绍Netty支持的协议Netty的优点和缺点Tomcat和Netty的区别Tomcat和Netty的应用场Tomcat和Netty来处理大规模并发连接的优化Tomcat与Netty的网络模型的区别Tomcat与Netty架构设计拓展 Tomcat介绍 Tomcat是一个免费的、开放源代码…

Matlab-修改默认启动路径

Matlab-修改默认启动路径 第一:找到MATLAB的安装路径 第二步&#xff1a;进入到…\toolbox\local下&#xff0c;找到matlabrc.m 第三部&#xff1a;编辑matlabrc.m&#xff0c;在文本最后一行加入启动文件路径

应急响应中的溯源方法

在发现有入侵者后&#xff0c;快速由守转攻&#xff0c;进行精准地溯源反制&#xff0c;收集攻击路径和攻击者身份信息&#xff0c;勾勒出完整的攻击者画像。 对内溯源与对内溯源 对内溯源&#xff1a;确认攻击者的行为 &#xff0c;分析日志 数据包等&#xff1b; 对外溯源&…

Flutter中鼠标 onEnter onExit onHover 实现代码分析

生活会给你任何最有益的经历&#xff0c;以助你意识的演变。 转载请注明出处: 这里对最近用到的一些 Flutter 开源的东西进行总结积累&#xff0c;希望能帮助到大家。 文章目录 背景测试代码flutter 代码onEnter & onExitonHover end 背景 Android设备在使用的时候&#…

3.认识HTML

一、HTML是什么&#xff1f; 超&#xff1a;超链接 二、W3C制定了HTML规范 2014年HTML5正式发布 三、HTML初体验 四、老师常用网站

大数据---35.HBase 常用的api的具体解释

Hbase是一个分布式的、面向列的开源数据库&#xff0c;HDFS文件操作常有两种方式&#xff0c;一种是命令行方式&#xff0c;即Hbase提供了一套与Linux文件命令类似的命令行工具。另一种是JavaAPI&#xff0c;即利用Hbase的Java库&#xff0c;采用编程的方式操作Hbase数据库。 …

红队攻防实战之DC1

如果额头终将刻上皱纹&#xff0c;你只能做到&#xff0c;不让皱纹刻在你的心上 0x01 信息收集: 1.1 端口探测 使用nmap工具 端口扫描结果如下&#xff1a; 由nmap扫描可以知道&#xff0c;目标开放了22,80,111,46204端口&#xff0c;看到端口号22想到ssh远程连接&#xff…

docker部署mysql主主备份 haproxy代理(swarm)

docker部署mysql主主备份 haproxy代理&#xff08;swarm&#xff09; docker部署mysql主主备份 docker部署mysql主主备份&#xff08;keepalived&#xff09;跨主机自动切换 docker部署mysql主主备份 haproxy代理&#xff08;swarm&#xff09; 1. 环境准备 主机IPnode119…

【贪心】最小生成树Prim算法Python实现

文章目录 [toc]问题描述最小生成树的性质证明 Prim算法Prim算法的正确性时间复杂性Python实现 个人主页&#xff1a;丷从心 系列专栏&#xff1a;贪心算法 问题描述 设 G ( V , E ) G (V , E) G(V,E)是无向连通带权图&#xff0c; E E E中每条边 ( v , w ) (v , w) (v,w)的…

多线程的基本使用与多线程中条件变量的使用——消费者生产者问题实例

多线程的基本使用与多线程中条件变量的使用——消费者生产者问题实例 本文主要涉及多线程的使用方法&#xff0c;通过两个实例来对多线程的使用进行理解&#xff0c; 案例包括&#xff1a; 1.一个线程负责计数&#xff0c;另一个线程负责打印计数值 2.消费者生产者问题 文章目录…

【MySQL索引特性】

目录&#xff1a; 前言引入认识磁盘MySQL与存储 索引的理解理解单个Page理解多个Page引入B树结构聚簇索引 VS 非聚簇索引 索引操作创建主键索引唯一索引的创建普通索引的创建查看索引删除索引 总结 前言 剑指offer&#xff1a;一年又10天 引入 索引&#xff0c;是用来提高查询…

【python】python课设 天气预测数据分析及可视化(完整源码)

目录 1. 前言2. 项目结构3. 详细介绍3.1 main.py3.2 GetModel.py3.3 GetData.py3.4 ProcessData.py3.5天气网.html 4. 成果展示 1. 前言 本文介绍了天气预测数据分析及可视化的实现过程使用joblib导入模型和自定义模块GetModel获取模型&#xff0c;输出模型的MAE。使用pyechart…

ansible的控制语句

本章内容主要介绍 playbook 中的控制语句 使用when判断语句block-rescue判断循环语句 一个play中可以包含多个task&#xff0c;如果不想所有的task全部执行&#xff0c;可以设置只有满足某个条件才执行这个task&#xff0c;不满足条件则不执行此task。本章主要讲解when 和 blo…

Linux安装及管理程序

一、Linux应用程序管理 1、应用程序与系统命令的关系 1.对比系统命令和应用程序的不同 位置&#xff1a; Linux中一切皆为文件 演示内部命令和外部命令 位置 应用程序位置 用途&#xff1a; 命令主要处理系统的基本操作&#xff08;复制&#xff0c;配置&#xff09; 应用程…

大模型工具_Langchain-Chatchat

https://github.com/chatchat-space/Langchain-Chatchat 原Langchain-ChatGLM 1 功能 整体功能&#xff0c;想解决什么问题 基于 Langchain 与 ChatGLM 等LLM模型&#xff0c;搭建一套针对中文场景与开源模型&#xff0c;界面友好、可离线运行的知识库问答解决方案。 当前解决…

米勒电容与米勒效应

米勒电容与米勒效应 米勒效应米勒效应的形成原理及分析米勒效应的危害和改进 米勒效应 Ciss CGE CGC 输入电容 Coss CGC CEC 输出电容 Crss CGC 米勒电容 下面我们以MOS中的米勒效应来展开说明&#xff1a; 米勒效应在MOS驱动中臭名昭著&#xff0c;它是由MOS管的米勒电容引发…

运行时和编译时使用的so库不同是否影响可执行文件执行

引子 近日遇到如下问题: 1.如果可执行文件依赖的so库在编译和执行阶段使用的名字一样&#xff0c;但是内容不一样&#xff0c;比如运行时相比于编译时在so库里增加了几个api定义&#xff0c;so库还可以正常使用吗&#xff1f; 2.如果可执行文件依赖的so库在编译和执行阶段使用的…