Win32创建虚拟打印机

最近有个需求需要对报告打印进行统一的管理,最终实现方案如下:

1、安装Microsoft Print To PDF虚拟打印机,该打印机可以将所有打印数据转换为PDF

2、通过Microsoft Print To PDF虚拟机参数复制一台新的虚拟打印机

3、创建打印输出端口,指定输出路径

4、设置新虚拟打印机的端口为新创建的端口。

安装Microsoft Print To PDF

注意:仅支持Windows 10 及以上系统

Microsoft Print To PDF属于Windows可选功能,可以借助 dism.exe进行安装布署。如下:

1 dism /Online /Enable-Feature /FeatureName:"Printing-PrintToPDFServices-Features" /NoRestart /Quiet

使用CreateProcess函数执行dism.exe

 1 #include<Windows.h>2 #include<tchar.h>3 4 BOOL InstallMicrosoftPrintToPDF()5 {6     LPWSTR szCmd = _tcsdup(LR"(dism /Online /Enable-Feature /FeatureName:"Printing-PrintToPDFServices-Features" /NoRestart /Quiet)");7     STARTUPINFO si{};8     PROCESS_INFORMATION pi{};9     si.cb = sizeof(si);
10     auto nRet = CreateProcess(NULL, szCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
11 
12     if (!nRet)
13     {
14         if (pi.hThread)
15         {
16             CloseHandle(pi.hThread);
17         }
18 
19         if (pi.hProcess)
20         {
21             CloseHandle(pi.hProcess);
22         }
23     }
24 
25     free(szCmd);
26     return nRet;
27 }

创建本地打印机端口

 1 /// <summary>2 /// 创建本地打印机端口3 /// </summary>4 /// <returns></returns>5 BOOL CreateLocalPort()6 {7     LPWSTR szPortName = _tcsdup(L"C:\\test.pdf");8 9     LPPORT_INFO_2 pPrtInfo2 = NULL;
10     DWORD pcbNeeded = 0;
11     DWORD pcReturned = 0;
12 
13     //枚举本地打印机端口
14     EnumPorts(NULL, 2, NULL, 0, &pcbNeeded, &pcReturned);
15 
16 
17     //枚举本地打印机端口
18     pPrtInfo2 = (LPPORT_INFO_2)LocalAlloc(LPTR, pcbNeeded);
19     auto result = EnumPorts(NULL, 2, (LPBYTE)pPrtInfo2, pcbNeeded, &pcbNeeded, &pcReturned);
20 
21     if (!result || pPrtInfo2 == NULL)
22         return FALSE;
23 
24     for (int i = 0; i < pcReturned; i++)
25     {
26         if (wcscmp((pPrtInfo2 + i)->pPortName, szPortName) == 0)
27             return TRUE;
28     }
29 
30     HANDLE hPrinter = NULL;
31     PRINTER_DEFAULTS printerDefaults{};
32     printerDefaults.pDatatype = NULL;
33     printerDefaults.pDevMode = NULL;
34     printerDefaults.DesiredAccess = SERVER_ACCESS_ADMINISTER;
35 
36     LPWSTR szPrinterName = _tcsdup(L",XcvMonitor Local Port");
37 
38     result = OpenPrinter(szPrinterName, &hPrinter, &printerDefaults);
39 
40     if (!result || hPrinter == NULL)
41     {
42         //查看错误
43         //GetLastError();
44         return FALSE;
45     }
46 
47     DWORD dwPcbNeeded = 0;
48     DWORD dwStatus = 0;
49 
50     result = XcvData(hPrinter, L"AddPort", (PBYTE)szPortName, (lstrlenW(szPortName) + 1) * sizeof(TCHAR), NULL, 0, &dwPcbNeeded, &dwStatus);
51 
52     if (!result)
53     {
54         //GetLastError();
55         return FALSE;
56     }
57 
58     ClosePrinter(hPrinter);
59 
60     free(szPortName);
61     free(szPrinterName);
62 }

