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 8192ULONG 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 & USB002switch (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 1LPTSTR 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);
#endifif (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.mzph.cn/news/291224.shtml

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

相关文章

IOS调用WCF提供的服务方法,但是方法的参数是WCF那边自定义的对象,这样有办法调用么,如果可以IOS应该怎么传参呢?请问有了解的么,...

最近做一个项目后端使用WCF接收Android手机拍照并带其它参数保存到服务器里&#xff1b;刚好把最近学习的WCF利用上&#xff0c;本以为是个比较简单的功能应该很好实现&#xff0c;没想到其中碰到不少问题&#xff0c;在网上搜索很久一直没有想到的解决方案&#xff0c;最后实现…

ABP vNext微服务架构详细教程——项目部署

1基础配置在之前的文章中&#xff0c;我们已经配置了Kubernetes集群并安装了管理工具Kubesphere&#xff0c;文章地址为&#xff1a;https://mp.weixin.qq.com/s/MgpdMv5A-fYxN7XY8N9Djw登录Kubesphere页面&#xff0c;打开工作台&#xff0c;在平台资源选项卡中点击“企业空间…

offsetTop和scrollTop的差别

近期想写个组件&#xff0c;结果被这两个属性搞的有点晕&#xff0c;查了下文档和资料&#xff0c;对这两个属性总结例如以下&#xff1a; 一直以来对offsetLeft&#xff0c;offsetTop&#xff0c;scrollLeft&#xff0c;scrollTop这几个方法非常迷糊&#xff0c;花了一天的时间…

quartz (一) 基于 Quartz 开发企业级任务调度应用

本文转自&#xff1a;http://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/ Quartz 基本概念及原理 Quartz Scheduler 开源框架 Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目&#xff0c;完全基于 Java 实现。该项目于 2009 年被 Terracotta 收购&…

C# 11 中的参数 null 检查

C# 11 中的参数 null 检查IntroC# 11 将引入一个新的操作符 !! 来简化我们代码中的对于参数的 null 检查&#xff0c;昨天发布的 .NET 7 Preview 1 已经支持了这一语法&#xff0c;感兴趣的不妨来试一下吧&#xff0c;下面我们就来看一下如何使用吧Prepare如果你想在本地代码中…

cms的 php代码,KingCMS/PHP可执行代码

实例:Example :{king:title/}{king:content/}作者:{king:_author/}上面的代码没有什么特别的地方&#xff0c;但客户的要求有了变化&#xff0c;他想在详细页的内容开始前调用缩略图&#xff0c;没有则忽略。所以问题也来了&#xff0c;因为有的文章有缩略图&#xff0c;有的没…

CentOS单机安装k8s并部署.NET 6程序

学习云原生&#xff0c;k8s 是一个基础&#xff0c;为了做一些实验&#xff0c;单机部署是最方便的&#xff0c;下面将介绍在 CentOS 中单机安装 k8s &#xff0c;并将一个 .NET 6 的程序发布到 k8s 中。环境宿主机&#xff1a;Mac 10.15.7CentOS版本&#xff1a;7.6内存&#…

php session缓存,扫盲:php session缓存至memcached中的方法

memcached是一套分布式的快取系统&#xff0c;当初是DangaInteractive为了LiveJournal所发展的&#xff0c;但被许多软件(如MediaWiki)所使用。这是一套开放源代码软件&#xff0c;以BSDlicens更改为&#xff1a;session.save_handler memcachesession.save_path"tcp://12…

MASA Framework - DDD设计(2)

Clean Architecture国内对于Clean Architecture的翻译很多&#xff0c;干净/整洁/清晰。但无论哪一种都说明了它简洁、清晰的特性。早期它长这样看到这张图的同学可能会对另外一张图有印象洋葱架构(Onion)现在长这样看起来好像是亲戚&#xff0c;它们的确也有着千丝万缕的关系分…

SpringMVC核心分发器DispatcherServlet分析[附带源码分析]

