Ghostscript.NET - нет выходного файла при запуске в качестве службы Windows

Я пишу службу Windows для сканирования набора каталогов на наличие новых файлов PDF и преобразования их в TIFF с помощью Ghostscript.NET. Когда я скомпилировал и запустил код как обычную программу, он работал отлично, но когда я использовал тот же код в качестве службы, выходной TIFF никогда не отображался. Я установил целевой каталог, чтобы разрешить запись для всех, и исходный PDF-файл удаляется, как и предполагалось, поэтому это не должно быть проблемой разрешений для пользователя «Локальная система». Аудит каталога для доступа к ошибкам и успехам просто показывает список успехов.

Существует функция, которая считывает цветовую популяцию PDF-файла, чтобы определить, является ли он цветным документом или черно-белым, отсканированным как цветной. Эта часть работает, поэтому нет проблем с доступом и чтением PDF.

Я также попытался удалить «-q» из ключей Ghostscript, и у меня не было сообщений об ошибках, а «-dDEBUG» выводит столько мусора, что я не знаю, что он говорит, но ничего не помечено как ошибка.

public static void ConvertPDF(string file, GSvalues gsVals)
{

    gsProc = new Ghostscript.NET.Processor.GhostscriptProcessor();
    System.Collections.Generic.List<string> switches = new System.Collections.Generic.List<string>();
    switches.Add("-empty"); // GS.NET ignores the first switch
    switches.Add("-r" + gsVals.Resolution); // dpi
    switches.Add("-dDownScaleFactor=" + gsVals.ScaleFactor); // Scale the image back down
    switches.Add("-sCompression=lzw"); // Compression
    switches.Add("-dNumRenderingThreads=" + Environment.ProcessorCount);
    switches.Add("-c \"30000000 setvmthreshold\"");
    switches.Add("-dNOGC");

    string device;
    if (_checkPdf(file, gsVals.InkColorLevels, gsVals))
    {
        gsVals.WriteLog("Color PDF");
        device = "-sDEVICE=tiffscaled24"; // 24bit Color TIFF
    }
    else
    {
        gsVals.WriteLog("Grayscale PDF");
        device = "-sDEVICE=tiffgray"; // grayscale TIFF
    }    
    switches.Add(device);

    // Strip the filename out of the full path to the file
    string filename = System.IO.Path.GetFileNameWithoutExtension(file);
    // Set the output file tag
    string oFileName = _setFileName(oPath + "\\" + filename.Trim(), GSvalues.Extension);
    string oFileTag = "-sOutputFile=" + oFileName;
    switches.Add(oFileTag);
    switches.Add(file);

    // Process the PDF file
    try
    {
        string s = string.Empty;
        foreach (string sw in switches) s += sw + ' ';
            gsVals.DebugLog("Switches:\n\t" + s);

        gsProc.StartProcessing(switches.ToArray(), new GsStdio());
        while (gsProc.IsRunning) System.Threading.Thread.Sleep(1000);
    }
    catch (Exception e)
    {
         gsVals.WriteLog("Exception caught: " + e.Message);
         Console.Read();
    }

    gsVals.DebugLog("Archiving PDF");
    try
    {
        System.IO.File.Move(file, _setFileName(gsVals.ArchiveDir + "\\" + filename, ".pdf"));
    }
    catch (Exception e)
    {
        gsVals.WriteLog("Error moving PDF: " + e.Message);
    }
}


private static string _setFileName(string path, string tifExt)
    {
        if (System.IO.File.Exists(path + tifExt)) return _setFileName(path, 1, tifExt);
        else return path + tifExt;
    }

private static string _setFileName(string path, int ctr, string tifExt)
    {
        // Test the proposed altered filename. It it exists, move to the next iteration
        if(System.IO.File.Exists(path + '(' + ctr.ToString() + ')' + tifExt)) return _setFileName(path, ++ctr, tifExt);
        else return path + '(' + ctr.ToString() + ')' + tifExt;
    }

Это пример вывода сгенерированных переключателей (извлеченный из журнала вывода):

Switches: -empty -r220 -dDownScaleFactor=1 -sCompression=lzw -dNumRenderingThreads=4 -c "30000000 setvmthreshold" -dNOGC -sDEVICE=tiffscaled24 -sOutputFile=\\[servername]\amb_ops_scanning$\Test.tiff \\[servername]\amb_ops_scanning$\Test.pdf 

