1. 打印机处理器(PrintProcessor)
BOOL bRet = TRUE;
CHAR pDriverDirectory[MAX_PATH];
DWORD pcbNeeded;
//获取打印机驱动程序位置
if(FALSE == GetPrintProcessorDirectory(NULL, "Windows x64", 1, (LPBYTE)&pDriverDirectory, MAX_PATH, &pcbNeeded)){
//Error
return -1;
}
//拷贝文件...
//添加打印机处理器
if(FALSE == AddPrintProcessor(NULL, NULL, "printProcessor.dll", "printProcessor")){
//Error
return -1;
}
PrintProcessor最主要的是处理PrintDocumentOnPrintProcessor函数,修改EMF的处理方法,这里希望将读取的打印机中间文件spl转成jpg图片。
参考论文《EMF虚拟打印机实现》(李朝中)中的PrintDocumentOnPrintProcessor函数和SaveSplFile函数。
SPL文件保存后,接下来就是将SPL文件解析成EMF文件。
这里可以参考网上spltoemfex类的实现,调用SplToEmf函数转成MEF文件。
将EMF转JPG文件需要使用GDI+,参考https://blog.csdn.net/numen27/article/details/25642985(将EMF文件转换成JPG文件)
添加打印机需要管理器权限,因此最好将UAC,在“连接器”:“清单文件”中设置。
如果打印机驱动程序已经存在了,可能会出现“另一个程序正在使用此文件”的错误。因此需要先结束Print Spool服务,再在最后启动服务,启动和结束服务的例子可以参考网上博文:https://blog.csdn.net/njbling1/article/details/52662936?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7
如果打印机驱动程序已经存在,不会出现报错。
2. 添加端口监视器
MONITOR_INFO_2 MonitorInfo = {0};
MonitorInfo.pName = "monitor";
MonitorInfo.pEnvironment = "Windows x64";
MonitorInfo.pDLLName = "localmon.dll";//SYSTEM
if(FALSE == AddMonitor(NULL, 2, (LPBYTE)&MonitorInfo)){
return FALSE;
}
添加打印机的dll需要放在SYSTEM32路径下,分别包括localmon、localui、pjlmon,具体代码参照dkk。
如果打印监视器已经安装,AddMonitor会返回False。
3. 添加端口
if(FALSE == AddPort(NULL, NULL, "monitor"))
{
//Error
return -1;
}
调用此函数时,会弹出localui中的对话框,但正常的处理程序一般不应让用户手动输入端口名称,可以修改AddPortUI函数,同时需要注意的是:
-
ddklocalmon.dll中有ddklocalui.dll的引用,同样需要修改,才能保证ddklocalui.dll正确被引用。
-
AddPortUI赋值字符串使用AllocSplStr函数。
-
前面说在替换文件前删除dll可能会删除失败,错误码5,需要如果检查到这种情况,需要多删除几次。
4. 添加驱动
DRIVER_INFO_4 DriverInfo = {0};
DriverInfo.cVersion = 3;
DriverInfo.pName = _T("driver");
DriverInfo.pEnvironment = _T("Windows x64"); // Win32 x86
DriverInfo.pDriverPath = _T("unidrv.dll");
DriverInfo.pDataFile = _T("emf.gpd");
DriverInfo.pConfigFile = _T("unidrvui.dll");
DriverInfo.pHelpFile = _T("unidrv.hlp");
DriverInfo.pDependentFiles = _T("UNIRES.DLL\0STDNAMES.GPD\0\0");
DriverInfo.pMonitorName = NULL; // "PJL monitor"
DriverInfo.pDefaultDataType = _T("EMF"); // "EMF"
DriverInfo.pszzPreviousNames = _T("\0\0"); // "OldName1\0OldName2\0\0
if(FALSE == AddPrinterDriver (NULL, 4, (LPBYTE)&DriverInfo)){
showErrMsg("AddPort");
return -1;
}
驱动地址可以通过GetPrintProcessorDirectory函数获取。
BOOL bRet = TRUE;
CHAR pPrintProcessorDirectory[MAX_PATH];
DWORD pcbNeeded;
//获取打印机驱动程序位置
if(FALSE == GetPrintProcessorDirectory(NULL, "Windows x64", 1, (LPBYTE)&pPrintProcessorDirectory, MAX_PATH, &pcbNeeded)){
showErrMsg("GetPrintProcessorDirectory");
return -1;
}
5. 添加打印机
PRINTER_INFO_2 PrinterInfo = {0};
PrinterInfo.pPrinterName = _T("printer 1.0");
PrinterInfo.pPortName = _T("port");
PrinterInfo.pDriverName = _T("driver");
PrinterInfo.pComment = _T("printer 1.0");
PrinterInfo.pLocation = _T("Anywhere");
PrinterInfo.pPrintProcessor = _T("printProcessor");
PrinterInfo.pDatatype = _TEXT("NT EMF 1.008");
if(FALSE == AddPrinter (NULL, 2, (LPBYTE)&PrinterInfo)){
showErrMsg("AddPrinterDriver");
return FALSE;
}