views:

97

answers:

4

I have a WinForms application. It's written to disk. I run the application from disk and eject the disk from CD. Then I take the exception: there is no disk in the drive. please insert a disk into drive. How can I catch this exception and correctly close my application?

+1  A: 

You can modify your Program.cs like this:

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        try
        {
            Application.Run(new Form1());
        }
        catch (Exception ex)
        {
            // Display the exception to the end user.
        }
        finally
        {
            // Do your cleanup here.
        }
    }

This should catch all exceptions you do not catch otherwise in the application itself.

Pieter
It actually wont catch system and thread exceptions.
honibis
use a finally instead of a catch block. finally blocks will be hit regardless of system and thread exception.
Femaref
You're right; updated answer.
Pieter
+4  A: 

You should try to detect cd eject. Here is a working sample: publicjoe.f9.co.uk/csharp/snip/snip002.html

you can also check out this: http://stackoverflow.com/questions/1662794/detecting-eject-insert-of-removeable-media

honibis
Application.ThreadException doesn't catch this exception. The message is appear, but Application_ThreadException doesn't invoked.
Rover
Did you add UnhandledException handler too? This message wont be displayed itself if you handle it.
honibis
I tried to add AppDomain.CurrentDomain.UnhandledException. It also doesn't catch this exception.
Rover
edited the answer, adding SetUnhandledExceptionMode. You can also consider detecting cd eject.
honibis
Same problem. I take the error message and can't close application by X button. But now i take some exceptions in .._Exception methods. But it appear only after unneeded MessageBox.
Rover
This is the only way to handle system exceptions(as soon as i know). You should try to detect cd eject. Here is a working sample: http://www.publicjoe.f9.co.uk/csharp/snip/snip002.html
honibis
Checking of ejecting of CD is working. You can change your post and leave only links on detection of CD ejection and I mark your answer as accepted. Thanks for help! :)
Rover
The answer is edited. You welcome :)
honibis
+3  A: 

This is a generic problem with any kind of program, not specific to a Winforms app. It is related to the way a process is created. Windows creates a so-called memory mapped file, it maps the bytes in the file into the address space of the process. It is a very efficient way to read data from a file, a disk read only occurs if the data is actually needed. When program execution jumps to a specific chunk of code, or the JIT compiler needs the IL for a method, a page fault is generated when the data hasn't been read yet. The operating system solves it by reading a chunk from the file. You only pay for code that actually runs.

Another major benefit is when the system is under pressure and too much RAM is needed to keep processes running. The memory manager chucks pages out of RAM to make room for the process that needs the CPU. These pages are normally written to the paging file. But that's unnecessary when they came from the EXE file. It can simply discard them and always get them back by re-reading the file.

You can probably see where this leads: loading pages from the EXE file can't work anymore when you popped out the disk. Windows notices this and puts up the dialog. This is a very low level error handling mechanism, you cannot trap it in the process itself. That couldn't work since that would require executing code in your program. Code that cannot be loaded from disk anymore.

You might be able to suppress the error by pinvoking SetErrorMode(). Not sure, never tried it. But that doesn't really solve anything, the next best thing that could happen is that Windows terminates the program with an obscure error. The only reasonable solution is either to let the user put the disk back, as requested by Windows, or to run an installer from your media so that a copy of the program is created on the hard drive.

Hans Passant
+2  A: 

Use EDITBIN /SWAPRUN to tell Windows that your executable resides on a removable device. It'll copy it to the swap file before running it.

This is what you'd usually do for (e.g.) an installer. I'm not sure if this works if you depend on other DLLs, but I assume it does.

For other resources, you'd want to copy them to a temporary location as soon as possible during initialization.

Alternatively, you can call SetErrorMode to disable the message box and handle the error correctly.

Roger Lipscombe
Thanks! It's very interesting idea, but my application has many external resources. In any case i should close the program.
Rover
Fair enough. Updated with some more details in case anyone else finds this useful.
Roger Lipscombe