Print html document from Windows Service in C# without print dialog Print html document from Windows Service in C# without print dialog windows windows

Print html document from Windows Service in C# without print dialog


Here's the Holy Grail.

Taking advantage of StaTaskScheduler (taken from Parallel Extension Extras (release on Code Gallery)).

Features: waits for the printing completion, doesn't show print settings, hopefully reliable.

Limitations: requires C# 4.0, uses default printer, doesn't allow to change print template

    TaskScheduler Sta = new StaTaskScheduler(1);    public void PrintHtml(string htmlPath)    {        Task.Factory.StartNew(() => PrintOnStaThread(htmlPath), CancellationToken.None, TaskCreationOptions.None, Sta).Wait();    }    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);        }    }//--------------------------------------------------------------------------// //  Copyright (c) Microsoft Corporation.  All rights reserved. // //  File: StaTaskScheduler.cs////--------------------------------------------------------------------------using System.Collections.Concurrent;using System.Collections.Generic;using System.Linq;namespace System.Threading.Tasks.Schedulers{    /// <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;            }        }    }}


To add to Vadim's limitation you can set the default printer before printing by using:

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

And changeing slightly the PrintHtml method with:

    public void PrintHtml(string htmlPath, string printerDevice)    {        if (!string.IsNullOrEmpty(printerDevice))            SetAsDefaultPrinter(printerDevice);        Task.Factory.StartNew(() => PrintOnStaThread(htmlPath), CancellationToken.None, TaskCreationOptions.None, _sta).Wait();    }

Now I don't know how that will fair in a heavy printing environment considering there could be concurrency issues with changeing the default printer a lot. But so far this is the best I came up with to fix this limitation.


In windows service the Microsoft web browser control is not working. I had used that code it's working fine in windows application but when i am using within a windows service then the program getting stuck in this line

axWebBrowser1.Navigate(@"C:\mydoc.html", ref empty, ref empty, ref empty, ref empty);

thanks for reply,Anup Pal