2011-04-08 17 views
7

Tengo un servicio de Windows que debe imprimir periódicamente documentos PDF desde el servidor. Mi función esCómo imprimir documentos PDF desde el Servicio de Windows

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(); 
} 

cuando implemente esta en ventanas de aplicación que funciona, pero cuando implemente en el servicio de Windows no funciona.

¿Me puede ayudar?

+0

¿Qué "no funciona"? ¿Estás obteniendo un error? –

Respuesta

10

que resolver un problema en esta sesión 0. utilizo esta clase

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; 
     } 
    } 
} 

Ahora, mi mirada función como:

 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); 
     } 

} 

Además, ServiceProcessInstaller debe tener cuenta establecida en "LocalSystem". Cuando creé el servicio, configuré el "Servicio local" con este usuario, pero no funciona. No probé con "Servicio de red" o "Usuario"

1

usted no mencionó el sistema operativo, pero sospecha que se está ejecutando en la función Session 0 isolation que se agregó en Windows Vista, Server 2008 y posterior OS comunicados

clases de aplicación afectadas por esta función son:

Servicios que crean la interfaz de usuario.

Un servicio que intenta usar funciones de mensaje de ventana como SendMessage y PostMessage para comunicarse con una aplicación.

Aplicaciones que crean objetos con nombre global.

+0

Actualmente me estoy ejecutando en Windows 7. Pero más tarde tendré que ejecutar en XP nad Windows 7 o Vista. No tengo ningún error cuando ejecuto o depuro esta función. – cashmere

+0

Sí, no lanzará y error pero no verá los resultados esperados en tiempo de ejecución. Sin embargo, ¿ha probado en una máquina con Windows XP para ver si la impresión realmente funciona, que confirmará si esto es una cosa de la sesión 0, porque Windows XP no tiene aislamiento S0 – kd7

+0

¿Cómo puedo resolver el problema con la sesión 0? – cashmere

1

He resuelto este problema con las ediciones de registro encontradas en este artículo: https://support.microsoft.com/en-us/kb/184291.

La impresión desde los servicios de Windows (y también IIS) usa la cuenta de SYSTEM tal como lo entiendo. Esta cuenta no tiene acceso a la impresora, pero estas ediciones de registro otorgan los privilegios de impresión de la cuenta SYSTEM.

Aquí se presenta un resumen:

  1. exportación de 3 teclas ("carpetas dentro regedit.exe") a archivos .reg:
    • HKEY_CURRENT_USER \ Software \ Microsoft \ Windows NT \ Current Version \ Dispositivos
    • HKEY_CURRENT_USER \ Software \ Microsoft \ Windows NT \ Current Version \ PrinterPorts
    • HKEY_CURRENT_USER \ Software Version \ Microsoft \ Windows NT \ Current \ Windows
  2. editar sus archivos reg 3 de nueva creación y cambio "HKEY_CURRENT_USER" a "HKEY_USERS \ DEFAULT"
  3. ejecutar su reg 3 archivos haciendo doble clic en ellos en el explorador (esto les añade al registro)

Eso es todo lo que se necesita. Esto me ayudó a imprimir archivos PDF a una impresora desde PHP en IIS 7. Mi script php funcionaría perfectamente siempre y cuando el servidor tuviera la sesión iniciada por el administrador cuando se ejecutara el script.Ahora nadie necesita iniciar sesión y la secuencia de comandos todavía funciona :)

Espero que esto le ahorre a alguien tanto la búsqueda y retoques como lo puse para encontrarlo.

Cuestiones relacionadas