我正在尝试将一些 html 内容呈现到 Windows 服务中的位图。
我正在使用 System.Windows.Controls.WebBrowser 来执行渲染。基本的渲染设置作为一个独立的进程工作,带有一个托管控件的 WPF 窗口,但作为一项服务,至少我没有触发 LoadCompleted 事件。
我知道对于这个 WPF 控件,我至少需要一个 Dispatcher 或其他消息泵循环。也许我做得对,WebBrowser 控件只需要额外的技巧/不兼容。这是我所拥有的:
我相信只需要运行一个 Dispatcher 并且它可以在服务的生命周期内运行。我相信 Dispatcher.Run() 是实际的循环本身,因此需要它自己的线程,否则它可以阻塞。并且该线程需要[STAThread]
在这种情况下。因此,在相关的静态构造函数中,我有以下内容:
var thread = new Thread(() =>
{
dispatcher = Dispatcher.CurrentDispatcher;
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
哪里dispatcher
是静态字段。同样,我认为只能有一个,但我不确定我是否应该能够Dispatcher.CurrentDispatcher()
在任何地方使用并获得正确的参考。
渲染操作如下。我创建、导航和处理WebBrowser
ondispatcher
的线程,但事件处理程序分配和mres.Wait
我认为可能都发生在渲染请求处理操作上。我得到The calling thread cannot access this object because a different thread owns it
了,但现在有了这个设置,我没有。
WebBrowser wb = null;
var mres = new ManualResetEventSlim();
try
{
dispatcher.Invoke(() => { wb = new WebBrowser(); });
wb.LoadCompleted += (s, e) =>
{
// Not firing
};
try
{
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms, Encoding.Unicode))
{
sw.Write(html);
sw.Flush();
ms.Seek(0, SeekOrigin.Begin);
// GO!
dispatcher.Invoke(() =>
{
try
{
wb.NavigateToStream(ms);
Debug.Assert(Dispatcher.FromThread(Thread.CurrentThread) != null);
}
catch (Exception ex)
{
// log
}
});
if (!mres.Wait(15 * 1000)) throw new TimeoutException();
}
}
catch (Exception ex)
{
// log
}
}
finally
{
dispatcher.Invoke(() => { if (wb != null) wb.Dispose(); });
}
当我运行它时,每次我都会收到超时异常,因为 LoadCompleted 永远不会触发。我试图验证调度程序是否正常运行和抽水。不知道怎么做,但我从静态构造函数中钩住了一些调度程序的事件,并从中得到了一些打印输出,所以我认为它正在工作。
代码确实到达了wb.NavigateToStream(ms);
断点。
这是 Dispatcher 的糟糕应用吗?wb.LoadCompleted 的未触发是由于其他原因吗?
谢谢!