2010-10-19 7 views
0

Je développe une application intranet ASP.NET MVC2 pour une petite entreprise. La petite entreprise a plusieurs types d'imprimantes et en fonction de ce qui est requis, une demande d'impression sera envoyée (du navigateur/utilisateur) au serveur et le serveur enverra le travail d'impression à la bonne imprimante en conséquence. S'il vous plaît noter que c'est un environnement complètement nouveau pour eux et j'ai le contrôle sur à peu près tout. Je vais plus que probablement utiliser un système d'exploitation très léger (peut-être Asus ExpressGate ou Chrome OS en fonction de la date de lancement?) Afin que les utilisateurs ne peuvent pas avoir d'imprimantes installées, mais le serveur aura tout configuré.Impression avec C#/ASP.NET MVC2

Voici ma question:

Y at-il un moyen simple d'imprimer à partir du côté serveur (sans dialogue bien sûr parce qu'il n'y aura plus personne en attente de cliquer dessus) une page en utilisant le lien html comme un paramètre et en gardant le format HTML bien sûr.

J'ai vu quelques possibilités de choses COM là-bas mais s'il y a des possibilités pour éviter cela en utilisant une classe .net j'apprécierais. J'utilise .net 4.0. Je prendrai cependant toutes les suggestions, même si elle est basée sur COM. S'il vous plaît noter que toute solution de contournement logique serait également pris en compte, un exemple rapide (pas encore étudié) serait de transférer ce fichier HTML dans un fichier doc et l'envoi de ce fichier à l'imprimante.

Code d'édition retiré pour cause d'inutilisation.

Edits2: suivant ce lien: Print html document from Windows Service in C# without print dialog

solution sainte graal de Vadim fonctionne. Cependant, il a des limitations comme l'utilisation de l'imprimante par défaut uniquement. Je suis en train de modifier l'imprimante par défaut avant que l'impression se produise, ce qui entraîne l'impression à la bonne imprimante. Je peux voir des problèmes d'adoption qui se passe ici, mais jusqu'à présent c'est le meilleur que je suis venu avec (la plupart de ce code est de Vadim et je lui donne tout le crédit pour cela):

/// <summary>Provides a scheduler that uses STA threads.</summary> 
public sealed class StaTaskScheduler : TaskScheduler, IDisposable 
{ 
    /// <summary>Stores the queued tasks to be executed by our pool of STA threads.</summary> 
    private BlockingCollection<Task> _tasks; 
    /// <summary>The STA threads used by the scheduler.</summary> 
    private readonly List<Thread> _threads; 

    /// <summary>Initializes a new instance of the StaTaskScheduler class with the specified concurrency level.</summary> 
    /// <param name="numberOfThreads">The number of threads that should be created and used by this scheduler.</param> 
    public StaTaskScheduler(int numberOfThreads) 
    { 
     // Validate arguments 
     if (numberOfThreads < 1) throw new ArgumentOutOfRangeException("concurrencyLevel"); 

     // Initialize the tasks collection 
     _tasks = new BlockingCollection<Task>(); 

     // Create the threads to be used by this scheduler 
     _threads = Enumerable.Range(0, numberOfThreads).Select(i => 
     { 
      var thread = new Thread(() => 
      { 
       // Continually get the next task and try to execute it. 
       // This will continue until the scheduler is disposed and no more tasks remain. 
       foreach (var t in _tasks.GetConsumingEnumerable()) 
       { 
        TryExecuteTask(t); 
       } 
      }); 
      thread.IsBackground = true; 
      thread.SetApartmentState(ApartmentState.STA); 
      return thread; 
     }).ToList(); 

     // Start all of the threads 
     _threads.ForEach(t => t.Start()); 
    } 

    /// <summary>Queues a Task to be executed by this scheduler.</summary> 
    /// <param name="task">The task to be executed.</param> 
    protected override void QueueTask(Task task) 
    { 
     // Push it into the blocking collection of tasks 
     _tasks.Add(task); 
    } 

    /// <summary>Provides a list of the scheduled tasks for the debugger to consume.</summary> 
    /// <returns>An enumerable of all tasks currently scheduled.</returns> 
    protected override IEnumerable<Task> GetScheduledTasks() 
    { 
     // Serialize the contents of the blocking collection of tasks for the debugger 
     return _tasks.ToArray(); 
    } 