创建新虚拟打印机

创建端口后,调用EnumPrinters函数枚举打印机,找到Microsoft Print to PDF打印机。

拿到Microsoft Print to PDF的打印机参数后,其它参数不变,只更改打印机名称,调用AddPrinter创建一个新打印机。

再调用SetPrinter设置端口为刚创建的端口

 1 /// <summary>2 /// 根据Microsoft Print To PDF创建新虚拟打印机3 /// </summary>4 /// <returns></returns>5 BOOL CreateVirtualPrinter()6 {7     LPTSTR szPrinterName = _tcsdup(L"虚拟打印机");8     LPTSTR szPortName = _tcsdup(L"C:\\test.pdf");9 
10     DWORD pcbNeeded = 0;
11     DWORD pcReturned = 0;
12 
13     auto result = EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 2, NULL, 0, &pcbNeeded, &pcReturned);
14     LPPRINTER_INFO_2 pPrtInfo2 = (LPPRINTER_INFO_2)LocalAlloc(LPTR, pcbNeeded);
15     result = EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 2, (LPBYTE)pPrtInfo2, pcbNeeded, &pcbNeeded, &pcReturned);
16 
17     if (!result || pPrtInfo2 == NULL)
18         return FALSE;
19 
20     for (int i = 0; i < pcReturned; i++)
21     {
22         LPPRINTER_INFO_2 p_curPrtInfo = pPrtInfo2 + i;
23         if (wcscmp(p_curPrtInfo->pPrinterName, L"Microsoft Print to PDF") == 0)
24         {
25             p_curPrtInfo->pPrinterName = szPrinterName;
26             HANDLE hPrinter = AddPrinter(NULL, 2, (LPBYTE)p_curPrtInfo);
27             LPPRINTER_INFO_2 p_tmpPrtInfo = NULL;
28             DWORD dw_tmpNeeded = 0;
29             GetPrinter(hPrinter, 2, (LPBYTE)p_tmpPrtInfo, dw_tmpNeeded, &dw_tmpNeeded);
30             p_tmpPrtInfo = (LPPRINTER_INFO_2)LocalAlloc(LPTR, dw_tmpNeeded);
31             result = GetPrinter(hPrinter, 2, (LPBYTE)p_tmpPrtInfo, dw_tmpNeeded, &dw_tmpNeeded);
32 
33             if (p_tmpPrtInfo == NULL || result == FALSE)
34                 return FALSE;
35 
36             p_tmpPrtInfo->pPortName = szPortName;
37             result = SetPrinter(hPrinter, 2, (LPBYTE)p_tmpPrtInfo, 0);
38             ClosePrinter(hPrinter);
39             SetDefaultPrinter(szPrinterName);
40             break;
41         }
42     }
43 
44     free(szPrinterName);
45     free(szPortName);
46 
47     return TRUE;
48 }

创建完成后,可以在设备和打印机里看到新创建出来 的打印机。

可以看到打印机默认输出位置为 C:\test.pdf(注意:不支持在系统盘根目录创建,正式使用时,请使用其它路径)

 此时我们再进行打印时,会默认将打印内容转换为PDF并输出到 C:\test.pdf

 示例代码

参考:

c++ - How to create a new port and assign it to a printer - Stack Overflow

Printing (Documents and Printing) - Win32 apps | Microsoft Learn

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

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

相关文章

服务器数据恢复—LeftHand存储中raid5阵列多块磁盘离线的数据恢复案例

LeftHand存储支持RAID5、RAID6、RAID10磁盘阵列&#xff0c;同时还支持卷快照&#xff0c;卷动态扩容等。下面简单聊一下LeftHand存储的结构和一个LeftHand p4500存储中磁盘阵列数据恢复案例。 服务端&#xff1a; 客户端&#xff1a; LeftHand存储结构&#xff1a; Lefthand存…

