首页 > 解决方案 > C#:在任务中打开 webbrowser --> 当前 STA 问题

问题描述

一般的:

  1. WPF 应用程序
  2. 使用 mahapps.metro
  3. 使用 mahapps 进度对话框

我想达到什么

  1. 打开 mahapps 进度对话框
  2. 在进度对话框中做一些事情
  3. 其中一项工作是为用户登录打开一个 webbrowser 元素 --> 登录后我得到了 cookie
  4. 仅在登录过程(任务)之后才做一些事情

当前问题

问题是,我必须为 mahapps 进度对话框创建一个任务,如下所示...

public async void startWork(view)
    {
        MahApps.Metro.Controls.MetroWindow window = Window.GetWindow(view) as MahApps.Metro.Controls.MetroWindow;
         if (window != null)
         {
             var controller = await window.ShowProgressAsync("Please wait...", "Progress message");
             controller.Maximum = 100;

             await Task.Run(() => {

                  Login.RunAuthentification(controller, IDApp, IDToken, BaseUrl);
             });
            await controller.CloseAsync();
         }}

RunAuthentification 中,我有一个 webbrowser 的功能,看起来像这样

public static async Task<string> openLogin(string _authorizationUrl)
    {
        string sessionId = null;

        if (_authorizationUrl.Length == 0)
        {
            throw new ArgumentException("Authorization url cannot be empty");
        }

        // The method should return based on an event.
        // We need a Semaphore which we can then be released in the event handler
        var signal = new System.Threading.SemaphoreSlim(0, 1);


        // Create a WindowsForms window
        var window = new System.Windows.Forms.Form
        {
            Text = "Please provide your login ...",
            Size = new System.Drawing.Size(800, 545)
        };

        // Create a WindowsForms WebBrowser element
        var browser = new System.Windows.Forms.WebBrowser
        {
            Dock = DockStyle.Fill,
            Url = new Uri(_authorizationUrl)
        };
        // Add the browser to the window
        window.Controls.Add(browser);

        // If the window is closed, release the signal so the async task will be completed
        window.Closed += (sender, args) => signal.Release();

        // We will listen for any change of the url and wait for the authorization code.
        // The browser might be redirected multiple times

        browser.DocumentCompleted += (sender, eventArgs) =>
        {
            Console.WriteLine(eventArgs.Url.ToString());

            if (eventArgs.Url.AbsolutePath.EndsWith("/confirmed"))
            {
                var cookies = browser.Document?.Cookie.Split(';')
                    .Select(keyValueStr => keyValueStr.Split('='))
                    .ToDictionary(arr => arr[0].Trim(), arr => arr[1].Trim());

                cookies?.TryGetValue("SESSION", out sessionId);
                window.Close();
            }
        };

        // Show the window
        window.ShowDialog();

        // Wait until the signal is set
        await signal.WaitAsync();
        return sessionId;
    }

现在我收到错误消息,我无法访问网络浏览器,因为实际线程不是 STA。有谁知道我如何优雅地解决这个问题?

标签: c#wpftaskmahapps.metro

解决方案


您将需要使用 Dispatcher 或 DispatcherSynchronizationContext 将您的任务中的调用“编组”到主 UI 线程。以下是使用 DispatcherSynchronizationContext 的方法:

public async void startWork(view)
{
    MahApps.Metro.Controls.MetroWindow window = Window.GetWindow(view) as MahApps.Metro.Controls.MetroWindow;
    if (window != null)
    {
        var controller = await window.ShowProgressAsync("Please wait...", "Progress message");
        controller.Maximum = 100;
         
        //capture the current SynchronizationContext
        var context = SynchronizationContext.Current;

        await System.Threading.Tasks.Task.Run(() =>
        {
            //use the context to marshal the call on the main UI thread
            context.Send((o) => Login.RunAuthentification(controller, IDApp, IDToken, BaseUrl), null);
        });

        await controller.CloseAsync();
     }
}

https://docs.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchersynchronizationcontext?redirectedfrom=MSDN

https://docs.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatcher


推荐阅读