【问题标题】:Ghostscript.NET - no output file when run as Windows serviceGhostscript.NET - 作为 Windows 服务运行时没有输出文件
【发布时间】:2014-11-19 02:35:06
【问题描述】:

我正在编写一个 Windows 服务来扫描一组目录以查找新的 PDF 文件,并使用 Ghostscript.NET 将它们转换为 TIFF。当我将代码作为普通程序编译并运行时,它运行良好,但是当我将相同的代码用作服务时,输出 TIFF 永远不会出现。我已将目标目录设置为允许为每个人编写,并且原始 PDF 已按预期删除,因此对于“本地系统”用户而言,这不应该是权限问题。审核访问失败和成功的目录仅显示成功列表。

有一个功能可以读取 PDF 的颜色填充,以确定它是彩色文档还是彩色扫描的黑白文档。该部分有效,因此访问和阅读 PDF 没有问题。

我还尝试从 Ghostscript 开关中删除“-q”,我没有报告任何错误,“-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 是通过 NuGet 安装到 VS 2012 中的 4.0.30319 版本。ILMerge 2.13.0307 用于将 GS.NET dll 打包到 exe 中,也是为了便于移植。这些东西在普通 EXE 和 Windows 服务版本之间都没有改变,正如我所说,普通 EXE 可以正常工作。

【问题讨论】:

  • 可能需要会话 0 进行渲染。出于安全目的,Windows 服务会阻止会话 0。
  • 调查一下,谢谢... Google 中的“ghostscript session 0”现在将此页面显示为第一次点击 :)
  • 您是否勾选了“允许服务与桌面交互。”?也许值得尝试一下。 stackoverflow.com/q/4237225/397817 另外,Google 中的“Windows 服务阻止会话 0”,第一个结果:stackoverflow.com/q/17364932/397817 :)
  • 与桌面交互没有帮助。我找到了一个 MSWhite Paper,它列出了 Vista+ 中服务 0 的变化。看起来所有服务都是会话 0,而用户会话是 1+。 “由于 Session 0 不再是用户会话,因此在 Session 0 中运行的服务无法访问视频驱动程序。这意味着服务渲染图形的任何尝试都会失败。”
  • 您确定您的 Windows 服务有权访问 \[servername]\?您可以尝试将输入和输出文件设置到本地驱动器吗?

标签: c# ghostscript ghostscript.net


【解决方案1】:

我通过使用来自 advapi32.dll 的 CreateProcessAsUser() 以及来自 this article 的代码使其工作。

我还必须重新调整开关的顺序:

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

我用来加速转换的原始来源省略了“-f”部分,以及 -f 是标记文件的标签这一事实。我不知道为什么这在 GS.NET 中有效,但是使用正常的 gswin32c.exe 我收到一个错误,说它是一个无效文件,直到我以这种方式设置开关。

奇怪的是,这个方法创建的进程仍然是 Session 0,但它确实有效。我会继续修补,但现在它正在工作。

【讨论】:

  • -f 不会“标记文件”,它会关闭以“-c”开头的直接 PostScript 输入。如果不使用 -c,则不需要 -f....
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-03
  • 1970-01-01
  • 2013-05-29
  • 1970-01-01
  • 2011-09-08
  • 2018-02-12
相关资源
最近更新 更多