如何在 Win x64 上使用 WinAPI 正确安装虚拟打印机?

Posted

技术标签:

【中文标题】如何在 Win x64 上使用 WinAPI 正确安装虚拟打印机?【英文标题】:How to properly install virtual printer using WinAPI on Win x64? 【发布时间】:2011-10-14 17:36:48 【问题描述】:

我正在尝试使用 WinAPI 调用从 C++ 控制台程序安装虚拟打印机。它在 Windows XP 上运行良好,但在 Windows 7 x64 上,有一些进程会锁定系统文件夹中的文件,这是安装所必需的。我认为它们只出现在 x64 Windows 系统上,但我还没有在 Windows XP x64 上测试过。

这些是进程 splwow64.exe 和 PrintIsolationHost.exe。我试图以编程方式杀死它们,结果很好(好吧,为了终止 PrintIsolationHost.exe,我设置了一个调试权限,'因为它是系统进程)但我开始认为我的代码可能有问题,如果它没有不要以这种方式工作。显然必须有某种安装方式,而无需终止任何系统进程。

代码是这样的:

BOOL res = FALSE;
printf("Run install:\n\n");

// Set debug privilages to current process
HANDLE hTokenThis( NULL );
OpenProcessToken( GetCurrentProcess(), TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hTokenThis );
SetPrivilege( hTokenThis, SE_DEBUG_NAME, TRUE );

printf("Stop spooler service...\r\n");
if(!StopService("spooler"))
    return FALSE;

// Stop splwow64.exe and PrintIsolationHost.exe - it can prevent some files to be copied
HANDLE process = GetProcessByName( "splwow64.exe" );
if (process)
    TerminateProcess( process, 0 );

// Stop PrintIsolationHost.exe (it runs under SYSTEM and requires debug rights to be stopped)
process = GetProcessByName( "PrintIsolationHost.exe" );
if (process)
    TerminateProcess( process, 0 );

// Continue install
printf("Copy driver file...\r\n");
if(!CopyInstFile())

    return FALSE;

printf("Start spooler service...\r\n");
if(!StartServices("spooler"))
    return FALSE;

printf("Add Port...\r\n");
res = AddPort();
ERROR_CHECK_EXIT(res)

printf("Add Driver...\r\n");
res = AddDriver();
ERROR_CHECK_EXIT(res)

printf("Add print Processor...\r\n");
res = AddProcessor();
ERROR_CHECK_EXIT(res)

printf("Add printer...\r\n");
res = AddPrint();
ERROR_CHECK_EXIT(res)

安装各种东西的功能:

BOOL CPrintInstal::AddDriver()

    DRIVER_INFO_3  driverInfo;
    memset(&driverInfo,0,sizeof(driverInfo ));
    driverInfo.cVersion = 3;
    driverInfo.pName = PRINTERDRIVERNAME;
    driverInfo.pEnvironment = NULL;//"Windows NT x86";
    driverInfo.pDriverPath="UNIDRV.DLL";
    driverInfo.pDataFile=PDFCONVERTED_GPD;
    driverInfo.pConfigFile= "UNIDRVUI.DLL";
    driverInfo.pHelpFile= "UNIDRV.HLP";
    driverInfo.pDependentFiles = NULL;
    driverInfo.pDefaultDataType=NULL;

    return AddPrinterDriver(NULL,3,(LPBYTE)&driverInfo);


BOOL CPrintInstal::AddPrint()

    PRINTER_INFO_2 printInfo;
    memset(&printInfo,0,sizeof(PRINTER_INFO_2));
    printInfo.pServerName=NULL;
    printInfo.pPrinterName=PRINTERNAME;
    printInfo.pShareName=NULL;
    printInfo.pPortName=PORTNAME_A;
    printInfo.pPrintProcessor =PRINTPROCESSORNAME;
    printInfo.pDatatype = "NT EMF 1.008";
    printInfo.pDriverName =PRINTERDRIVERNAME; 
    printInfo.Attributes = PRINTER_ATTRIBUTE_LOCAL | PRINTER_ATTRIBUTE_QUEUED | PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS;
    SetLastError(0);
    HANDLE handle = AddPrinter(NULL,2,(LPBYTE)&printInfo);
    if(handle == NULL)
    
        if(GetLastError()!=1802)
            return FALSE;   
    
    ClosePrinter(handle);
    return TRUE;

有的比较多,有的比较长,没用的就不贴了。

有什么办法可以防止系统锁定文件并强制安装打印机?

附:我在复制文件时停止后台处理程序服务,然后在调用 WinAPI 之前运行它。 P.P.S 这不是我的代码。这是我们需要为客户维护的遗留代码。

【问题讨论】:

你找到解决方案了吗?愿意分享吗? @agarcian 这是很久以前的事了,我不记得我用过什么确切的解决方案,但我认为这是下面单个答案中描述的解决方案 - 使用注册表分支并运行重启后的安装程序或使用 MoveFileEx。 此代码是安装虚拟打印机驱动程序还是模拟虚拟打印机(即,当我们打印时,虚拟驱动程序应与可用打印机一起列在下方) 【参考方案1】:

不,没有办法防止文件被锁定。即使您停止了 spooler、splwow64 和您能想到的所有其他程序,其他程序仍有可能打开您的 DLL 之一。尤其如此,因为您使用的是 UNIDRV,因为许多其他打印机驱动程序都在使用它。

MoveFileEx 函数是唯一可靠的解决方案。如果您的任何文件副本由于访问被拒绝错误而失败,请使用带有 MOVEFILE_DELAY_UNTIL_REBOOT 选项的 MoveFileEx 并提示用户重新启动。您还可以将安装程序放入注册表的 RunOnce 键(以感叹号为前缀),以保证在重新启动后它会继续安装。这将对您的安装程序进行重大更改,但这是唯一可靠的方法。

【讨论】:

spoller 重启后是否工作,以便我可以调用所有 API 并安装打印机? 是的,后台打印程序将在您的安装程序之前启动。 迂腐提示:RunOnce 键(值以! 为前缀)不是更合适的地方吗? 是的,它会的。我应该这么说,所以编辑答案。 +1 用于提及感叹号。我不知道。 还有一个问题:我应该使用哪个注册表早午餐 - HKLM 或 HKCU 以管理员权限运行安装程序?重启后我的程序将如何与 UAC 交互?也许我应该使用 RunOnce\Setup 键?但它似乎工作不正确 - 我不能强迫它工作。但是这把钥匙的感觉是max等于我需要的。

以上是关于如何在 Win x64 上使用 WinAPI 正确安装虚拟打印机?的主要内容,如果未能解决你的问题,请参考以下文章

正确在win10上编译openssl x64并且运行

winapi - 如何正确使用 LayeredWindows

以最小化状态启动 chromium 浏览器 (Win32/WinAPI)

子类化组合框时如何抑制自动搜索(Win32/WinAPI)

在 Win8 x64 上使用 Sql Compact CE 的 Windows Mobile

如何正确建立和指定的配置和平台,x64和x86