C语言指针详解-包过系列(一)目录版

C语言指针详解-包过系列&#xff08;一&#xff09;目录版 1.内存和地址1.1内存1.2 深入理解编址 2.指针变量和地址2.1 取地址操作符&#xff08;&&#xff09;2.2 指针变量和解引用操作符&#xff08;*&#xff09;2.2.1 指针变量2.2.2 指针变量各部分理解2.2.3 解引用操作…

Oracle 常用函数大全

文章目录 一、空校验1. NVL 空校验2. COALESCE 空校验 二、排序1. ORDER BY 排序2. ORDER BY DECODE 指定值排序 三、排名1. RANK 排名2. DENSE RANK 密集排名 四、限制条数1. ROWNUM 限制2. FETCH 限制 五、字符串处理1. TO_CHAR 字符串转换2. || 字符串拼接3. CONCAT 字符串拼…

【Qt】颜色对话框QColorDialog

颜色对话框QColorDialog 颜⾊对话框的功能是允许⽤⼾选择颜⾊。继承⾃ QDialog 类。 Qt QColorDialog 的功能就是内置了调色板&#xff0c;效果和上图画图板的调色板类似。 常用方法介绍&#xff1a; QColorDialog (QWidget *parent nullptr) //创建对象的同时设置⽗对象Q…

获取navicat已保存数据库连接的密码

打开connections.ncx&#xff0c;可以看到Passwordxxx,这是加密后的密码 解密 在线的运行工具https://tool.lu/coderunner 运行如下代码&#xff0c;代码中的密码改成你的密码&#xff0c;在倒数第二行位置 <?phpnamespace FatSmallTools;class NavicatPassword{protected…

draw.io图片保存路径如何设置

当在draw.io中画图时&#xff0c;对于图片的保存经常选择了但是后面打开了又不知道图片的位置在哪&#xff0c;如下图所示&#xff0c;选择的途径很多。 为了之后存储文件较为简单&#xff0c;今天小编从源头设置图片的存储位置&#xff0c;使用浏览器设置的保存文件的路径。 …

2024年9月4日嵌入式学习

内存泄漏&#xff1a; 内存泄漏&#xff08;Memory Leak&#xff09;是指程序中已动态分配的内存由于某种原因程序未释放或无法释放&#xff0c;导致系统内存的浪费&#xff0c;严重时会导致程序运行缓慢甚至崩溃。这种情况在长时间运行的程序或大型系统中尤为常见&#xff0c;…

A02、Java编程性能调优(02)

1、Stream如何提高遍历集合效率 1.1、什么是Stream 现在很多大数据量系统中都存在分表分库的情况。例如&#xff0c;电商系统中的订单表&#xff0c;常常使用用户 ID 的 Hash 值来实现分表分库&#xff0c;这样是为了减少单个表的数据量&#xff0c;优化用户查询订单的速度。 …

2024.9.3 作业

自己实现栈和队列 代码&#xff1a; /*******************************************/ 文件名&#xff1a;sq.h /*******************************************/ #ifndef SQ_H #define SQ_H #include <iostream> #include<cstring>using namespace std; class …

Matlab R2022b使用Camera Calibrator工具箱张正友标定法进行相机标定附带标定前后对比代码

打开Camera Calibrator 在这添加你拍摄的图片 根据你每个方块的实际边长填写&#xff0c;我是15mm。 通俗一点&#xff0c;要k3就选3 Coefficients&#xff0c;否则为0&#xff1b;要p1、p2就选Tangential Distortion。然后进行计算。 可以点击右侧误差高的选中图像进行移…

【C/C++】C语言实现蛇形矩阵

目录 题目描述输入描述:输出描述:示例思路代码 题目描述 给你一个整数n&#xff0c;输出n∗n的蛇形矩阵。 输入描述: 输入一行&#xff0c;包含一个整数n 输出描述: 输出n行&#xff0c;每行包含n个正整数&#xff0c;通过空格分隔。 1<n<1000 示例 输入 4输出 …