目录 前言DispatcherServlet初始化过程DispatcherServlet处理请求过程总结参考资料前言 SpringMVC是目前主流的Web MVC框架之一。 如果有同学对它不熟悉&#xff0c;那么请参考它的入门blog&#xff1a;http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 本…

WPF 展示视频修改为WriteableBitmap

WPF开发者QQ群&#xff1a;340500857由于微信群人数太多入群请添加小编微信号yanjinhuawechat 或 W_Feng_aiQ 邀请入群需备注WPF开发者 PS&#xff1a;有更好的方式欢迎推荐。接着上一篇&#xff0c;进行WriteableBitmap性能优化修改后运行对比如下&#xff1a;前&#xff08;C…

linux之类似Windows的资源管理器gnome-system-monitor(可用这个杀死进程)

1、使用 直接运行下面命令gnome-system-monitor 如果没有安装用下面命令安装sudo apt-get install gnome-system-monitor 2、结果 可以点击右键然后杀死相关进程&#xff0c;这也是杀死进程的办法。

HttpClient异常处理手册

HttpClient异常处理手册 开源中国 发表于 2014-08-26 19:44:06异常处理 HttpClient的使用者在执行HTPP方法&#xff08;GET,PUT,DELETE等&#xff09;&#xff0c;可能遇到会两种主要类型的异常&#xff1a; 传输异常协议异常并不是所有的异常都会传播给HttpClient的用户。Htt…

再读《精通css》02:选择器

2019独角兽企业重金招聘Python工程师标准>>> 1.2 为样式找到目标1、类型选择器用来选择特定类型的原素。比如p&#xff0c;a&#xff0c;h1等等。也叫元素选择器或简单选择器。2、后代选择器用来寻找特定元素或元素组的后代。后代选择器由两个选择器之间的空格表示。…

余弦欧式距离matlab,余弦相似度和欧几里得距离

1.余弦相似度同过两个向量的夹角的余弦值来判断两个向量的相似度。余弦值取值[-1,1],越接近1&#xff0c;两向量夹角越小&#xff0c;越相似。图片.png二维公式&#xff1a;图片.pngn维公式&#xff1a;图片.png存在的问题[1]&#xff1a;余弦相似度更多的是从方向上区分差异&a…

App Store 排名获取。

为什么80%的码农都做不了架构师&#xff1f;>>> https://affiliate.itunes.apple.com/resources/documentation/genre-mapping/ app榜示例 &#xff0c; 取中国免费榜前10条&#xff1a; 首先访问 https://itunes.apple.com/WebObjects/MZStoreServices.woa…

使用 Playwright 对 ASP.NET Core 应用执行功能测试

前言在前面的文章中&#xff0c;我们已经介绍过 Playwright for .NET&#xff0c;它常用于自动化测试已经部署好的 Web 应用。其实&#xff0c;开发人员也可以使用它在 ASP.NET Core 应用程序中进行功能测试。功能测试功能测试是从用户角度编写&#xff0c;用于基于其要求验证系…

PHP自动查找指定文件夹下所有文件BOM和删除所有文件

2019独角兽企业重金招聘Python工程师标准>>> <?php if (isset($_GET[dir])){ //设置文件目录 $basedir$_GET[dir]; }else{ $basedir .; } $auto 1; checkdir($basedir); function checkdir($basedir){ if ($dh opendir(…

php支持cs吗,关于composer、phpmd和phpcs于windows中的安装与使用方法

Composer项目地址 https://getcomposer.org中文 http://docs.phpcomposer.com/Composer是 PHP 的一个依赖管理工具。它允许你申明项目所依赖的代码库&#xff0c;它会在你的项目中为你安装他们。一、安装Composer官网有详细介绍安装方法&#xff0c;包括windows和linux系统。以…

基于ASP.NET Core api 的服务器事件发送

现如今程序员对Web API的调用已经是轻车熟路。但是传统的api调用都是拉模式&#xff0c;也就是主动发起请求去调用一个api.但是程序员往往对另一种很有用的模式很陌生&#xff0c;即推模式。拉模式 - 主动调用并获取结果的模式。推模式 - 订阅并接受数据推送的模式。今天要介绍…