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
}
}
}
我做错了什么?任何指针?
也许您需要:
将服务登录设置为"Local System account"。
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
我有一个静默打印网页的控制台应用程序。参考: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
}
}
}
我做错了什么?任何指针?
也许您需要:
将服务登录设置为"Local System account"。
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