locked
Delete Dll w/o closing the application

    Question

  • Hello,

    I am trying to create, execute and delete dll’s at runtime in c#. I was able to create and execute dll’s at runtime but couldn’t delete without closing the application. This is what i am trying to do in my application, If the file gets outdated. When a user tries to run it, its compile replace the old dll with the new one. At this point I get the error “Could not change or delete file’abcd.dll’, the file is being used by other process”. Now  I need to close to app to free the dll. Which I don’t want to do ..

     I did refer to MSDN help.. i found out i need use appdomain to load and unload dll's to delete at runtime. i tried that but i am getting this exception could not load file or assembly 'tc0013', Version=0.0.0.0, Culture=neutral, PublicKey Token= null' or one of its dependencies. The system cannot find the file specified.":"tc0013".

    namespace autotester

    {

      public partial class frmMain : Form

      {

        TestcaseList tcList =  new TestcaseList();

     

         // if file is outdated or missing

         if ((File.GetLastWriteTime("C:\\TestClass\\" + fileName + ".dll").CompareTo(defaultDate) == 0) ||  (File.GetLastWriteTime("C:\\TestClass\\" + fileName + ".dll")) < (File.GetLastWriteTime(scriptfile)))

         {

            tcList.DeleteTestCase(fileName.Substring(2, 4));

            RunScript(scriptfile, fileName, true);

         }

         else

         {

            If(RunScript(scriptfile, fileName, false))

            tcList.SetSelected(fileName.Substring(2, 4), true);

         }         

     

         // compile and add dll’s to the testcase list

        public bool RunScript(string scriptfile, string scriptname, bool compile)

        {

         bool retval = true;

         CodeDomProvider csp = CodeDomProvider.CreateProvider("CSharp");

         CompilerParameters cp = new CompilerParameters();

         System.CodeDom.Compiler.TempFileCollection tfc = new TempFileCollection(".", false);

         CompilerResults cr = new CompilerResults(tfc);

     

         string scripttype = "autotester" + "." + scriptname;

     

         // Build the output assembly path

         cp.OutputAssembly = "C:\\TestClass\\" + scriptname + ".dll";

         cp.ReferencedAssemblies.Add(System.IO.Path.GetDirectoryName(Application.ExecutablePath) +"\\autotester.exe");

        cp.WarningLevel = 3;

        cp.CompilerOptions = "/target:library /optimize";

        cp.GenerateExecutable = false;

        cp.GenerateInMemory = false;

     

             AppDomain domain = AppDomain.CreateDomain("MyDomain");

       assembly =

    Assembly.LoadFrom(cp.OutputAssembly);

        if (compile == true)

        {

         try

           {

             cr = csp.CompileAssemblyFromFile(cp, scriptfile); // compile file

     

             if (cr.Errors.Count > 0)

             {

                foreach (CompilerError ce in cr.Errors)

                   Console.WriteLine(scriptname.ToString + ce.ErrorText.ToString() + ce.Line.ToString());

                   retval = false;

                   return retval;

             }

            }

            catch (DirectoryNotFoundException)

            {

               Console.WriteLine(" ERROR: File Not Found at " + scriptfile);

            }

            catch (FileNotFoundException)

            {

               Console.WriteLine(" ERROR: File Not Found at " + scriptfile);

            }

          }
          ObjectHandle instance = domain.CreateInstance(assembly.FullName, scripttype); -> i Get could not load file or assembly 'tc0013' exception in this line but i see the 'tc0013.dll @ the specified path'
          object unwrap = instance.Unwrap(); 
          testClass = (Testcase)assembly.CreateInstance(scripttype);
                    

     

           if (testClass != null)
          {
            if (!tcList.GetTestCase(scriptname.Substring(2, 4)))
            tcList.AddCase(testClass);
            scriptcount++;
          }
          AppDomain.Unload(domain);

          return retval;

       }

      }

    }

     

    namespace autotester

    {

        class TestcaseList

        {

           private static List<Testcase> l = new List<Testcase>();

     

           public TestcaseList()

            {

                m_Iterations = 1;

                m_PassCount = m_FailCount = 0;

                m_CurrentCaseIndex = 0;

                m_TestRunInProgress = false;

            }

     

            public void AddCase(Testcase tc)

            {

                JobConfigComparer tcc = new JobConfigComparer();

     

                l.Add(tc);

                l.Sort(tcc);

            }

     

            public void DeleteTestCase(string strTestCaseNumber)

            {

                foreach (Testcase tc in l)

                {

                    if (tc.GetNumber().StartsWith(strTestCaseNumber))

                    {

                        l.Remove(tc);

                        break;

                    }

                }

            }

     

            public void TestThread()

            {  

                                foreach (Testcase tc in l)

               {

                   m_tc = tc;

                   if (tc.ExecuteTestcase() == true)

                   {

                       m_PassCount++;

                   }

                   else

                   {

                       m_FailCount++;

                   }

             }

    }

    What i am doing wrong?? how to make this work??

    Thank you.

    Monday, February 22, 2010 3:52 PM

Answers

  • Another option is to never load the actual assembly into the domain. 

    byte[] bytes = File.ReadAllBytes(@"C:\Users\billybob\Documents\Visual Studio 2008\Projects\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug\WindowsFormsApplication1.exe");
    Assembly assm = Assembly.Load(bytes);

    This leaves the assembly on disk completely unrelated to the current application.
    Coding Light - Illuminated Ideas and Algorithms in Software
    Coding Light WikiLinkedInForumsBrowser
    • Proposed as answer by Driem Monday, February 22, 2010 9:57 PM
    • Marked as answer by vJAIY Tuesday, February 23, 2010 8:29 PM
    Monday, February 22, 2010 5:13 PM

All replies

  • Can't be done. As long as you're referencing the DLL from your original application, and as long as you've used any of the types provided in that DLL, that DLL is locked.  You'll have to somehow close the application to delete the DLL.

    You can accomplish this by creating an application that performs the following steps:

    1. Application 1 starts.  It needs to delete a referenced DLL.
    2. Application 1 runs Application 2.
    3. Application 2 waits for application 1 to quit.
    4. Application 1 quits.
    5. Application 2 notices application 1 has quit, and deletes the DLL. It might download an updated DLL at this point and place it in the proper directory.
    6. Application 2 runs Application 1, then quits.

    Either way you go, the application referencing the DLL has to release the file lock, which is only done by quitting the app.
    Coding Light - Illuminated Ideas and Algorithms in Software
    Coding Light WikiLinkedInForumsBrowser
    Monday, February 22, 2010 3:58 PM
  • You need to load the assembly in an object created in another AppDomain.
    That way, your original appdomain won't get any reference to the loaded dll.
    After you unload the apdomain where the dll was loaded you can delete the dll.

    Here's code to call one method in a dll and delete it.

    public class Loader : MarshalByRefObject
    {
        object CallInternal(string dll, string typename, string method, object[] parameters)
        {
            Assembly a = Assembly.LoadFile(dll);
            object o = a.CreateInstance(typename);
            Type t = o.GetType();
            MethodInfo m = t.GetMethod(method);
            return m.Invoke(o, parameters);
        }
        public static object Call(string dll, string typename, string method, params object[] parameters)
        {
            AppDomain dom = AppDomain.CreateDomain("Volatile");
            Loader ld = (Loader)dom.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Loader).FullName);
            object result = ld.CallInternal(dll, typename, method, parameters);
            AppDomain.Unload(dom);
            File.Delete(dll);
            return result;
        }
    }
    Monday, February 22, 2010 5:10 PM
  • Another option is to never load the actual assembly into the domain. 

    byte[] bytes = File.ReadAllBytes(@"C:\Users\billybob\Documents\Visual Studio 2008\Projects\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug\WindowsFormsApplication1.exe");
    Assembly assm = Assembly.Load(bytes);

    This leaves the assembly on disk completely unrelated to the current application.
    Coding Light - Illuminated Ideas and Algorithms in Software
    Coding Light WikiLinkedInForumsBrowser
    • Proposed as answer by Driem Monday, February 22, 2010 9:57 PM
    • Marked as answer by vJAIY Tuesday, February 23, 2010 8:29 PM
    Monday, February 22, 2010 5:13 PM
  • Thanks you guys.. you saved my day..
    Tuesday, February 23, 2010 8:30 PM