最近有个需求需要对报告打印进行统一的管理,最终实现方案如下:
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进行安装部署。如下:
dism /Online /Enable-Feature /FeatureName:"Printing-PrintToPDFServices-Features" /NoRestart /Quiet使用CreateProcess函数执行dism.exe
#include<Windows.h>#include<tchar.h>BOOL InstallMicrosoftPrintToPDF(){LPWSTR szCmd = _tcsdup(LR"(dism /Online /Enable-Feature /FeatureName:"Printing-PrintToPDFServices-Features" /NoRestart /Quiet)");STARTUPINFO si{};PROCESS_INFORMATION pi{};si.cb = sizeof(si);auto nRet = CreateProcess(NULL, szCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);if (!nRet){if (pi.hThread){CloseHandle(pi.hThread);}if (pi.hProcess){CloseHandle(pi.hProcess);}}free(szCmd);return nRet;}
创建本地打印机端口
/// <summary>/// 创建本地打印机端口/// </summary>/// <returns></returns>BOOL CreateLocalPort(){LPWSTR szPortName = _tcsdup(L"C:\\test.pdf");LPPORT_INFO_2 pPrtInfo2 = NULL;DWORD pcbNeeded = 0;DWORD pcReturned = 0;//枚举本地打印机端口EnumPorts(NULL, 2, NULL, 0, &pcbNeeded, &pcReturned);//枚举本地打印机端口pPrtInfo2 = (LPPORT_INFO_2)LocalAlloc(LPTR, pcbNeeded);auto result = EnumPorts(NULL, 2, (LPBYTE)pPrtInfo2, pcbNeeded, &pcbNeeded, &pcReturned);if (!result || pPrtInfo2 == NULL)return FALSE;for (int i = 0; i < pcReturned; i++){if (wcscmp((pPrtInfo2 + i)->pPortName, szPortName) == 0)return TRUE;}HANDLE hPrinter = NULL;PRINTER_DEFAULTS printerDefaults{};printerDefaults.pDatatype = NULL;printerDefaults.pDevMode = NULL;printerDefaults.DesiredAccess = SERVER_ACCESS_ADMINISTER;LPWSTR szPrinterName = _tcsdup(L",XcvMonitor Local Port");result = OpenPrinter(szPrinterName, &hPrinter, &printerDefaults);if (!result || hPrinter == NULL){//查看错误//GetLastError();return FALSE;}DWORD dwPcbNeeded = 0;DWORD dwStatus = 0;result = XcvData(hPrinter, L"AddPort", (PBYTE)szPortName, (lstrlenW(szPortName) + 1) * sizeof(TCHAR), NULL, 0, &dwPcbNeeded, &dwStatus);if (!result){//GetLastError();return FALSE;}ClosePrinter(hPrinter);free(szPortName);free(szPrinterName);}
创建新虚拟打印机
创建端口后,调用EnumPrinters函数枚举打印机,找到Microsoft Print to PDF打印机。
拿到Microsoft Print to PDF的打印机参数后,其它参数不变,只更改打印机名称,调用AddPrinter创建一个新打印机。
再调用SetPrinter设置端口为刚创建的端口
/// <summary>/// 根据Microsoft Print To PDF创建新虚拟打印机/// </summary>/// <returns></returns>BOOL CreateVirtualPrinter(){LPTSTR szPrinterName = _tcsdup(L"虚拟打印机");LPTSTR szPortName = _tcsdup(L"C:\\test.pdf");DWORD pcbNeeded = 0;DWORD pcReturned = 0;auto result = EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 2, NULL, 0, &pcbNeeded, &pcReturned);LPPRINTER_INFO_2 pPrtInfo2 = (LPPRINTER_INFO_2)LocalAlloc(LPTR, pcbNeeded);result = EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 2, (LPBYTE)pPrtInfo2, pcbNeeded, &pcbNeeded, &pcReturned);if (!result || pPrtInfo2 == NULL)return FALSE;for (int i = 0; i < pcReturned; i++){LPPRINTER_INFO_2 p_curPrtInfo = pPrtInfo2 + i;if (wcscmp(p_curPrtInfo->pPrinterName, L"Microsoft Print to PDF") == 0){p_curPrtInfo->pPrinterName = szPrinterName;HANDLE hPrinter = AddPrinter(NULL, 2, (LPBYTE)p_curPrtInfo);LPPRINTER_INFO_2 p_tmpPrtInfo = NULL;DWORD dw_tmpNeeded = 0;GetPrinter(hPrinter, 2, (LPBYTE)p_tmpPrtInfo, dw_tmpNeeded, &dw_tmpNeeded);p_tmpPrtInfo = (LPPRINTER_INFO_2)LocalAlloc(LPTR, dw_tmpNeeded);result = GetPrinter(hPrinter, 2, (LPBYTE)p_tmpPrtInfo, dw_tmpNeeded, &dw_tmpNeeded);if (p_tmpPrtInfo == NULL || result == FALSE)return FALSE;p_tmpPrtInfo->pPortName = szPortName;result = SetPrinter(hPrinter, 2, (LPBYTE)p_tmpPrtInfo, 0);ClosePrinter(hPrinter);SetDefaultPrinter(szPrinterName);break;}}free(szPrinterName);free(szPortName);return TRUE;}
创建完成后,可以在设备和打印机里看到新创建出来 的打印机。

可以看到打印机默认输出位置为 C:\test.pdf
注意:不支持在系统盘根目录创建,正式使用时,请使用其它路径
此时我们再进行打印时,会默认将打印内容转换为PDF并输出到 C:\test.pdf
示例代码
https://github.com/zhaotianff/cnblog-demo-code/tree/main/CreateVirtualPrinterDemo
参考:
https://stackoverflow.com/questions/1325485/how-to-create-a-new-port-and-assign-it-to-a-printer
https://learn.microsoft.com/en-us/windows/win32/printdocs/printdocs-printing
你可能还想看:

01

02

03

04

05

06

07

08

09

10

Happy Time


Win开发者
[
粉丝微信群
].

github丨https://github.com/zhaotianff
Advertisement
欢迎转载

夜雨聆风