在类库WebBrowser控件 [英] WebBrowser control in a class library

查看:127
本文介绍了在类库WebBrowser控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以,正如标题所暗示的,我想要使用WebBrowser控件在类库。我已经通过几个这么喜欢这个优秀的帖子,但独特的走了在我的情况的是,WebBrowser对象必须保持活着应用程序的生命并保持其状态/饼干在不同的电话,该库的客户端将不时。

So as the title suggests, I'm trying to use WebBrowser control in a class library. I've gone through several SO questions like this excellent post, but the unique thing in my situation is that the WebBrowser object must remain alive for the life of application and keep its state/cookies across different calls that the library clients will make from time to time.

我已经证实,WebBrowser控件不这样做导航,除非它被创建的线程包含一个消息泵。但只要我介绍一个消息泵,在的code块Application.Run()通话并没有进一步的事件发生。任何帮助将真正appricated。

I have confirmed that WebBrowser control does not do navigation unless the thread it was created on contains a message pump. But as soon as I introduce a message pump, the code blocks at Application.Run() call and no further events are generated. Any help will really be appricated.

推荐答案

如果我理解正确的问题,你需要运行 web浏览器的一个实例控件的生命周期您的图书馆,并保持它活着,并独立在一个专用的STA线程有自己的WinForms消息循环。

If I understood the question correctly, you need to run an instance of WebBrowser control for the lifetime of your library, and keep it alive and independent on a dedicated STA thread with its own WinForms message loop.

在code以下显示了它如何可能做到的,使用一个被称为辅助类 MessageLoopApartment 。请注意 web浏览器被创建和操作上的一个单独的线程。

The code below shows how it can possibly be done, using a helper class called MessageLoopApartment. Note how the WebBrowser gets created and manipulated on a separate thread.

href="http://stackoverflow.com/tags/task-parallel-library/info">任务并行库的 MessageLoopApartment.Run 定STA线程上的任务可以同步与被等待task.Wait()或异步等待任务,结果和异常从STA线程通过 Task.Result / 传播任务.Execption ,异常被重新抛出调用者的堆栈帧。

The Task Parallel Library is very handy in getting the synchronization job done. The tasks scheduled on the STA thread with MessageLoopApartment.Run can be waited synchronously with task.Wait() or asynchronously with await task, results and exceptions are propagated from the STA thread via Task.Result/Task.Execption, exceptions are re-thrown on the caller's stack frame.

MessageLoopApartment 的实施与.NET 4.0 ,它不使用任何.NET 4.5功能的兼容。客户端code(即 web浏览器导航试验)可选择使用 异步/计谋 ,这可能需要 Microsoft.Bcl.Async 以面向.NET 4.0。 TPL和异步/计谋大大简化操纵 MessageLoopApartment 的线程内创建的对象,如 _webBrowser

The implementation of MessageLoopApartment is compatible with NET 4.0, it doesn't use any .NET 4.5 features. The client code (the WebBrowser navigation test) optionally uses async/await, which may require Microsoft.Bcl.Async to target .NET 4.0. TPL and async/await greatly simplify manipulating objects created inside the MessageLoopApartment's thread, like _webBrowser.

导航试验是在 MainForm_Load 执行,但 _webBrowser 的寿命和 _apartment 由单个呼叫的界限并不限定。这两个被里面的 MainForm_FormClosed 破坏。该测试程序是一个WinForms应用程序,但它可能也有一个控制台应用程序或其他任何东西。

