2011-04-08 94 views
7

我有必须定期从服务器打印PDF文档的Windows服务。我的作用是如何从Windows服务打印PDF文档

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服务实现它不起作用。

你能帮我吗?

+0

什么“不起作用”?你有错误吗? – 2011-04-08 14:15:59

回答

10

我解决会话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 

    /// <summary> 
    /// Initializes a new instance of the <see cref="ProcessStarter"/> class. 
    /// </summary> 
    public ProcessStarter() 
    { 

    } 

    /// <summary> 
    /// Initializes a new instance of the <see cref="ProcessStarter"/> class. 
    /// </summary> 
    /// <param name="processName">Name of the process.</param> 
    /// <param name="fullExeName">Full name of the exe.</param> 
    public ProcessStarter(string processName, string fullExeName) 
    { 
     processName_ = processName; 
     processPath_ = fullExeName; 
    } 

    /// <summary> 
    /// Initializes a new instance of the <see cref="ProcessStarter"/> class. 
    /// </summary> 
    /// <param name="processName">Name of the process.</param> 
    /// <param name="fullExeName">Full name of the exe.</param> 
    /// <param name="arguments">The arguments.</param> 
    public ProcessStarter(string processName, string fullExeName, string arguments) 
    { 
     processName_ = processName; 
     processPath_ = fullExeName; 
     arguments_ = arguments; 
    } 

    /// <summary> 
    /// Gets the current user token. 
    /// </summary> 
    /// <returns></returns> 
    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; 
    } 

    /// <summary> 
    /// Runs this instance. 
    /// </summary> 
    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); 
    } 

    /// <summary> 
    /// Stops this instance. 
    /// </summary> 
    public void Stop() 
    { 
     Process[] processes = Process.GetProcesses(); 
     foreach (Process current in processes) 
     { 
      if (current.ProcessName == processName_) 
      { 
       current.Kill(); 
      } 
     } 
    } 

    /// <summary> 
    /// Waits for exit. 
    /// </summary> 
    /// <returns></returns> 
    public int WaitForExit() 
    { 
     WaitForSingleObject(processInfo_.hProcess, INFINITE); 
     int errorcode = GetLastError(); 
     return errorcode; 
    } 



    #region IDisposable Members 

    /// <summary> 
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 
    /// </summary> 
    public void Dispose() 
    { 
    } 

    #endregion 

    private string processPath_ = string.Empty; 
    private string processName_ = string.Empty; 
    private string arguments_ = string.Empty; 
    private PROCESS_INFORMATION processInfo_; 

    /// <summary> 
    /// Gets or sets the process path. 
    /// </summary> 
    /// <value>The process path.</value> 
    public string ProcessPath 
    { 
     get 
     { 
      return processPath_; 
     } 
     set 
     { 
      processPath_ = value; 
     } 
    } 

    /// <summary> 
    /// Gets or sets the name of the process. 
    /// </summary> 
    /// <value>The name of the process.</value> 
    public string ProcessName 
    { 
     get 
     { 
      return processName_; 
     } 
     set 
     { 
      processName_ = value; 
     } 
    } 

    /// <summary> 
    /// Gets or sets the arguments. 
    /// </summary> 
    /// <value>The arguments.</value> 
    public string Arguments 
    { 
     get 
     { 
      return arguments_; 
     } 
     set 
     { 
      arguments_ = value; 
     } 
    } 
} 

现在的问题,我的功能看起来像:

 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必须有帐户设置为 “本地系统”。当我创建服务时,我将此用户设置为“本地服务”,但它不起作用。我没有尝试使用“网络服务”或“用户”

1

你没有提到的操作系统,但我怀疑你正在运行到这是在Windows Vista中添加的Session 0 isolation功能,Server 2008和随后的OS版本

受此功能应用类包括:

创建UI的服务。

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

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

+0

我目前在Windows 7上运行,但后来我需要在Windows 7或Vista上运行XP。当我运行或调试此功能时,我没有任何错误。 – cashmere 2011-04-08 14:38:46

+0

是的,它不会抛出错误,但在运行时不会看到预期的结果。但是,您是否尝试过在Windows XP计算机上查看打印是否真正起作用,并确认这是否为会话0,因为Windows XP没有S0隔离 – kd7 2011-04-08 14:40:41

+0

如何解决会话0问题? – cashmere 2011-04-08 15:24:14

1

我使用本文中的注册表编辑解决了此问题:https://support.microsoft.com/en-us/kb/184291

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

这里是一个总结:

  1. 出口3个键( “器regedit.exe中的文件夹”),以reg文件:
    • HKEY_CURRENT_USER \ SOFTWARE \微软\的Windows NT \当前版本\设备
    • HKEY_CURRENT_USER \ SOFTWARE \微软\的Windows NT \当前版本\ PrinterPorts
    • HKEY_CURRENT_USER \ SOFTWARE \微软\的Windows NT \当前版本\ Windows下
  2. 编辑您的3个新创建的REG文件,并改变“HKEY_CURRENT_USER”到“HKEY_USERS \ .DEFAULT”
  3. 通过双击它们在资源管理器中运行3个REG文件(此将它们添加到注册表)

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

我希望这可以节省一些人的搜索和修补程序,因为我找到了它。