你能从Application.DoEvents()内部捕获异常吗?

发布于 2024-07-15 03:55:22 字数 1631 浏览 10 评论 0原文

我遇到过在 VS2005 中运行的程序和直接运行可执行文件之间的奇怪差异。 本质上,当在 Application.DoEvents() 调用内的方法中引发异常时,可以在 Visual Studio 内运行时捕获该异常。 当运行编译后的可执行文件时,异常未被捕获并且程序崩溃。

这是一些简单的代码来演示该问题。 假设标准 winforms 样板和两个按钮和一个标签。

要运行此程序,请单击开始按钮开始 10 秒计数。 在 10 秒过去之前,按下中止按钮。 并且在 DoEvents() 内部会抛出异常。 应该捕获异常。 仅当在 Visual Studio 中运行时才会发生这种情况。

    private void StartButton_Click(object sender, EventArgs e) {
        DateTime start = DateTime.Now;

        try {
            while (DateTime.Now - start < new TimeSpan(0, 0, 10)) {
                this.StatusLabel.Text = DateTime.Now.ToLongTimeString();
                Application.DoEvents();
            }

            MessageBox.Show("Completed with no interuption.");
        } catch (Exception) {
            MessageBox.Show("User aborted.");                
        }
    }

    private void ButtonAbort_Click(object sender, EventArgs e) {
        throw new Exception("aborted");
    }

我希望能够捕获这些异常。 有什么办法让它发挥作用吗?

更新:

我愿意考虑除导致重入性头痛的 DoEvents() 之外的其他方法。 但我还没有找到一种似乎效果更好的方法。 我的情况是,我有一个长时间运行的循环正在控制一些科学仪器,并且经常必须等待温度稳定或其他什么。 我想让我的用户能够中止该进程,因此我有一个中止按钮,它只是抛出一个自定义异常,我打算在最初启动该进程的站点捕获该异常。 这似乎是一个完美的解决方案。 除了由于某种原因它不起作用这一事实。

如果无法使其发挥作用,是否有更好的方法?

更新2:

当我将其添加为Main()的第一行时,这使得它可以作为可执行文件工作,但不能在VS中工作,因此情况相反。 疯狂的是,它似乎是一个空操作。 我能理解这是如何做的。

Application.ThreadException += delegate(
        object sender, 
        System.Threading.ThreadExceptionEventArgs e
    ) 
    { throw e.Exception; };

疯了吧。

I've encountered a strange difference between a program running in VS2005 and running the executable directly. Essentially, when an exception is thrown in a method inside an Application.DoEvents() call, the exception can be caught when running inside Visual Studio. When running the compiled executable, the exception is not caught and the program crashes.

Here is some simple code to demonstrate the problem. Assume standard winforms boilerplate and two buttons and a label.

To run this, click the start button to start the 10 second count. Before 10 seconds elapses, press the abort button. and an exception will be thrown inside the DoEvents(). The exception should be caught. This only happens when running inside Visual Studio.

    private void StartButton_Click(object sender, EventArgs e) {
        DateTime start = DateTime.Now;

        try {
            while (DateTime.Now - start < new TimeSpan(0, 0, 10)) {
                this.StatusLabel.Text = DateTime.Now.ToLongTimeString();
                Application.DoEvents();
            }

            MessageBox.Show("Completed with no interuption.");
        } catch (Exception) {
            MessageBox.Show("User aborted.");                
        }
    }

    private void ButtonAbort_Click(object sender, EventArgs e) {
        throw new Exception("aborted");
    }

I want to be able to catch these exceptions. Is there any way to make it work?

Update:

I'm willing to consider approaches other than the re-entrant-headache-inducing DoEvents(). But I haven't found one that seems to work better. My scenario is that I have a long running loop which is controlling some scientific instruments, and frequently has to wait for a temperature to stabilize or something. I want to give my users the ability to abort the process, so I have an abort button that simply throws a custom exception, that I intend to catch at the site where the process is originally kicked off. It seemed to be a perfect solution. Except for the fact that it doesn't work for some reason.

If it's not possible to get this to work, is there a better approach?

Update 2:

When I add this as the first line of Main(), that makes it work as an executable, but not in VS, so the situation is reversed. The crazy thing is that it appears to be a no-op. I can understand how this does anything.

Application.ThreadException += delegate(
        object sender, 
        System.Threading.ThreadExceptionEventArgs e
    ) 
    { throw e.Exception; };

This is insane.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

苹果你个爱泡泡 2024-07-22 03:55:22

您真的必须使用DoEvents吗? 它会导致重新进入,这可能很难调试。

我怀疑,如果您从应用程序中删除可重入性,您将能够更轻松地找出在哪里捕获异常。

编辑:是的,肯定有更好的方法来做到这一点。 在不同的线程上执行长时间运行的任务。 UI 线程应该执行UI 操作。 让您的“中止”按钮设置一个长期运行的任务定期检查的标志。 有关 WinForms 线程的示例,请参阅我的 WinForms 线程页面,以及
波动性页面 是一个线程将标志设置为被另一个线程观看。

