System.Windows.Controls.WebBrowser,System.Windows.Threading.Dispatcher和Windows服务

时间:2013-08-13 15:47:00

标签: wpf windows-services webbrowser-control dispatcher

我正在尝试将一些html内容呈现给Windows服务中的位图。

我正在使用System.Windows.Controls.WebBrowser来执行渲染。基本的渲染设置作为一个独立的进程工作,WPF窗口托管控件,但作为一项服务,至少我没有得到LoadCompleted事件。

我知道我至少需要一个Dispatcher或其他消息泵循环来实现这个WPF控件。也许我做得对,WebBrowser控件只需要额外的技巧/不兼容性。这就是我所拥有的:

我相信只有一个Dispatcher需要运行,并且它可以在服务的整个生命周期内运行。我相信Dispatcher.Run()是实际的循环本身,因此需要它自己的线程,否则它可以阻止。在这种情况下,该线程需要[STAThread]。因此,在相关的静态构造函数中,我有以下内容:

var thread = new Thread(() =>
{
    dispatcher = Dispatcher.CurrentDispatcher;

    Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

其中dispatcher是静态字段。同样,我认为只能有一个,但我不确定我是否应该从任何地方使用Dispatcher.CurrentDispatcher()并获得正确的参考。

渲染操作如下。我在WebBrowser的线程上创建,导航和处理dispatcher,但事件处理程序分配和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是否未触发?

谢谢!

2 个答案:

答案 0 :(得分:1)

以下是代码的修改版本,可用作控制台应用。几点:

  • 您需要WPF WebBrowser的父窗口。它可能是如下所示的隐藏窗口,但必须在物理上创建(即具有实时HWND句柄)。否则,WB永远不会完成加载文档(wb.Document.readyState == "interactive"),并且LoadCompleted永远不会被解雇。我不知道这种行为,它与WebBrowser控件的WinForms版本不同。请问为什么你选择这个项目的WPF?

  • 您需要在创建WB控件的同一个线程上添加wb.LoadCompleted事件处理程序(调度程序的线程在此处)。在内部,WPF WebBrowser只是公寓线程WebBrowser ActiveX控件的包装器,它通过IConnectionPointContainer接口公开其事件。规则是,对公寓线程COM对象的所有调用必须在最初创建对象的线程上(或代理),因为这就是这种对象所期望的。从这个意义上说,IConnectionPointContainer方法与WB的其他方法没什么不同。

  • 一个次要的StreamWriter会自动关闭它初始化的流(除非明确告知不在构造函数中这样做),因此不需要用{{1}包装流}。

代码已准备好编译和运行(它需要一些额外的程序集引用:PresentationFramework,WindowsBase,System.Windows,System.Windows.Forms,Microsoft.mshtml)。

using

答案 1 :(得分:0)

Webbrowser Control可能需要Desktop Interaction才能呈现内容:

enter image description here

我的感觉是说使用WPF控件,特别是Webbrowser-Control(=围绕IE ActiveX控件的Wrapper)并不是最好的想法。还有其他渲染引擎可能更适合这项任务:{ {3}}