2010-10-19 2 views
0

Я разрабатываю приложение ASP.NET MVC2 для интрасети для малого бизнеса. Малый бизнес имеет несколько типов принтеров, и в зависимости от того, что требуется, запрос на печать будет отправлен (от браузера/пользователя) на сервер, и сервер отправит задание на печать на правый принтер соответственно. Обратите внимание, что это совершенно новая среда для них, и я контролирую почти все. Я, скорее всего, буду использовать очень легкую ОС (возможно, Asus ExpressGate или Chrome OS в зависимости от даты запуска?), Поэтому пользователи не могут устанавливать какие-либо принтеры, но сервер будет иметь все настройки.Печать с использованием C#/ASP.NET MVC2

Вот мой вопрос:

Есть простой способ печати на стороне сервера (без диалога, конечно, потому что не будет никого ждать, чтобы нажимать на них) страницу, используя ссылку HTML, как параметр и сохранение формата HTML, конечно.

Я видел несколько возможностей COM-материалов там, но если есть возможности избежать этого, используя класс .net, я был бы признателен. Я использую .net 4.0. Однако я буду принимать любые предложения, даже если это COM.

Edit: Пожалуйста, обратите внимание, что любое решение, которое имеет смысл, также будет принято во внимание, быстро (пока не изучали) пример будет передать этот HTML в файл док и отправки этого файла на принтер.

Редактировать-Код снят из-за недостатков использования.

Edits2: После этой ссылке: Print html document from Windows Service in C# without print dialog

святой Грааль решение Вадима ДЕЛАЕТ работу. Тем не менее, он имеет ограничения, такие как использование только принтера по умолчанию. Я изменяю принтер по умолчанию перед началом печати, что приводит к тому, что печать переходит к правильному принтеру. Я могу видеть, что некоторые вопросы Совпадение здесь происходит, но до сих пор это лучшее, что я придумал (большая часть этого кода от Вадима, и я дал ему полный кредит для этого):

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

} 

ответ

0

Это вспомогательный помощник для .net 4.0, который управляет конфликтами/потоками.

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

Я использую 2 перечислений, один для управления веб-браузер 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 
} 

Другой обычай сделал для все, что нужно для печати:

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

сейчас , вот мой помощник принтера, который я использую, который устанавливает принтер по умолчанию (и печатает на него), также изменяет маржу в соответствии с тем, что мне нужно напечатать:

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

Как вы заметили, у меня есть webbrowserhelper, который является другим классом. Он довольно большой, и я не буду вставлять его. Тем не менее, я ввожу ссылки в комментарии рядом с ним, где вы можете получить код. У самого webbrowser есть основной недостаток кеша, и даже если вы заставляете его обновлять страницу, он всегда будет захватывать кеш, следовательно, имеет место clearcache. Я узнал на своей шкуре.

Я надеюсь, что это поможет всем. Обратите внимание, что это для .net 4.0.

0

Пару статей для readng, который может быть полезным являются:

Обновит, если я найду что-нибудь еще. Надеюсь это поможет.

+0

Да В настоящее время у меня есть помощник принтера, который я нашел здесь в stackoverflow, который я добавил как изменение выше. К сожалению, это не помогает мне печатать HTML-рендеринг файлов. –

+0

Я не смотрел, но есть ли способ распечатать поток? Если это так, вы можете потенциально получить созданный html, а затем распечатать поток ответов, который включает html ... просто сумасшедшая мысль :) – WestDiscGolf

+0

Используя код, который я показываю там, он работает. Для этого этот проект работает, потому что это небольшой бизнес. Я бы, вероятно, передумал, это было нечто, что будет использоваться намного больше из-за совпадения с изменением принтера по умолчанию. Мне не нравится работать с COM-объектами, и для этого требуется, чтобы IE был установлен на сервере, но должен делать то, что вам нужно делать? Я по-прежнему открыт для других более масштабируемых предложений! –

Смежные вопросы