C# Windows 服务 - WebBrowser 控件无法在发布模式下工作

C# Windows Service - WebBrowser Control not working in Release Mode

我有一个静默打印网页的控制台应用程序。参考:Print WebBrowser without previewing i.e. single click print

我正在尝试将其转换为 Windows 服务。代码在调试模式下工作正常但在发布模式下失败(在我安装服务之后)。事件查看器有错误:

Faulting application name: TestWindowsService.exe, version: 1.0.0.0, time stamp: 0x59c94fa7
Faulting module name: MSHTML.dll, version: 11.0.9600.18792, time stamp: 0x59908408

这是完整代码(我以打印google.com为例):

using System;
using System.ServiceProcess;
using System.Timers;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace TestWindowsService
{
    public partial class Scheduler : ServiceBase
    {
        private System.Timers.Timer timer1 = null;
        public Scheduler()
        {
            InitializeComponent();
        }

        public void onDebug()
        {
            OnStart(null);
        }

        protected override void OnStart(string[] args)
        {

            timer1 = new System.Timers.Timer();
            this.timer1.Interval = 3000; //every 3 seconds
            this.timer1.Elapsed += new ElapsedEventHandler(this.timer1_Tick);
            timer1.AutoReset = false;
            timer1.Enabled = true;
        }

        private void timer1_Tick(object sender, ElapsedEventArgs e)
        {
            try
            {

                var task = MessageLoopWorker.Run(DoWorkAsync, "http://www.google.com");
                task.Wait();
                Console.WriteLine("DoWorkAsync completed.");

            }
            catch (Exception ex)
            {
                Console.WriteLine("DoWorkAsync failed: " + ex.Message);
            }
            finally
            {
                if (null != timer1)
                {
                    timer1.Start();
                }
            }
        }

        public static class MessageLoopWorker
        {
            public static async Task<object> Run(Func<object[], Task<object>> worker, params object[] args)
            {
                var tcs = new TaskCompletionSource<object>();

                var thread = new Thread(() =>
                {
                    EventHandler idleHandler = null;

                    idleHandler = async (s, e) =>
                    {
                        // handle Application.Idle just once
                        Application.Idle -= idleHandler;

                        // return to the message loop
                        await Task.Yield();

                        // and continue asynchronously
                        // propogate the result or exception
                        try
                        {
                            var result = await worker(args);
                            tcs.SetResult(result);
                        }
                        catch (Exception ex)
                        {
                            tcs.SetException(ex);
                        }

                        // signal to exit the message loop
                        // Application.Run will exit at this point
                        Application.ExitThread();
                    };

                    // handle Application.Idle just once
                    // to make sure we're inside the message loop
                    // and SynchronizationContext has been correctly installed
                    Application.Idle += idleHandler;
                    Application.Run();
                });

                // set STA model for the new thread
                thread.SetApartmentState(ApartmentState.STA);

                // start the thread and await for the task
                thread.Start();
                try
                {
                    return await tcs.Task;
                }
                finally
                {
                    thread.Join();
                }
            }
        }

        // navigate WebBrowser to the list of urls in a loop
        static async Task<object> DoWorkAsync(object[] args)
        {
            //logger.Info("Start working.");
            var wb = new WebBrowser();
            wb.ScriptErrorsSuppressed = true;

            if (wb.Document == null && wb.ActiveXInstance == null)
            {
                //logger.Info($"Unable to initialize the underlying WebBrowserActiveX");
                throw new ApplicationException("Unable to initialize the underlying WebBrowserActiveX");
            }
            // get the underlying WebBrowser ActiveX object;
            var wbax = (SHDocVw.WebBrowser)wb.ActiveXInstance;
            TaskCompletionSource<bool> loadedTcs = null;
            WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (s, e) =>
                loadedTcs.TrySetResult(true); // turn event into awaitable task

            TaskCompletionSource<bool> printedTcs = null;
            SHDocVw.DWebBrowserEvents2_PrintTemplateTeardownEventHandler printTemplateTeardownHandler = (p) =>
                printedTcs.TrySetResult(true); // turn event into awaitable task

            // navigate to each URL in the list
            foreach (var url in args)
            {
                loadedTcs = new TaskCompletionSource<bool>();
                wb.DocumentCompleted += documentCompletedHandler;
                try
                {
                    wb.Navigate(url.ToString());
                    // await for DocumentCompleted
                    await loadedTcs.Task;
                }
                finally
                {
                    wb.DocumentCompleted -= documentCompletedHandler;
                }

                // the DOM is ready, 
                //Console.WriteLine(url.ToString());
                //Console.WriteLine(wb.Document.Body.OuterHtml);

                // print the document
                printedTcs = new TaskCompletionSource<bool>();
                wbax.PrintTemplateTeardown += printTemplateTeardownHandler;
                try
                {
                    wb.Print();
                    // await for PrintTemplateTeardown - the end of printing
                    await printedTcs.Task;
                }
                finally
                {
                    wbax.PrintTemplateTeardown -= printTemplateTeardownHandler;
                }
                //logger.Info($"{url.ToString()}  is Complete ");
                //Console.WriteLine(url.ToString() + " printed.");
            }

            wb.Dispose();
            return null;
        }

        protected override void OnStop()
        {
        }
    }
}

这是Program.cs

using System;
using System.ServiceProcess;
using System.Text;
using System.Threading;

namespace TestWindowsService
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {

#if DEBUG
            Scheduler myservice = new Scheduler();
            myservice.onDebug();
            Thread.Sleep(Timeout.Infinite);

#else
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Scheduler()
            };
            ServiceBase.Run(ServicesToRun);
#endif
        }
    }
}

我做错了什么?任何指针?

也许您需要:

  1. 将服务登录设置为"Local System account"。

  2. Select "Allow Service to interact with desktop".

我的问题是 VISTA 和后续 OS 版本中添加的会话 0 隔离功能。

我在这些 link 的帮助下解决了我的问题:

C# Windows Service Creates Process but doesn't executes it

Printing from a Windows Service

https://social.msdn.microsoft.com/Forums/vstudio/en-US/75e06571-cb7d-4c77-bdbc-4983fd8cfad6/print-pdf-from-windows-service?forum=csharpgeneral