none
How to catch FileNotFoundException when missing one of referenced assemblies RRS feed

  • Question

  • Hi all,

    I'm developing a simple application that references an 3rd party assembly.
    In case this assembly isn't installed in GAC, I want to display my custom error message and close the application.

    My problem is that FileNotFoundException is thrown before the Main function is called, so I can't handle this issue by the good way and windows Send/Don't Send dialog is displayed.

    Is there any way to resolve this issue and programmatically close the application before FileNotFoundException is thrown?

    Thanks,
    Michael.

    Saturday, June 7, 2008 4:32 PM

Answers

  • > My problem is that FileNotFoundException is thrown before the Main function is called

    Just the fact that a referenced assembly is missing does not cause an application to crash immediately on startup.  Typically, the error happens as soon as a method that makes use of the assembly is called.

    If the problem is that the Main method is making use of the referenced assembly, you could try refactoring your Main method:

        static void Main()  
        {  
          try 
          {  
            OldMain();  
          }  
          catch (System.IO.FileNotFoundException)  
          {  
            MessageBox.Show("Caught FileNotFoundException");  
          }  
        } 

    There are also the AppDomain.UnhandledException and Application.ThreadException events that are raised when unhandled exceptions occur.  You might be able to use these events to gracefully shut down your application.
    Saturday, June 7, 2008 5:22 PM

