如何从Windows服务打印PDF文档

我有Windows服务,必须定期从服务器打印PDF文档。 我的function是

private void PrintFormPdfData(byte[] formPdfData) { string tempFile; tempFile = Path.GetTempFileName(); using (FileStream fs = new FileStream(tempFile, FileMode.Create)) { fs.Write(formPdfData, 0, formPdfData.Length); fs.Flush(); } string pdfArguments = string.Format("/p /h\"{0}\"", tempFile); string pdfPrinterLocation = @"C:\Program Files (x86)\Adobe\Reader 9.0\Reader\AcroRd32.exe"; ProcessStartInfo newProcess = new ProcessStartInfo(pdfPrinterLocation, pdfArguments); newProcess.CreateNoWindow = true; newProcess.RedirectStandardOutput = true; newProcess.UseShellExecute = false; newProcess.RedirectStandardError = true; Process pdfProcess = new Process(); pdfProcess.StartInfo = newProcess; pdfProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; pdfProcess.Start(); pdfProcess.WaitForExit(); } 

当我在Windows应用程序中实现它时,它可以工作,但是当我在Windows服务中实现时它不起作用。

你能帮助我吗?

我用Session 0解决了问题。我使用这个类

  public class ProcessStarter : IDisposable { #region Import Section private static uint STANDARD_RIGHTS_REQUIRED = 0x000F0000; private static uint STANDARD_RIGHTS_READ = 0x00020000; private static uint TOKEN_ASSIGN_PRIMARY = 0x0001; private static uint TOKEN_DUPLICATE = 0x0002; private static uint TOKEN_IMPERSONATE = 0x0004; private static uint TOKEN_QUERY = 0x0008; private static uint TOKEN_QUERY_SOURCE = 0x0010; private static uint TOKEN_ADJUST_PRIVILEGES = 0x0020; private static uint TOKEN_ADJUST_GROUPS = 0x0040; private static uint TOKEN_ADJUST_DEFAULT = 0x0080; private static uint TOKEN_ADJUST_SESSIONID = 0x0100; private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY); private static uint TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID); private const uint NORMAL_PRIORITY_CLASS = 0x0020; private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400; private const uint MAX_PATH = 260; private const uint CREATE_NO_WINDOW = 0x08000000; private const uint INFINITE = 0xFFFFFFFF; [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public int nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; } public enum SECURITY_IMPERSONATION_LEVEL { SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation } public enum TOKEN_TYPE { TokenPrimary = 1, TokenImpersonation } public enum WTS_CONNECTSTATE_CLASS { WTSActive, WTSConnected, WTSConnectQuery, WTSShadow, WTSDisconnected, WTSIdle, WTSListen, WTSReset, WTSDown, WTSInit } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct STARTUPINFO { public Int32 cb; public string lpReserved; public string lpDesktop; public string lpTitle; public Int32 dwX; public Int32 dwY; public Int32 dwXSize; public Int32 dwYSize; public Int32 dwXCountChars; public Int32 dwYCountChars; public Int32 dwFillAttribute; public Int32 dwFlags; public Int16 wShowWindow; public Int16 cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] internal struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [StructLayout(LayoutKind.Sequential)] private struct WTS_SESSION_INFO { public Int32 SessionID; [MarshalAs(UnmanagedType.LPStr)] public String pWinStationName; public WTS_CONNECTSTATE_CLASS State; } [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern uint WTSGetActiveConsoleSessionId(); [DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern bool WTSQueryUserToken(int sessionId, out IntPtr tokenHandle); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public extern static bool DuplicateTokenEx(IntPtr existingToken, uint desiredAccess, IntPtr tokenAttributes, SECURITY_IMPERSONATION_LEVEL impersonationLevel, TOKEN_TYPE tokenType, out IntPtr newToken); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern bool CreateProcessAsUser(IntPtr token, string applicationName, string commandLine, ref SECURITY_ATTRIBUTES processAttributes, ref SECURITY_ATTRIBUTES threadAttributes, bool inheritHandles, uint creationFlags, IntPtr environment, string currentDirectory, ref STARTUPINFO startupInfo, out PROCESS_INFORMATION processInformation); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern bool CloseHandle(IntPtr handle); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern int GetLastError(); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern int WaitForSingleObject(IntPtr token, uint timeInterval); [DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern int WTSEnumerateSessions(System.IntPtr hServer, int Reserved, int Version, ref System.IntPtr ppSessionInfo, ref int pCount); [DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit); [DllImport("wtsapi32.dll", ExactSpelling = true, SetLastError = false)] public static extern void WTSFreeMemory(IntPtr memory); [DllImport("userenv.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment); #endregion ///  /// Initializes a new instance of the  class. ///  public ProcessStarter() { } ///  /// Initializes a new instance of the  class. ///  /// Name of the process. /// Full name of the exe. public ProcessStarter(string processName, string fullExeName) { processName_ = processName; processPath_ = fullExeName; } ///  /// Initializes a new instance of the  class. ///  /// Name of the process. /// Full name of the exe. /// The arguments. public ProcessStarter(string processName, string fullExeName, string arguments) { processName_ = processName; processPath_ = fullExeName; arguments_ = arguments; } ///  /// Gets the current user token. ///  ///  public static IntPtr GetCurrentUserToken() { IntPtr currentToken = IntPtr.Zero; IntPtr primaryToken = IntPtr.Zero; IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; int dwSessionId = 0; IntPtr hUserToken = IntPtr.Zero; IntPtr hTokenDup = IntPtr.Zero; IntPtr pSessionInfo = IntPtr.Zero; int dwCount = 0; WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref dwCount); Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO)); Int32 current = (int)pSessionInfo; for (int i = 0; i < dwCount; i++) { WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO)); if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State) { dwSessionId = si.SessionID; break; } current += dataSize; } WTSFreeMemory(pSessionInfo); bool bRet = WTSQueryUserToken(dwSessionId, out currentToken); if (bRet == false) { return IntPtr.Zero; } bRet = DuplicateTokenEx(currentToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken); if (bRet == false) { return IntPtr.Zero; } return primaryToken; } ///  /// Runs this instance. ///  public void Run() { IntPtr primaryToken = GetCurrentUserToken(); if (primaryToken == IntPtr.Zero) { return; } STARTUPINFO StartupInfo = new STARTUPINFO(); processInfo_ = new PROCESS_INFORMATION(); StartupInfo.cb = Marshal.SizeOf(StartupInfo); SECURITY_ATTRIBUTES Security1 = new SECURITY_ATTRIBUTES(); SECURITY_ATTRIBUTES Security2 = new SECURITY_ATTRIBUTES(); string command = "\"" + processPath_ + "\""; if ((arguments_ != null) && (arguments_.Length != 0)) { command += " " + arguments_; } IntPtr lpEnvironment = IntPtr.Zero; bool resultEnv = CreateEnvironmentBlock(out lpEnvironment, primaryToken, false); if (resultEnv != true) { int nError = GetLastError(); } CreateProcessAsUser(primaryToken, null, command, ref Security1, ref Security2, false, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, null, ref StartupInfo, out processInfo_); DestroyEnvironmentBlock(lpEnvironment); CloseHandle(primaryToken); } ///  /// Stops this instance. ///  public void Stop() { Process[] processes = Process.GetProcesses(); foreach (Process current in processes) { if (current.ProcessName == processName_) { current.Kill(); } } } ///  /// Waits for exit. ///  ///  public int WaitForExit() { WaitForSingleObject(processInfo_.hProcess, INFINITE); int errorcode = GetLastError(); return errorcode; } #region IDisposable Members ///  /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. ///  public void Dispose() { } #endregion private string processPath_ = string.Empty; private string processName_ = string.Empty; private string arguments_ = string.Empty; private PROCESS_INFORMATION processInfo_; ///  /// Gets or sets the process path. ///  /// The process path. public string ProcessPath { get { return processPath_; } set { processPath_ = value; } } ///  /// Gets or sets the name of the process. ///  /// The name of the process. public string ProcessName { get { return processName_; } set { processName_ = value; } } ///  /// Gets or sets the arguments. ///  /// The arguments. public string Arguments { get { return arguments_; } set { arguments_ = value; } } } 

现在,我的function看起来像:

  private void PrintFormPdfData(byte[] formPdfData) { string tempFile; tempFile = Path.GetTempFileName(); using (FileStream fs = new FileStream(tempFile, FileMode.Create)) { fs.Write(formPdfData, 0, formPdfData.Length); fs.Flush(); } string pdfArguments =string.Format("/t /o {0} \"Printer name\"", tempFile); string pdfPrinterLocation = @"C:\Program Files (x86)\Adobe\Reader 9.0\Reader\AcroRd32.exe"; try { ProcessStarter processStarter = new ProcessStarter("AcroRd32", pdfPrinterLocation, pdfArguments); processStarter.Run(); processStarter.WaitForExit(); processStarter.Stop(); } finally { File.Delete(tempFile); } } 

此外,ServiceProcessInstaller必须将Account设置为“LocalSystem”。 当我创建服务时,我使用此用户设置为“本地服务”它不起作用。 我没试过“网络服务”或“用户”

您没有提到操作系统,但我怀疑您遇到了在Windows Vista,Server 2008和后续操作系统版本中添加的Session 0隔离function

受此function影响的应用程序类包括:

创建UI的服务。

尝试使用窗口消息函数(如SendMessage和PostMessage)与应用程序通信的服务。

应用程序创建全局命名对象

我通过本文中的注册表编辑解决了这个问题: https : //support.microsoft.com/en-us/kb/184291 。

从Windows服务(以及IIS)打印使用我理解的SYSTEM帐户。 此帐户无权访问打印机,但这些注册表编辑为SYSTEM帐户提供了打印权限。

以下是摘要:

  1. 将3个密钥(“regedit.exe中的文件夹”)导出到.reg文件:
    • HKEY_CURRENT_USER \ Software \ Microsoft \ Windows NT \ Current Version \ Devices
    • HKEY_CURRENT_USER \ Software \ Microsoft \ Windows NT \ Current Version \ PrinterPorts
    • HKEY_CURRENT_USER \ Software \ Microsoft \ Windows NT \ Current Version \ Windows
  2. 编辑3个新创建的reg文件,并将“HKEY_CURRENT_USER”更改为“HKEY_USERS \ .DEFAULT”
  3. 通过在资源管理器中双击它们来运行3个reg文件(这会将它们添加到注册表中)

这就是全部。 这帮助我在IIS 7中从PHP打印pdf文件到打印机。只要服务器在脚本运行时碰巧让管理员登录,我的php脚本就可以正常工作。 现在没有人需要登录,脚本仍然有效:)

我希望这可以节省一些人的搜索和修补,就像我找到它一样。