【发布时间】:2019-07-16 17:48:08
【问题描述】:
我有一个 C# WinForms 应用程序。
我正在使用 Unity Container nuget 包来注入我的服务类。如果我在Program.cs 中获取服务类并调用我的 Web api 以使用 odata 验证用户名和密码,它会成功运行。如果我调用 Application.Run(myForm),其中 myForm 是 Telerik RadForm,并异步运行相同的调用,应用程序将关闭且不抛出异常。
我正在使用Application.ThreadException 并注册Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException),但这并没有捕捉到任何东西。
导致应用程序崩溃的行是对System.Net.Http.HttpClient.PostAsync() 的调用。此调用是从位于单独的.Net Standard 2.0 Class Library 中的服务类中进行的。主应用程序是.NET Framework 4.7.2 Windows Application,UI 控件位于.NET Framework 4.7.2 Class Library。同样,如果在 Telerik RadForm 之外调用,此调用将成功。
在 Telerik RadForm 中,我尝试使用以下命令执行调用:
private async void btnLogin_Click(object sender, System.EventArgs e)var t = new Thread(() => Runner.DoWork(new Credentials(u, p, CLIENT_ID)))- 不使用
await对静态方法的异步调用
当我在服务类上调用LoginAsync(...).Result 时,我确实得到了不同的结果。这会导致应用程序进入线程锁定状态,但不会导致应用程序崩溃。
更新:
有两种 RadForm; DashboardForm 和 LoginForm。 Program.cs 启动仪表板以检查现有的 Bearer 令牌。如果令牌不存在,则仪表板使用.ShowDialog() 显示登录,以防止在通过身份验证之前使用仪表板。然后登录使用上述服务类。
如果不是使用Application.Run() 启动Program.cs 中的仪表板,而是启动登录,则用于身份验证的服务调用成功。
然后我尝试在新线程中从仪表板启动登录,但这导致了与上述相同的问题。
如果 System.Net.Http.HttpClient.PostAsync() 从另一个 Form 显示的 Form 调用,为什么会导致应用程序崩溃(没有例外)?
计划
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
WinInjectionHelper.Register(UnityConfig.RegisterComponents);
Application.ThreadException += Application_ThreadException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.Run(new DashboardForm());
}
private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
MessageBox.Show("Big error...");
}
仪表板
public partial class DashboardForm : Telerik.WinControls.UI.RadForm, IDashboard
{
private readonly IProductService _service;
public DashboardForm()
{
this._service = WinInjectionHelper.Generate2<IProductService>();
this.InitializeComponent();
}
private void DashboardForm_Load(object sender, EventArgs e)
{
if (this.NotAuthenticated())
{
var frm = new LoginForm();
if (frm.ShowDialog() != DialogResult.OK)
{
this.Close();
}
}
else
{
//TODO: display error to user
this.Close();
}
}
}
登录
public partial class LoginForm : Telerik.WinControls.UI.RadForm
{
private const string CLIENT_ID = "...";
private readonly IAuthenticationService _service;
public LoginForm()
{
this._service = WinInjectionHelper.Generate2<IAuthenticationService>();
this.InitializeComponent();
}
private void btnCancel_Click(object sender, System.EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
private async void btnLogin_Click(object sender, System.EventArgs e)
{
var u = this.txtUsername.Text.Trim();
var p = this.txtPassword.Text.Trim();
if (string.IsNullOrWhiteSpace(u))
{
MessageBox.Show("Username is required!", "NOTICE", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
if (string.IsNullOrWhiteSpace(p))
{
MessageBox.Show("Password is required!", "NOTICE", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
try
{
var jwt = await this._service.LoginAsync(new Credentials(u, p, CLIENT_ID));
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, u),
new Claim(ClaimTypes.Email, u),
new Claim(ClaimTypes2.AccessToken, jwt.AccessToken),
}, "JWT");
((GenericPrincipal)Thread.CurrentPrincipal).AddIdentity(identity);
this.DialogResult = DialogResult.OK;
this.Close();
}
catch
{
MessageBox.Show("An error occurred while processing your request.", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
【问题讨论】:
-
首先不要在新线程上打开表单,那只是自找麻烦。请发布minimal reproducible example 以准确显示正在使用的代码以及导致问题的原因。
-
就像 JSteward 所说,在任何线程上完成的任何与 UI 相关的操作都会使您的应用程序崩溃。至于为什么你没有得到异常,除非你使用等待或等待,否则返回任务的方法中抛出的异常不会冒泡。
-
OP,如果可能的话,您应该避免创建线程或使用
Thread类。处理这个问题的更现代(也更容易)的方法是使用任务和异步/等待。 -
正如我所说,尝试的第一件事是使用异步方法调用(一直到堆栈),并且每一层都包含在 try/catch 中。没有理由不捕获异常,但没有抛出异常。
标签: c# winforms crash httpclient