iOS18 beta版本怎么回退至iOS17正式版本?

截止目前&#xff0c;苹果最近的iOS18的beta测试版本已经发了8版了&#xff0c;有许多朋友们都已升级提前尝鲜了&#xff0c;升级体验后许多果粉朋友们觉得有许多功能还是不够稳定&#xff0c;有些许bug&#xff0c;就想要降级&#xff0c;回退到iOS17的正式版&#xff0c;但又…

2024数博会技术成果回顾 | KPaaS助力企业数智化转型

2024年8月28日至30日&#xff0c;中国国际大数据产业博览会&#xff08;简称“数博会”&#xff09;在贵州省贵阳市隆重举行。本届数博会以“数智共生&#xff1a;开创数字经济高质量发展新未来”为主题&#xff0c;吸引了来自全球各地的400多家企业和2.1万余名嘉宾参与&#x…

AI自动生成PPT哪个软件好?如何自动生成专业级PPT?

新学期伊始&#xff0c;准备开学演讲稿的你是否还在为制作PPT而烦恼&#xff1f;别担心&#xff0c;现在有了AI的帮助&#xff0c;生成专业且吸引人的PPT变得轻而易举。 本文将为你揭秘4种高效的AI自动生成PPT的方法&#xff0c;让你在新学期的演讲中脱颖而出。无论是简洁明了…

Python | Leetcode Python题解之第391题完美矩形

题目&#xff1a; 题解&#xff1a; class Solution:def isRectangleCover(self, rectangles: List[List[int]]) -> bool:area, minX, minY, maxX, maxY 0, rectangles[0][0], rectangles[0][1], rectangles[0][2], rectangles[0][3]cnt defaultdict(int)for rect in rec…

glsl着色器学习 (十二)平移

平移和旋转、缩放是一样的&#xff0c;替换成平移矩阵即可&#xff1b; // 创建一个单位矩阵 const translateYMatrix mat4.create();// 沿着Y轴向下平移1个单位 mat4.fromTranslation(translateYMatrix, [0, -1, 0]);// 设置矩阵 gl.uniformMatrix4fv(matrixUniformLocation…

Linux进程状态 僵尸进程 孤儿进程

进程状态 R (running) Linux 的运行状态(循环空语句) S (sleeping) 阻塞状态 称为浅度睡眠(可以被唤醒)死循环打印语句时等待输入时使用 sleep 函数时 D (disk sleep) 也是一种阻塞状态&#xff0c;称为深度睡眠进程在等待磁盘写入完毕期间&#xff0c;这个进程不能被任何人杀…

2157. 优秀的拆分(power)

代码 #include<bits/stdc.h> using namespace std; int a[10001]; int main() {int n,t1,k0;bool flagfalse;cin>>n;if(n%21) {cout<<-1;return 0;}while(n>0){if(n%21){k;a[k]t; }nn/2;tt*2;}if(k>1) {flagtrue;for(int ik;i>1;i--)cout<&l…

oracle 数据库 day0823

ok了家人们&#xff0c;今天学习了orcle的基本用法&#xff0c;一日不见&#xff0c;如隔三秋啊&#xff0c; 一.多表联合查询 和之前学习的MySQL数据库一样的用法&#xff0c; 1.1 笛卡尔积查询 SELECT * FROM A表,B表 查询员工表和部门表 select * from emp e, dept d; e…

跨平台RTSP播放器之VLC Media Player还是SmartPlayer?

好多开发者纠结&#xff0c;RTSP流播放&#xff0c;到底是用开源的VLC Media Player还是大牛直播SDK的SmartPlayer&#xff1f;针对此&#xff0c;本文做个简单的技术探讨&#xff0c;方便开发者根据实际需要&#xff0c;做适合自己场景的选择&#xff1a; VLC Media Player …