The navigation test is performed inside MainForm_Load, but the lifetime of _webBrowser and _apartment is not limited by the boundaries of that single call. Both gets destroyed inside MainForm_FormClosed. The test app is a WinForms app, but it may as well be a console app or anything else.

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinForms_21772632
{
    // http://stackoverflow.com/q/21772632/1768303

    public partial class MainForm : Form
    {
        MessageLoopApartment _apartment;

        // _webBrowser is created on a separate thread,
        // with MessageLoopApartment.Run
        WebBrowser _webBrowser;

        // MainForm
        public MainForm()
        {
            InitializeComponent();

            // create an independent STA thread
            _apartment = new MessageLoopApartment();

            // create a WebBrowser on that STA thread
            _webBrowser = _apartment.Run(
                () => new WebBrowser(),
                CancellationToken.None).Result;

            this.Load += MainForm_Load;
            this.FormClosed += MainForm_FormClosed;
        }

        // navigation test
        async void MainForm_Load(object senderLoad, EventArgs eLoad)
        {
            // navigate
            var cts = new CancellationTokenSource(10000); // cancel in 10s
            var url = "http://example.com";
            var html = await _apartment.Run(async () =>
            {
                WebBrowserDocumentCompletedEventHandler handler = null;
                var navigateTcs = new TaskCompletionSource<bool>();
                handler = (s, e) =>
                    navigateTcs.TrySetResult(true);
                _webBrowser.DocumentCompleted += handler;
                try
                {
                    using (cts.Token.Register(() => navigateTcs.TrySetCanceled()))
                    {
                        _webBrowser.Navigate(url);
                        await navigateTcs.Task;
                        return _webBrowser.Document.Body.OuterHtml;
                    }
                }
                finally
                {
                    _webBrowser.DocumentCompleted += handler;
                }
            },
            cts.Token);

            // show the HTML of the downloaded page
            MessageBox.Show(html);
        }

        void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            // destroy the WebBrowser
            _apartment.Run(
                () => _webBrowser.Dispose(),
                CancellationToken.None).Wait();

            // shut down the appartment
            _apartment.Dispose();
        }
    }

    /// <summary>MessageLoopApartment</summary>
    public class MessageLoopApartment : IDisposable
    {
        Thread _thread; // the STA thread

        TaskScheduler _taskScheduler; // the STA thread's task scheduler

        public TaskScheduler TaskScheduler { get { return _taskScheduler; } }

        /// <summary>MessageLoopApartment constructor</summary>
        public MessageLoopApartment()
        {
            var tcs = new TaskCompletionSource<TaskScheduler>();

            // start an STA thread and gets a task scheduler
            _thread = new Thread(startArg =>
            {
                EventHandler idleHandler = null;

                idleHandler = (s, e) =>
                {
                    // handle Application.Idle just once
                    Application.Idle -= idleHandler;
                    // return the task scheduler
                    tcs.SetResult(TaskScheduler.FromCurrentSynchronizationContext());
                };

                // 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();
            });

            _thread.SetApartmentState(ApartmentState.STA);
            _thread.IsBackground = true;
            _thread.Start();
            _taskScheduler = tcs.Task.Result;
        }

        /// <summary>shutdown the STA thread</summary>
        public void Dispose()
        {
            if (_taskScheduler != null)
            {
                var taskScheduler = _taskScheduler;
                _taskScheduler = null;

                // execute Application.ExitThread() on the STA thread
                Task.Factory.StartNew(
                    () => Application.ExitThread(),
                    CancellationToken.None,
                    TaskCreationOptions.None,
                    taskScheduler).Wait();

                _thread.Join();
                _thread = null;
            }
        }

        /// <summary>A wrapper around Task.Factory.StartNew</summary>
        public Task Run(Action action, CancellationToken token)
        {
            return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler);
        }

        /// <summary>A wrapper around Task.Factory.StartNew to run lambdas with a result</summary>
        public Task<TResult> Run<TResult>(Func<TResult> action, CancellationToken token)
        {
            return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler);
        }

        /// <summary>A wrapper around Task.Factory.StartNew to run async lambdas</summary>
        public Task Run(Func<Task> action, CancellationToken token)
        {
            return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler).Unwrap();
        }

        /// <summary>A wrapper around Task.Factory.StartNew to run async lambdas with a result</summary>
        public Task<TResult> Run<TResult>(Func<Task<TResult>> action, CancellationToken token)
        {
            return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler).Unwrap();
        }

    }
}

这篇关于在类库WebBrowser控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