C#:在任务中打开网络浏览器 --> 当前 STA 问题
C#: Open webbrowser within task --> currently STA problem
一般:
- WPF 应用程序
- 使用mahapps.metro
- 使用 mahapps 进度对话框
我想达到什么目的
- 打开 mahapps 进度对话框
- 在进度对话框中做一些事情
- 其中一项工作是为用户登录打开一个 webbrowser 元素 --> 登录后我取回 cookies
- 仅在登录程序(任务)后做一些事情
当前问题
问题是,我必须为 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.dispatcher
一般:
- WPF 应用程序
- 使用mahapps.metro
- 使用 mahapps 进度对话框
我想达到什么目的
- 打开 mahapps 进度对话框
- 在进度对话框中做一些事情
- 其中一项工作是为用户登录打开一个 webbrowser 元素 --> 登录后我取回 cookies
- 仅在登录程序(任务)后做一些事情
当前问题
问题是,我必须为 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.dispatcher