All replies

  • > My problem is that FileNotFoundException is thrown before the Main function is called

    Just the fact that a referenced assembly is missing does not cause an application to crash immediately on startup.  Typically, the error happens as soon as a method that makes use of the assembly is called.

    If the problem is that the Main method is making use of the referenced assembly, you could try refactoring your Main method:

        static void Main()  
        {  
          try 
          {  
            OldMain();  
          }  
          catch (System.IO.FileNotFoundException)  
          {  
            MessageBox.Show("Caught FileNotFoundException");  
          }  
        } 

    There are also the AppDomain.UnhandledException and Application.ThreadException events that are raised when unhandled exceptions occur.  You might be able to use these events to gracefully shut down your application.
    Saturday, June 7, 2008 5:22 PM
  • Hi BinaryCoder,
    Thanks for your reply.

    This is my Main function:



    using ExternalAssemblyNameSpace;
    ...........


    static void Main(string[] args)
    {

        //Check Input Command Line Parameters
        if (args.Length != 4)
        {
            MessageBox.Show("Error", "Error");
            Environment.Exit(100);
        }
    }


    I run my application without any command line params.
    When I run this application while an referenced assembly is present in the same folder - I get the expected message.
    When I remove referenced assembly from the folder - I get windows exception.
    As could be seen from the code above, there is no any usage of referrenced assembly withing this code.

    Is there any chance that "using" derective is the sorce for the problem?

     

    • Edited by MishaSoft Saturday, June 7, 2008 6:05 PM An additional observation was made and updated in the post.
    Saturday, June 7, 2008 6:00 PM
  • BC is right, you won't get an exception until the JIT compiler tries to load the assembly to compile the code in its class methods.  I cannot reproduce your problem.  There must be something inside that assembly that somehow gets initialized at startup.  No clue what that could be.  Beware of static classes and field initializers.  There must be a reason you didn't just remove the using statement...
    Hans Passant.
    Saturday, June 7, 2008 7:15 PM
    Moderator
  • If you are not sure that the third-party assembly exists, you should do the following simple thing: do not ever refer the classes directly from that assembly. Here an explanation in samples:

    tip #1:

    replace the following code

    class MyClass {
      SomeTypeFromThirdPartyAssembly variableName;

      void foo() {
        variableName.DoSomething();
      }
    }

    with this one

    class MyClass {
      object variableName;

      void foo() {
        try {
          InternalFoo();
        }
        catch (FileNotFoundException) {
          // The runtime coulnd't find an assembly.
        }

        catch (TypeLoadException) {
          // The runtime coulnd't find referenced type in the assembly.
        }

      }

      private void InternalFoo() {
        ((SomeTypeFromThirdPartyAssembly)variableName).DoSomething();
      }
    }

    First, the JIT compiler will not resolve the type references of class members.
    Second, the JIT compiler will not resolve variable references in 'foo' method.


    tip #2:

    When CLR cannot find an assembly it raises 'AssemblyResolve' event on AppDomain class.
    ...
    AppDomain
    .CurrentDomain.AssemblyResolve += new ResolveEventHandler
    (CurrentDomain_AssemblyResolve);
    ...
    Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs
    args) {
      //
    args.Name - the name of the missing assembly.
      return null
    ;
    }


    tip #3:

    When an excpetion occurs in the main message loop of your forms application, and it's doesn't have any handlers, you can catch it by 'Application.ThreadException ' event:
    ...
    void ThreadException(object sender, ThreadExceptionEventArgs e) {
      // write a handler here
      .....
      // or throw it further
      throw e.Exception;
    }

    But note, if the exception occurs in 'Application.Idle' event handler, it will not be catched and will not be handled with 'Application.ThreadException' event handler.


    tip #4:

    If you want to track all unhandled exception in your program, you should advise 'AppDomain.UnhandledException' event.
    ...
    AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledException);
    ...
    void UnhandledException(object sender, UnhandledExceptionEventArgs
    e) {

      if
    (e.ExceptionObject is ThreadAbortException)
        // We shouldn't worry about threads being aborted, when some code calls 'Thread.Abort' mehtod,
        // because runtime injects opcodes with throwing this type of exception in the code.
        return;

     
    // log an exception here
      ......
      // But be aware!, that the exception may come from any thread.
      // That means, this method is never synchronized with the main thread.

      if (e.
    IsTerminating) {
        // This flag tells us whether the further program execution is impossible,
        // and CLR will terminate the program. I.e. a critical exception has occured.
      }
    }


    That's all you should know,
    Good luck :)

    Sunday, June 8, 2008 12:35 AM
  •  Hi all,

    Thanks to everyone for your assistnace, but it still doesn't work for me (including tip#1 provided by The most weird OX ever )
    I don't understand why you fail to reproduce the problem - it means that I have an additional problem on my side.

    I reproduce the problem by following steps:

    1. Created new Solution with Console Application (ConsoleApplication1).
    2. Add to the same solution Class Library project.
    3. Define strong-name signing for the ClassLibrary1 projects
    3. The ClassLibrary1 project has the single class Class1:

    using System;  
    using System.Collections.Generic;  
    using System.Text;  
     
    namespace ClassLibrary1  
    {  
        public class Class1  
        {  
            public string GetMessage()  
            {  
                return "This message was returned by Class1";  
            }  
        }  
    }  
     

    4. Add to the ConsoleApplication1 project reference to the ClassLibrary1 project output
    5. Edit Program.cs file of the ConsoleApplication1 project as following:

    using System;  
    using System.Collections.Generic;  
    using System.Text;  
     
    using ClassLibrary1;  
     
    namespace ConsoleApplication1  
    {  
        class Program  
        {  
            static void Main(string[] args)  
            {  
                if (args.Length < 2)  
                {  
                    Console.WriteLine("Hello World");  
                    Environment.Exit(0);  
                }  
     
                Class1 cls = new Class1();  
                string msg = cls.GetMessage();  
                Console.WriteLine(msg);  
            }  
        }  
    }  
     

    6. Thats it.

    Now, when I run ConsoleApplication1.exe while ClassLibrary1.dll is present in the same directory - every thing works as expected:
                - When ConsoleApplication1.exe is called without any command prompt params - "Hello World" is displayed.
                - When 2 command prompt params passed to ConsoleApplication.exe - "This message was returned by Class1" is displayed.

    But when I copy ConsoleApplication1.exe to an another folder, where ClassLibrary1.dll is missing, I get FileNotFound exception in the both cases (with command prompt params and without).
    Following the exception details:

    Unhandled Exception: System.IO.FileNotFoundException: Could not load file or ____  
    embly 'ClassLibrary1, Version=1.0.0.0, Culture=neutralPublicKeyToken=eca1358d5 
    898b7f7' or one of its dependencies. The system cannot find the file specified.  
    File name: 'ClassLibrary1, Version=1.0.0.0, Culture=neutralPublicKeyToken=eca1 
    358d5898b7f7' at ConsoleApplication1.Program.Main(String[] args)  
     

    Any ideas?

    Thanks,
    Michael.




    Sunday, June 8, 2008 3:52 PM
  • You've ignored the advice given before in your repro code.  What's the hang-up?
    Hans Passant.
    Sunday, June 8, 2008 4:52 PM
    Moderator
  • Thanks to everyone for assistance,
    I've found the problem out.

    The main idea was to export the code, referring to types provided by external assembly, to the separate function. The call to this function should be enclosed within try...catch block.

    If I understood correct, JIT compiles the whole function just a moment before it's called. So referring external types within the Main function throws exception when Main is being compiled (before any code starts to run).

    I've mistakenly thought that exception should be thrown during the call itself, and not during function compilation.

    Following working code:

     static void Main(string[] args)  
     {  
           if (args.Length < 2)  
           {  
              Console.WriteLine("Hello World");  
              Environment.Exit(100);  
           }  
     
           try 
           {  
              InternalFoo();  
           }  
           catch (FileNotFoundException)  
           {  
              Console.WriteLine("The runtime coulnd't find an assembly.");  
           }  
           catch (TypeLoadException)  
           {  
              Console.WriteLine("The runtime coulnd't find referenced type in the assembly.");  
           }  
    }  
     
    static void InternalFoo()  
    {  
         ExternalType variableName = new ExternalType();  
         Console.WriteLine(variableName.anyMethod());  
    }  
     


    Thanks,
    Michael.
    Monday, June 9, 2008 8:54 AM
  • I made the same mistaken assumption. Once I split the Main() in two, I was able to catch the exception.

    Many Thanks! (I love Google) ;-)
    Tuesday, September 22, 2009 2:08 PM
  • Great post - thanks!
    Monday, October 25, 2010 1:45 PM