Sunday, September 6, 2009

Dynamic loading – debug a form without the program

This article started when I was in charge of building large project (30+ DLLs by the last count)

Once you're in such a project you want to check the work of a certain DLL without the rest of them getting in the way. Not to mention without passing through the log-in procedure and the parent (or parents in some cases) of the form. You need the quick and dirty method – load it, launch it.


Probably the most important lines of code can be summarized as follows:


System.Reflection.Assembly asmbly = System.Reflection.Assembly.LoadFile(DLL_Path);

Type tp = asmbly.GetType(FormName);

object obj = asmbly.CreateInstance(tp.FullName);


That's it, obj contains the form ready for whatever we want.

But, usually that's not enough (except for some weird cases where the constructor does everything.) So I've added some colors to the picture. First thing to do was show a list of forms. Note that I need only the FORMS.

Using a basic form, a textbox for the DLL's name, a "browse" button, a combo to be filled, and a "load DLL" button.

The load DLL function is:


private void LoadDLL_btn_Click(object sender, EventArgs e)

{

Cursor = Cursors.WaitCursor;

Application.DoEvents();

try

{

if (System.IO.File.Exists(DLL_Path_txt.Text))

{

System.Reflection.Assembly asmbly = System.Reflection.Assembly.LoadFile(DLL_Path_txt.Text);

System.Reflection.AssemblyName[] refs = asmbly.GetReferencedAssemblies();

string base_path = DLL_Path_txt.Text.Substring(0, DLL_Path_txt.Text.LastIndexOf('\\') + 1);

foreach (string fl in System.IO.Directory.GetFiles(base_path))

{

try

{

System.IO.File.Copy(fl, Application.StartupPath + fl.Substring(fl.LastIndexOf('\\')), true);

}

catch { }

}

FormNamespace_cmb.Items.Clear();

foreach (Type tp in asmbly.GetTypes())

{

if (tp.IsSubclassOf(typeof(System.Windows.Forms.Form)))

{

FormNamespace_cmb.Items.Add(tp.FullName);

}

}

}

}

catch (Exception ex)

{

MessageBox.Show(ex.ToString());

}

finally

{

Cursor = Cursors.Default;

}

}


Since this utility is for debugging purposes forget security, hiding the exception is like trying to put shoes on in the darkness.

Actually that's all, the only piece of code you need now is a "Fire", or in a nicer phrasing "Launch" button, which has the following code


private void Launch_btn_Click(object sender, EventArgs e)

{

Cursor = Cursors.WaitCursor;

Application.DoEvents();

try

{

if (System.IO.File.Exists(DLL_Path_txt.Text))

{

System.Reflection.Assembly asmbly = System.Reflection.Assembly.LoadFile(DLL_Path_txt.Text);

System.Reflection.AssemblyName[] refs = asmbly.GetReferencedAssemblies();

string base_path = DLL_Path_txt.Text.Substring(0, DLL_Path_txt.Text.LastIndexOf('\\') + 1);

Environment.CurrentDirectory = base_path;

Application.DoEvents();

Type tp = asmbly.GetType(FormNamespace_cmb.Text);

object obj = asmbly.CreateInstance(tp.FullName);

if (obj is Form)

{

Form frm = (Form)obj;

frm.ShowDialog();

}

else

{

MessageBox.Show(tp.FullName + " is not a System.Windows.Form, please choose a different object.");

}

}

}

catch (Exception ex)

{

MessageBox.Show(ex.ToString());

}

finally

{

Cursor = Cursors.Default;

}

}

That's all, this code works for me. I use it mainly when the need to debug some new "error" arises (Ahhhm… null references… Ahhhm…)

It's by far lighter than the whole application, and I can manage it without the fear of changing a vital part in the project.