    /// <summary>Determines whether a Task may be inlined.</summary> 
    /// <param name="task">The task to be executed.</param> 
    /// <param name="taskWasPreviouslyQueued">Whether the task was previously queued.</param> 
    /// <returns>true if the task was successfully inlined; otherwise, false.</returns> 
    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) 
    { 
     // Try to inline if the current thread is STA 
     return 
      Thread.CurrentThread.GetApartmentState() == ApartmentState.STA && 
      TryExecuteTask(task); 
    } 

    /// <summary>Gets the maximum concurrency level supported by this scheduler.</summary> 
    public override int MaximumConcurrencyLevel 
    { 
     get { return _threads.Count; } 
    } 

    /// <summary> 
    /// Cleans up the scheduler by indicating that no more tasks will be queued. 
    /// This method blocks until all threads successfully shutdown. 
    /// </summary> 
    public void Dispose() 
    { 
     if (_tasks != null) 
     { 
      // Indicate that no new tasks will be coming in 
      _tasks.CompleteAdding(); 

      // Wait for all threads to finish processing tasks 
      foreach (var thread in _threads) thread.Join(); 

      // Cleanup 
      _tasks.Dispose(); 
      _tasks = null; 
     } 
    } 
} 
    public class PrinterHelper 
{ 
    readonly TaskScheduler _sta = new StaTaskScheduler(1); 
    public void PrintHtml(string htmlPath, string printerDevice) 
    { 
     if (!string.IsNullOrEmpty(printerDevice)) 
      SetAsDefaultPrinter(printerDevice); 


     Task.Factory.StartNew(() => PrintOnStaThread(htmlPath), CancellationToken.None, TaskCreationOptions.None, _sta).Wait(); 
    } 

    static void PrintOnStaThread(string htmlPath) 
    { 
     const short PRINT_WAITFORCOMPLETION = 2; 
     const int OLECMDID_PRINT = 6; 
     const int OLECMDEXECOPT_DONTPROMPTUSER = 2; 

     using(var browser = new WebBrowser()) 
     { 
      browser.Navigate(htmlPath); 
      while(browser.ReadyState != WebBrowserReadyState.Complete) 
       Application.DoEvents(); 

      dynamic ie = browser.ActiveXInstance; 
      ie.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, PRINT_WAITFORCOMPLETION); 
     } 
    } 


    static void SetAsDefaultPrinter(string printerDevice) 
    { 
     foreach (var printer in PrinterSettings.InstalledPrinters) 
     { 
      //Verify that the printer exists here 
     } 
     var path = "win32_printer.DeviceId='" + printerDevice + "'"; 
     using (var printer = new ManagementObject(path)) 
     { 
      ManagementBaseObject outParams = 
      printer.InvokeMethod("SetDefaultPrinter", 
      null, null); 
     } 

     return; 
    } 

} 

Répondre

0

Ceci est un assistant parallèle pour .net 4.0 seulement qui gère les collisions/threads.

/// <summary>Provides a scheduler that uses STA threads.</summary> 
public sealed class StaTaskScheduler : TaskScheduler, IDisposable 
{ 
    /// <summary>Stores the queued tasks to be executed by our pool of STA threads.</summary> 
    private BlockingCollection<Task> _tasks; 
    /// <summary>The STA threads used by the scheduler.</summary> 
    private readonly List<Thread> _threads; 

    /// <summary>Initializes a new instance of the StaTaskScheduler class with the specified concurrency level.</summary> 
    /// <param name="numberOfThreads">The number of threads that should be created and used by this scheduler.</param> 
    public StaTaskScheduler(int numberOfThreads) 
    { 
     // Validate arguments 
     if (numberOfThreads < 1) throw new ArgumentOutOfRangeException("numberOfThreads"); 

     // Initialize the tasks collection 
     _tasks = new BlockingCollection<Task>(); 

     // Create the threads to be used by this scheduler 
     _threads = Enumerable.Range(0, numberOfThreads).Select(i => 
     { 
      var thread = new Thread(() => 
      { 
       // Continually get the next task and try to execute it. 
       // This will continue until the scheduler is disposed and no more tasks remain. 
       foreach (var t in _tasks.GetConsumingEnumerable()) 
       { 
        TryExecuteTask(t); 
       } 
      }) {IsBackground = true}; 
      thread.SetApartmentState(ApartmentState.STA); 
      return thread; 
     }).ToList(); 

     // Start all of the threads 
     _threads.ForEach(t => t.Start()); 
    } 

    /// <summary>Queues a Task to be executed by this scheduler.</summary> 
    /// <param name="task">The task to be executed.</param> 
    protected override void QueueTask(Task task) 
    { 
     // Push it into the blocking collection of tasks 
     _tasks.Add(task); 
    } 

    /// <summary>Provides a list of the scheduled tasks for the debugger to consume.</summary> 
    /// <returns>An enumerable of all tasks currently scheduled.</returns> 
    protected override IEnumerable<Task> GetScheduledTasks() 
    { 
     // Serialize the contents of the blocking collection of tasks for the debugger 
     return _tasks.ToArray(); 
    } 