Настройки считываются в файле XML и сохраняются в классе GSVals. Класс также обрабатывает запись в системный журнал для вывода или в текстовый файл в обычной версии программы. GSSTDIO — это класс для обработки ввода и вывода GS, который просто перенаправляет весь вывод в те же журналы, что и GSVals. Единственным изменением кода между версией Программы и версией Службы является код обработки Службы, а вывод изменяется с текстового файла на системные журналы. В обработке Ghostscript ничего не изменилось.

Это скомпилировано как x86 для переносимости, но запускается на x64. Установлен GS 9.15, версии x86 и x64. GS.NET — это версия 4.0.30319, установленная через NuGet в VS 2012. ILMerge 2.13.0307 используется для упаковки DLL GS.NET в исполняемый файл, а также для переносимости. Ни одна из этих вещей не изменилась между обычным EXE и версиями службы Windows, и, как я уже сказал, обычный EXE работает без проблем.


person Scott    schedule 17.11.2014    source источник
comment
Скорее всего, для рендеринга требуется сеанс 0. Службы Windows блокируют сеанс 0 в целях безопасности.   -  person Aron    schedule 17.11.2014
comment
Учитывая это, спасибо ... сеанс ghostscript 0 в Google теперь показывает эту страницу как первое обращение :)   -  person Scott    schedule 17.11.2014
comment
Вы поставили галочку Разрешить сервису взаимодействовать с рабочим столом? Может стоит попробовать. stackoverflow.com/q/4237225/397817 Кроме того, службы Windows блокируют сеанс 0 в Google, первый результат: stackoverflow.com/q/17364932/397817 :)   -  person Stephen Kennedy    schedule 17.11.2014
comment
Взаимодействие с рабочим столом не помогло. Я нашел белую книгу MS, в котором представлены изменения в службе 0 в Vista+. Похоже, что все сервисы имеют сеанс 0, а сеансы пользователей — 1+. Поскольку сеанс 0 больше не является сеансом пользователя, службы, работающие в сеансе 0, не имеют доступа к видеодрайверу. Это означает, что любая попытка службы отобразить графику не удалась.   -  person Scott    schedule 17.11.2014
comment
Вы уверены, что ваша служба Windows имеет доступ к \[servername]\? Можете ли вы попробовать установить входной и выходной файл на локальный диск?   -  person HABJAN    schedule 17.11.2014
comment
Кроме того, попробуйте регистрировать сообщения Ghostscript stdio следующим образом: pastebin.com/1F5wCu8M   -  person HABJAN    schedule 17.11.2014
comment
Он может открыть исходный PDF-файл, который находится в том же каталоге, что и выходной путь, и архивирует PDF-файл (удаляет его) без ошибок. Таким образом, это должно указывать на то, что у него есть доступ R / W к общему ресурсу сервера.   -  person Scott    schedule 18.11.2014
comment
Мне придется повозиться с перенаправлением вывода GS из GS.NET, но я заработал, создав дополнительный процесс - см. мой ответ.   -  person Scott    schedule 18.11.2014
comment
Я не могу добраться до вашего изображения, драконовский веб-фильтр моей компании ненавидит реальную производительность. Но я перенаправил вывод Ghostscript.NET с помощью класса GsStdio, который просто печатает любой вывод в журналы. Я никогда ничего не видел, кроме как нажать любую клавишу, чтобы продолжить типы сообщений. Ваша ссылка похожа на -sstdout=[имя файла]?   -  person Scott    schedule 18.11.2014


Ответы (1)


Я заработал, используя CreateProcessAsUser() из advapi32.dll, используя код из эта статья.

Мне также пришлось перестроить порядок переключателей:

switches.Add("-c 30000000 setvmthreshold -f\"" + file + "\"")

Исходный источник, который я использовал для ускорения преобразования, не включал часть «-f», а также тот факт, что -f был тегом, помечающим файл. Я не знаю, почему это работало в GS.NET, но с обычным gswin32c.exe я получил сообщение об ошибке, говорящее, что это недопустимый файл, пока я не установил переключатель таким образом.

Как ни странно, процессы, создаваемые этим методом, по-прежнему являются сеансом 0, но на самом деле он работает. Я буду продолжать возиться, но пока это работает.

person Scott    schedule 18.11.2014
comment
-f не «помечает файл», он закрывает прямой ввод PostScript, который запускается с помощью «-c». Если вы не используете -c, вам не нужен -f.... - person KenS; 19.11.2014