编辑:我刚刚记得 BackgroundWorker(我的文章没有涵盖 - 它是在 .NET 2.0 之前编写的)有一个 CancelAsync 方法 - 基本上是这个(与 CancellationPendingWorkerSupportsCancellation 基本上处理“有一个要设置取消的标志”的内容只需从工作线程中检查 CancellationPending ,然后从中止按钮点击处理程序中调用 CancelAsync 即可。

Do you really have to use DoEvents? It leads to re-entrancy which can be very hard to debug.

I suspect that if you remove the re-entrancy from your application, you'll be able to work out where to catch the exception more easily.

EDIT: Yes, there's definitely a better way of doing this. Do your long-running task on a different thread. The UI thread should only be doing UI operations. Make your "abort" button set a flag which the long-running task checks regularly. See my WinForms threading page for an example of WinForms threading, and
the volatility page for an example of one thread setting a flag to be watched by another thread.

EDIT: I've just remembered that BackgroundWorker (which my article doesn't cover - it was written pre-.NET 2.0) has a CancelAsync method - basically this (used with the CancellationPending and WorkerSupportsCancellation which basically deals with the "have a flag to set to cancel" stuff for you. Just check CancellationPending from the worker thread, and call CancelAsync from your abort button click handler.

梦言归人 2024-07-22 03:55:22

因为 DoEvents 会导致底层消息循环到泵,它可能会导致重新进入(如 MSDN 中声明),这将使您的应用程序的行为变得不可预测。

我建议您将工作(等待温度稳定)移至线程中,并使用某种信号来告诉用户界面等待结束的时间。 这将使您的用户界面保持响应,而无需使用 Application.DoEvents

Because DoEvents causes the underlying message loop to pump, it can lead to re-entrancy (as stated in MSDN) which will make the behaviour of your application unpredictable.

I would suggest that you move your work (waiting for a temperature to stabilize) into a thread and use a signal of some kind to tell your user interface when the wait is over. This will allow your user interface to remain responsive without having to use Application.DoEvents.

谁的年少不轻狂 2024-07-22 03:55:22

我认为您也许能够使用 Application.ThreadException 事件捕获异常。

首先你必须设置
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 就在应用程序启动时、创建任何控件之前。 然后添加 ThreadException 事件的事件处理程序。

如果这不起作用,我建议使用 BackgroundWorker 这意味着您不需要使用 DoEvents 并且它也是更好的解决方案。

编辑:在 ThreadException 处理程序的示例中,您抛出了另一个异常。 这个想法是你把异常处理代码放在那里,而不是引发另一个异常。

不过,我仍然强烈建议您使用 BackgroundWorker 来执行循环代码。

I think you might be able to catch the exception using the Application.ThreadException event.

To do it first you'll have to set
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); right at the start of the application, before any controls get created. Then add the event handler for the ThreadException event.

If this doesn't work I'd recommend using a BackgroundWorker which means you won't need to use DoEvents and it would also be better solution.

EDIT: In your example of the ThreadException handler you're throwing another exception. The idea was that you put your exception handling code in there, not raise another exception.

However I'd still urge you to use a BackgroundWorker to execute your loop code.

童话 2024-07-22 03:55:22

并不是想提起旧消息,而是因为我一直在努力解决同样的问题并且没有找到解决方案。 如果您运行后台工作程序并且您的用户回到主应用程序线程中工作并执行其他操作,并且他们想要运行位于后台工作程序上的相同功能,Application.DoEvents() 似乎是最好的方法,您仍然可以用它。 问题在于您在 RunWorkerCompleted 事件中有代码。 当您使用 CancelAsync 取消工作线程本身时,RunWorkerCompleted 中存在的任何内容都将被执行。 问题是,通常 RunWorkerCompleted 中的任何内容都在访问 GUI,而您在运行 Application.DoEvents() 时无法访问 GUI,从 RunWorkerCompmleted 中删除代码并将其合并到 ReportProgress 函数中更可接受。 但是,您需要在 reportprogress 函数之前抛出检查事件,以确保主线程不会请求另一次运行。 因此,在reportprogress之前,类似这样的事情:

if (backgroundWorker1.CancellationPending == true){
     e.Cancel = true;
     return;
}
backgroundWorker1.ReportProgress(0);

因此,您的后台线程正在完成其所有工作,然后在您拥有ReportProgress之前,将此项检查放入其中。 这样,如果您的用户说嘿,我实际上想在另一个项目上运行此查询,他们将在循环中等待类似这样的内容:

if (backgroundWorker1.IsBusy == true){
     backgroundWorker1.CancelAsync();
     While(backgroundWorker1.CancellationPending == true){
          Application.DoEvents();
     }
}

虽然这显然是一个不好的做法,但使用它可以摆脱异常。 我对此进行了全面搜索,直到我开始修改我正在处理的一个项目,发现它出错的唯一原因是因为它同时访问主线程,我相信。 可能会因此而受到批评,但有一个答案。

Not trying to bring up old news but because I have struggled with this same problem and didnt find a solution. If your running a background worker and your users are back in the main application thread working and doing other stuff and they want to run that same function that lies on the background worker, Application.DoEvents() seems like the best way and you can still use it. The problem lies within the fact that you have code in the RunWorkerCompleted event. When your cancelling the worker itself with CancelAsync anything that exists within RunWorkerCompleted is going to get executed. The problem is that usually whatever is in your RunWorkerCompleted is accessing the GUI and you cant access the GUI while running Application.DoEvents(), removing your code from RunWorkerCompmleted and incorporating it into the ReportProgress function is more acceptable. However, you need to throw check events right before your reportprogress function to make sure the main thread is not requesting another run. So something like this right before reportprogress:

if (backgroundWorker1.CancellationPending == true){
     e.Cancel = true;
     return;
}
backgroundWorker1.ReportProgress(0);

So you background thread is doing all its work and then right before you have it ReportProgress throw this check in there. This way if your user has said hey I actually want to run this query on another item they would be waiting in a loop with something like this:

if (backgroundWorker1.IsBusy == true){
     backgroundWorker1.CancelAsync();
     While(backgroundWorker1.CancellationPending == true){
          Application.DoEvents();
     }
}

While this is apparently a bad practice to use it accomplishes getting rid of the exception. I searched all over for this until I started modifying a project I was working on and saw that the only reason it was erroring was because its accessing the main thread at the same time, I BELIEVE. Probably will get flamed for this but there is an answer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文