title: Render and Print HTML to non-default printer
body: <p>I have a C# WPF .NET 4.6 program that creates HTML files and I would like to print them automatically (without dialog) with a known, non-default printer. Of course this includes rendering the HTML first. Since the program creates these files, the HTML data can come from a <code>MemoryStream</code>, <code>FileStream</code>, or directly from string.</p>
  
  <p>The program has settings that allows the user to specify which printer to print to in advance, using <code>System.Drawing.Printing.PrinterSettings.InstalledPrinters</code>, as each file may require a different printer. At printing time, the printer's name is known, but will likely be different from the Windows default printer.</p>
  
  <p>I have researched a lot of other projects, but they do not seem to account for the printer being different from the default. Changing the default printer would be anti-social and would cause a world of threading-related pain. This seems to be the #1 accepted solution, though cannot be the best solution??</p>
  
  <hr>
  
  <p>Research and solutions looked at:</p>
  
  <p><a href="https://stackoverflow.com/questions/1298969/printing-the-contents-of-a-wpf-webbrowser">Printing the contents of a WPF WebBrowser</a> and <a href="https://stackoverflow.com/questions/23011397/silently-print-html-from-wpf-webbrowser/33824652#33824652">Silently print HTML from WPF WebBrowser</a> and 
  <a href="https://social.msdn.microsoft.com/Forums/vstudio/en-US/64e48f1b-ff5c-45cf-8dac-5b55f0d09928/wpf-webbrowser-control-how-to-print-html-content?forum=wpf" rel="nofollow noreferrer">corresponding MSDN</a> <a href="https://social.msdn.microsoft.com/Forums/vstudio/en-US/63ae5345-b2ca-45a9-9113-0ddc43e9925b/print-functionality-dialog-less-using-wpf-webbrowser-control?forum=wpf" rel="nofollow noreferrer">forum discussions</a>
  are insufficient as the COM <a href="https://msdn.microsoft.com/en-us/library/aa752117(v=vs.85).aspx" rel="nofollow noreferrer">ExecWB</a> function only prints to the default printer (?)</p>
  
  <p>The <a href="https://msdn.microsoft.com/en-us/library/b0wes9a3(v=vs.110).aspx" rel="nofollow noreferrer">MSDN example</a> only uses the Print() command on the <code>WebBrowser</code>, which again uses the default printer.</p>
  
  <p>So I went down the route of trying to change the printer options. <a href="https://stackoverflow.com/questions/3675430/programmatically-changing-the-destination-printer-for-a-winforms-webbrowser-cont">Programmatically changing the destination printer for a WinForms WebBrowser control</a> was asked, but has a rather unsatisfactory answer, as it has a broken link, and I don't know what external programs the running computer has, so I cannot guarantee Adobe, OpenOffice, etc. OP mentioned that they parsed the ActiveX COM without going into detail. Sounds tricky.</p>
  
  <p>Perhaps I could gleam something from writing to a RichTextBox like <a href="http://www.itwriting.com/htmleditor/" rel="nofollow noreferrer">this project</a> does, and hide the box?</p>
  
  <p>I thought <a href="https://stackoverflow.com/questions/28889315/silent-print-html-file-in-c-sharp-using-wpf">Silent print HTML file in C# using WPF</a> is on a good path, however the original post has hard-coded numbers for screen dimensions‽ and OP mentioned that the printer cut off the document. The accepted (and bountied) answer again use the ExecWB default printer settings method.</p>
  
  <p><a href="https://stackoverflow.com/questions/4974996/execcommandprint-false-idontunderstandthisargument">execCommand(&quot;Print&quot;, false, IDon&#39;tUnderstandThisArgument)</a> also showed promise, as its answer was an updated <a href="https://msdn.microsoft.com/en-us/library/system.drawing.printing.printdocument.defaultpagesettings(v=vs.110).aspx" rel="nofollow noreferrer">MSDN answer</a>, but the filestream sent to the printer does not allow HTML, nor does the <code>DocumentStream</code> from <code>WebBrowser</code> appear to work (the printer prints a single blank page).</p>
  
  <p><a href="https://stackoverflow.com/questions/714507/how-do-i-programatically-change-printer-settings-with-the-webbrowser-control">How do I programatically change printer settings with the WebBrowser control?</a> has very similar requirements to me, except changes the registry as a solution.</p>
  
  <p>Other than researching how others have done it, I also tried printing the WPF <code>WebBrowser</code> directly as it is a <code>Visual</code> control:</p>
  
  <pre><code>    public static bool Print(string printer, Visual objToPrint)
      {
        if (string.IsNullOrEmpty(printer))
        {
          return false;
        }
  
        var dlg = new PrintDialog
        {
          PrintQueue = new PrintServer().GetPrintQueue(printer)
        };
  
        dlg.PrintTicket.CopyCount = 1;
        dlg.PrintTicket.PageOrientation = PageOrientation.Portrait;
        dlg.PrintTicket.PagesPerSheet = 1;
  
        dlg.PrintVisual(objToPrint, "Print description");
        return true;
      }
  </code></pre>
  
  <p>however this does not print anything (because the <code>WebBrowser</code> is not visible?).
  And tried a <code>PrintDocument</code> as the updated <a href="https://msdn.microsoft.com/en-us/library/system.drawing.printing.printdocument.defaultpagesettings(v=vs.110).aspx" rel="nofollow noreferrer">MSDN article</a> suggested:</p>
  
  <pre><code>    public static async Task&lt;bool&gt; PrintHTMLAsync(string printer, string html)
      {
        bool result;
        using (var webBrowser = new System.Windows.Forms.WebBrowser())
        {
          webBrowser.DocumentCompleted += ((sender, e) =&gt; browserReadySemaphore.Release());
          byte[] buffer = Encoding.UTF8.GetBytes(html);
          webBrowser.DocumentStream = new MemoryStream(buffer);
  
          // Wait until the page loads.
          await browserReadySemaphore.WaitAsync();
  
          try
          {
            using (PrintDocument pd = new PrintDocument())
            {
              pd.PrinterSettings.PrinterName = printer;
              pd.PrinterSettings.Collate = false;
              pd.PrinterSettings.Copies = 1;
              pd.PrinterSettings.FromPage = 1;
              pd.PrinterSettings.ToPage = 1;
              pd.Print();
              result = true;
            }
          }
          catch (Exception ex)
          {
            result = false;
            Debug.WriteLine(ex);
          }
  
          return result;
        }
      }
  </code></pre>
  
  <p>with no joy.</p>
  
  <p>I've also used the PRINT DOS command:</p>
  
  <pre><code>public static string PerformSilentPrinting(string fileName, string printerName)
  {
    try
    {
      ProcessStartInfo startInfo = new ProcessStartInfo(fileName)
      {
        Arguments = string.Format("/C PRINT /D:\"{0}\" \"{1}\"", printerName, fileName),
        FileName = "cmd.exe",
        RedirectStandardOutput = true,
        UseShellExecute = false,
        WindowStyle = ProcessWindowStyle.Hidden,
      };
  
      // Will execute the batch file with the provided arguments
      Process process = Process.Start(startInfo);
  
      // Reads the output        
      return process.StandardOutput.ReadToEnd();
    }
    catch (Exception ex)
    {
      return ex.ToString();
    }
  }
  </code></pre>
  
  <p>but the print command seems to only accept text files.</p>