【发布时间】:2013-08-15 08:03:59
【问题描述】:
我正在尝试将一些 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() 并获得正确的参考。
渲染操作如下。我在dispatcher 的线程上创建、导航和处理WebBrowser,但我认为事件处理程序分配和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 没有触发是因为别的原因吗?
谢谢!
【问题讨论】:
-
wb.LoadCompleted不会被触发,因为您从创建 WebBrowser 对象的不同线程添加此事件处理程序。尝试将其移入dispatcher.Invoke(() => { .. })代表。那可能仍然不是一个好的设计,但至少应该被解雇。 -
@Noseratio - 感谢您的建议。我不认为需要在同一个线程上添加事件处理程序,但我只是尝试过,但它不允许事件触发 - 没有变化。一旦我得到它的工作,我会尝试和不只是看看。
标签: wpf windows-services webbrowser-control dispatcher