Как правильно установить виртуальный принтер с помощью WinAPI на Win x64?

Я пытаюсь установить виртуальный принтер из консольной программы C ++ с помощью вызовов WinAPI. Он отлично работает в Windows XP, но в Windows 7 x64 есть некоторые процессы, которые блокируют файлы в системных папках, необходимые для установки. Я думаю, что они появляются только в системах Windows x64, но я не тестировал их с 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;
}

Их больше, а некоторые очень длинные, поэтому я не буду публиковать их здесь, если они не нужны.

Есть ли способ предотвратить файлы блокировки системы и принудительную установку принтера?

P.S. Я останавливаю службу диспетчера очереди печати при копировании файлов, а затем запускаю ее перед любыми вызовами WinAPI.
P.P.S Это не мой код. Это унаследованный код, который нам нужно поддерживать для клиента.


person Roman Kruglov    schedule 14.10.2011    source источник
comment
Вы нашли решение, и готовы ли вы им поделиться?   -  person agarcian    schedule 01.09.2015
comment
@agarcian это было давно, я не помню, какое именно решение я использовал, но я думаю, что это было решение, описанное в единственном ответе ниже - использовать ветку реестра и запустить установщик после перезагрузки или использовать MoveFileEx .   -  person Roman Kruglov    schedule 01.09.2015
comment
Устанавливает ли этот код драйвер виртуального принтера или эмулирует виртуальный принтер (т.е. когда мы даем печать, виртуальный драйвер должен быть указан в списке доступных принтеров)   -  person Shameel Mohamed    schedule 02.04.2017


Ответы (1)


Нет, нет способа предотвратить блокировку файлов. Даже если вы остановите спулер, splwow64 и все остальное, о чем вы можете подумать, все равно существует вероятность, что какая-то другая программа откроет одну из ваших DLL. Это особенно верно, поскольку вы используете UNIDRV, потому что он используется многими другими драйверами принтера.

Функция MoveFileEx это единственное надежное решение. Если какая-либо из ваших копий файла не работает из-за ошибки отказа в доступе, используйте MoveFileEx с параметром MOVEFILE_DELAY_UNTIL_REBOOT и предложите пользователю перезагрузиться. Вы также можете поместить свой установщик в раздел реестра RunOnce (с префиксом восклицательного знака), чтобы гарантировать, что он продолжит установку после перезагрузки. Это будет значительным изменением для вашего установщика, но это единственный надежный подход.

person Carey Gregory    schedule 14.10.2011
comment
Работает ли споллер после перезагрузки, чтобы я мог вызвать все API и установить принтер? - person Roman Kruglov; 15.10.2011
comment
Да, диспетчер очереди печати будет запущен раньше вашего установщика. - person Carey Gregory; 15.10.2011
comment
Педантическое замечание: не стал бы RunOnce ключ (со значением с префиксом !) быть более подходящим местом? - person user786653; 15.10.2011
comment
Да, было бы. Я должен был сказать это, редактируя ответ. И +1 за упоминание восклицательного знака. Я не знал об этом. - person Carey Gregory; 15.10.2011
comment
Еще один вопрос: какую ветку реестра я должен использовать - HKLM или HKCU для запуска установщика с правами администратора? И как моя программа будет взаимодействовать с UAC после перезагрузки? Может стоит использовать ключ RunOnce \ Setup? Но вроде работает некорректно - заставить работать не могу. Но смысл этого ключа максимально равен тому, что мне нужно. - person Roman Kruglov; 17.10.2011
comment
Я бы использовал HKCU, поскольку вы хотите, чтобы тот же человек, который инициировал установку, завершил ее. UAC будет вести себя так же после перезагрузки. Если он включен, пользователь увидит запрос на повышение прав. Просто используйте клавишу RunOnce. - person Carey Gregory; 18.10.2011