C#:在任务中打开网络浏览器 --> 当前 STA 问题

C#: Open webbrowser within task --> currently STA problem

一般:

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

我想达到什么目的

  1. 打开 mahapps 进度对话框
  2. 在进度对话框中做一些事情
  3. 其中一项工作是为用户登录打开一个 webbrowser 元素 --> 登录后我取回 cookies
  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 中,我有一个用于网络浏览器的函数,看起来像这样

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。有谁知道我该如何优雅地解决这个问题?

您将需要使用 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