    /// <summary>Determines whether a Task may be inlined.</summary> 
    /// <param name="task">The task to be executed.</param> 
    /// <param name="taskWasPreviouslyQueued">Whether the task was previously queued.</param> 
    /// <returns>true if the task was successfully inlined; otherwise, false.</returns> 
    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) 
    { 
     // Try to inline if the current thread is STA 
     return 
      Thread.CurrentThread.GetApartmentState() == ApartmentState.STA && 
      TryExecuteTask(task); 
    } 

    /// <summary>Gets the maximum concurrency level supported by this scheduler.</summary> 
    public override int MaximumConcurrencyLevel 
    { 
     get { return _threads.Count; } 
    } 

    /// <summary> 
    /// Cleans up the scheduler by indicating that no more tasks will be queued. 
    /// This method blocks until all threads successfully shutdown. 
    /// </summary> 
    public void Dispose() 
    { 
     if (_tasks != null) 
     { 
      // Indicate that no new tasks will be coming in 
      _tasks.CompleteAdding(); 

      // Wait for all threads to finish processing tasks 
      foreach (var thread in _threads) thread.Join(); 

      // Cleanup 
      _tasks.Dispose(); 
      _tasks = null; 
     } 
    } 
} 

J'utilise 2 énumérations, on est pour le contrôle du navigateur Web le OLECMDID:

public enum OLECMDID 
{ 
    OLECMDID_OPEN = 1, 
    OLECMDID_NEW = 2, 
    OLECMDID_SAVE = 3, 
    OLECMDID_SAVEAS = 4, 
    OLECMDID_SAVECOPYAS = 5, 
    OLECMDID_PRINT = 6, 
    OLECMDID_PRINTPREVIEW = 7, 
    OLECMDID_PAGESETUP = 8, 
    OLECMDID_SPELL = 9, 
    OLECMDID_PROPERTIES = 10, 
    OLECMDID_CUT = 11, 
    OLECMDID_COPY = 12, 
    OLECMDID_PASTE = 13, 
    OLECMDID_PASTESPECIAL = 14, 
    OLECMDID_UNDO = 15, 
    OLECMDID_REDO = 16, 
    OLECMDID_SELECTALL = 17, 
    OLECMDID_CLEARSELECTION = 18, 
    OLECMDID_ZOOM = 19, 
    OLECMDID_GETZOOMRANGE = 20, 
    OLECMDID_UPDATECOMMANDS = 21, 
    OLECMDID_REFRESH = 22, 
    OLECMDID_STOP = 23, 
    OLECMDID_HIDETOOLBARS = 24, 
    OLECMDID_SETPROGRESSMAX = 25, 
    OLECMDID_SETPROGRESSPOS = 26, 
    OLECMDID_SETPROGRESSTEXT = 27, 
    OLECMDID_SETTITLE = 28, 
    OLECMDID_SETDOWNLOADSTATE = 29, 
    OLECMDID_STOPDOWNLOAD = 30, 
    OLECMDID_FIND = 32, 
    OLECMDID_DELETE = 33, 
    OLECMDID_PRINT2 = 49, 
    OLECMDID_PRINTPREVIEW2 = 50, 
    OLECMDID_PAGEACTIONBLOCKED = 55, 
    OLECMDID_PAGEACTIONUIQUERY = 56, 
    OLECMDID_FOCUSVIEWCONTROLS = 57, 
    OLECMDID_FOCUSVIEWCONTROLSQUERY = 58, 
    OLECMDID_SHOWPAGEACTIONMENU = 59, 
    OLECMDID_ADDTRAVELENTRY = 60, 
    OLECMDID_UPDATETRAVELENTRY = 61, 
    OLECMDID_UPDATEBACKFORWARDSTATE = 62, 
    OLECMDID_OPTICAL_ZOOM = 63, 
    OLECMDID_OPTICAL_GETZOOMRANGE = 64, 
    OLECMDID_WINDOWSTATECHANGED = 65, 
    OLECMDID_ACTIVEXINSTALLSCOPE = 66, 
    OLECMDID_UPDATETRAVELENTRY_DATARECOVERY = 67 
} 

L'autre est une mesure pour tout ce que vous devez imprimer:

public enum PrintDocumentType 
{ 
    Bill, 
    Label //etc... 
} 

maintenant , voici l'aide de mon imprimante que j'utilise qui définit l'imprimante par défaut (et imprime à elle), change également la marge selon ce que j'ai besoin imprimé:

public class PrinterHelper 
{ 
    readonly TaskScheduler _sta = new StaTaskScheduler(1); 
    public void PrintHtml(string htmlPath, string printerDevice, PrintDocumentType printDocumentType) 
    { 
     if (!string.IsNullOrEmpty(printerDevice)) 
      SetAsDefaultPrinter(printerDevice); 

     IeSetup(printDocumentType); 

     Task.Factory.StartNew(() => PrintOnStaThread(htmlPath), CancellationToken.None, TaskCreationOptions.None, _sta).Wait(); 
    } 

    static void PrintOnStaThread(string htmlPath) 
    { 
     const short printWaitForCompletion = 2; 
     const int oleCmdExecOptDontPromptUser = 2; 

     using(var browser = new WebBrowser()) 
     { 
      WebBrowserHelper.ClearCache(); /*needed since there is a major cache flaw. The WebBrowserHelper class is available at http://www.gutgames.com/post/Clearing-the-Cache-of-a-WebBrowser-Control.aspx with some slight changes or if website is taken off, it is based heavily on http://support.microsoft.com/kb/326201*/ 

      browser.Navigate(htmlPath); 
      while(browser.ReadyState != WebBrowserReadyState.Complete) 
       Application.DoEvents(); 

      dynamic ie = browser.ActiveXInstance; 


      ((IWebBrowser2)ie).ExecWB(SHDocVw.OLECMDID.OLECMDID_PRINT, OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, oleCmdExecOptDontPromptUser, printWaitForCompletion); 
     } 
    } 


    static void SetAsDefaultPrinter(string printerDevice) 
    { 
     foreach (var printer in PrinterSettings.InstalledPrinters) 
     { 
      //verify that the printer exists here 
     } 
     var path = "win32_printer.DeviceId='" + printerDevice + "'"; 
     using (var printer = new ManagementObject(path)) 
     { 
      printer.InvokeMethod("SetDefaultPrinter", 
           null, null); 
     } 

     return; 
    } 


    /// <summary> 
    /// Making sure the printer doesn't output the default footer and header of Internet Explorer (url, pagenumber, title, etc.). 
    /// </summary> 
    public void IeSetup(PrintDocumentType printDocumentType) 
    { 
     const string keyName = @"Software\Microsoft\Internet Explorer\PageSetup"; 
     using (RegistryKey key = Registry.CurrentUser.OpenSubKey(keyName, true)) { 
      if (key == null) return; 
      key.SetValue("footer", ""); 
      key.SetValue("header", ""); 

      switch (printDocumentType) 
      { 
       case PrintDocumentType.Label: 
        key.SetValue("margin_top", "0.12500"); 
        key.SetValue("margin_bottom", "0.12500"); 
        key.SetValue("margin_left", "0.25000"); 
        key.SetValue("margin_right", "0.25000"); 
        break; 

       case PrintDocumentType.Bill: 
        key.SetValue("margin_top", "0.75000"); 
        key.SetValue("margin_bottom", "0.75000"); 
        key.SetValue("margin_left", "0.75000"); 
        key.SetValue("margin_right", "0.75000"); 
        break; 
      } 
     } 
    } 
} 

Comme vous le remarquez, j'ai un webbrowserhelper qui est une autre classe. C'est assez gros et je ne vais pas le coller. Cependant, je saisis les liens dans le commentaire à côté de celui-ci où vous pouvez obtenir le code. Le webbrowser lui-même a un défaut de cache majeur et même si vous le forcez à rafraîchir la page, il récupère toujours le cache, donc un clearcache est en ordre. J'ai appris à la dure.

J'espère que cela aide tout le monde. Veuillez noter que ceci est pour .net 4.0.

0

Quelques articles pour readng qui peut-être utile sont:

mettra à jour si je trouve autre chose. J'espère que cela t'aides.

+0

Oui J'ai actuellement un assistant d'imprimante que j'ai trouvé ici dans stackoverflow que j'ai ajouté comme modification ci-dessus. Malheureusement, cela ne m'aide pas à imprimer un fichier rendu HTML jusqu'à présent. –

+0

Je n'ai pas regardé mais est-il possible d'imprimer un flux? Si oui, vous pouvez potentiellement obtenir le code HTML créé, puis imprimer le flux de réponse qui comprend le code HTML ... juste une pensée folle :) – WestDiscGolf

+0

En utilisant le code que je montre là-bas, cela fonctionne. Pour le besoin de ce projet cela fonctionne parce que c'est une petite entreprise. Je reconsidérerais probablement ceci était quelque chose qui allait être utilisé beaucoup plus en raison de la concurrence avec le changement de l'imprimante par défaut. Je déteste travailler avec des objets COM et celui-ci nécessite que IE soit installé sur le serveur, mais je dois faire ce que tu dois faire? Je suis toujours ouvert à d'autres suggestions plus évolutives! –