4

I am trying to install virtual printer from C++ console program using WinAPI calls. It works fine on Windows XP, but on Windows 7 x64 there are some processes which lock files in system folders, required for install. I think they appear only on x64 Windows systems, but I haven't tested it with Windows XP x64.

These are processes splwow64.exe and PrintIsolationHost.exe. I tried to kill them programmaticly and it turns out well (well, for terminating PrintIsolationHost.exe I've set a Debug Privileges, 'coz it's system process) but I have started to think there is probably something wrong with my code if it doesn't work in this way. Apparently there must be some way of installing without terminating any system processes.

The code is something like that:

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)

The functions which install various stuff:

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;
}

There are more of them and some are really long, so I won't post it here if it's not needed.

Is there any way to prevent system lock files and force printer install?

P.S. I stop spooler service while copying files and then run it before any calls to WinAPI.
P.P.S It's not my code. It's legacy code which we need to maintain for the customer.

Roman Kruglov
  • 3,375
  • 2
  • 40
  • 46
  • Did you figure out a solution and would you be willing to share it? – agarcian Sep 01 '15 at 03:18
  • @agarcian it was a long ago, i don't remember what exact solution i've used, but i think it was the solution described in the single answer below - to use the registry branch and run an installer after reboot or to use MoveFileEx. – Roman Kruglov Sep 01 '15 at 16:46
  • Does this code install a virtual printer driver or does it emulate the virtual printer (ie when we give print the virtual driver should be listed under with the available printers) – Shameel Mohamed Apr 02 '17 at 19:04

1 Answers1

2

No, there's no way to prevent files from being locked. Even if you stop the spooler, splwow64 and everything else you can think of, there's still the possibility some other program will have one of your DLLs open. This is especially true since you're using UNIDRV because it's used by many other printer drivers.

The MoveFileEx function is the only reliable solution. If any of your file copies fail due to an access denied error, use MoveFileEx with the MOVEFILE_DELAY_UNTIL_REBOOT option and prompt the user to reboot. You can also put your installer in the registry RunOnce key (prefixed by an exclamation mark) to guarantee it will continue the install after the reboot. This will be a significant change to your installer, but it's the only reliable approach.

Carey Gregory
  • 6,836
  • 2
  • 26
  • 47
  • Does spoller work after reboot so I can call all API and install printer? – Roman Kruglov Oct 15 '11 at 05:02
  • Yes, the spooler will be started before your installer. – Carey Gregory Oct 15 '11 at 15:18
  • 2
    Pedantic note: Wouldn't the [RunOnce](http://msdn.microsoft.com/en-us/library/aa376977\(v=vs.85\).aspx) key (with the value prefixed by `!`) be a more appropriate place? – user786653 Oct 15 '11 at 16:20
  • Yes, it would. I should have said that so editing the answer. And +1 for mentioning the exclamation mark. I didn't know about that. – Carey Gregory Oct 15 '11 at 16:54
  • One more question: what registry brunch should I use - HKLM or HKCU to run installer with admin privileges? And how will my program interact with UAC after reboot? Maybe I should use RunOnce\Setup key? But it seems to work incorrect - I can't force it to work. But the sense of this key is max equal to what I need. – Roman Kruglov Oct 17 '11 at 10:47
  • I would use HKCU since you want the same person who initiated the install to finish it. UAC will behave the same after reboot. If it's enabled the user will see an elevation prompt. Just use the RunOnce key. – Carey Gregory Oct 17 '11 at 22:32