2011-04-08 96 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腳本就可以正常工作。現在沒有人需要登錄,劇本仍然有效:)

我希望這可以節省一些人的搜索和修補程序,因爲我找到了它。