We will create a windows form to browse through a .Net assembly. The program lists the methods contained in the assembly selected by the user and the parameter name and parameter type for each assembly.
The example also illustrates the use of the File control in helping the user to select a particular type of file.
Step 1: Create the User Interface
Create a new Windows Application Project of type Visual C#. In the default new windows form, drag and drop the following controls from the ToolBox:
Refer to Figure 1.Label:
- Change Text property to Assembly
- TextBox: Change Name property to txtFile
- Button: Change Text property to
- Button: Change Text property to Reflect
- Label: Change Text property to Reflection
- TreeView: Change the Name property to AssemblyBox
- OpenFileDialog
Figure 1: User Interface for our form
Step 2: Import Namespaces
We will need to import these namespaces for accessing the reflection classes.
using System.Reflection;
using System.Reflection.Emit;
Step 3: Allow the user to select the Assembly File
The user inputs in this case are a valid assembly file name and a type name. The file name should be a valid file - with extension .dll or .exe. We use the OpenFileDialog control to get the user to select a file name of the specified type and display the selected value in the textbox adjacent to it.
Function to allow user to select a file is shown below: Double click on button1 and add the following code to its OnClick event handler.
private void button1_Click(object sender, System.EventArgs e)
{
openFileDialog1.CheckFileExists= true;
openFileDialog1.CheckPathExists = true;
openFileDialog1.Filter ="Exe files (*.exe)|*.exe|Dll files (*.dll)|*.dll|All files (*.*) *.*"
openFileDialog1.Multiselect = false;
openFileDialog1.ReadOnlyChecked = true;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
txtFile.Text = openFileDialog1.FileName;
}
}
Step 4: Code to dissect the assembly
Let us focus on the function that does all the work of verifying the assembly specified by the user in the form input. The user interface accepts a valid file name of type .dll or .exe and a type name. When the user clicks OK, the tree in the user interface is populated with the Fields, Properties and Methods in the type. We start by loading the assembly and querying for all types within the assembly. Using Reflection classes, we get the information on the fields, properties, methods and their parameters for each Type found in the assembly.
In our sample code listing, we have included a dummy class TestClass and a dummy field nMember in the main class. For testing the sample, you can run the sample file and select the executable file created for our sample itself and see how it looks (Figure 2).
Comments are included in the code listing below to explain the approach at every step.
private void Reflect()
{
Assembly assembly;
Type[] types;
int nIndex;
AssemblyBox.BeginUpdate();
//Clear the tree
AssemblyBox.Nodes.Clear();
try
{
//Load the assembly and get all the types.
assembly = Assembly.LoadFrom(txtFile.Text);
types = assembly.GetTypes();
}
//Display error if unable to create/load the assembly.
catch(FileNotFoundException)
{
MessageBox.Show("Could not load Assembly: {0}", txtFile.Text);
return;
}
catch(TypeLoadException)
{
MessageBox.Show("Error in loading types");
return;
}
catch(Exception ex)
{
MessageBox.Show("Error occured" + ex.Message);
return;
}
nIndex=0;
AssemblyBox.Nodes.Add("Assembly");
for (int i=0;i < types.Length;i++)
{
//Add the root node of the tree as the type name.
AssemblyBox.Nodes[0].Nodes.Add(new TreeNode(types[i].Name));
//Add the child node of the root with the text "Fields".
AssemblyBox.Nodes[0].Nodes[i].Nodes.Add(new TreeNode("Fields"));
//Fetch the Fields of the type in the specified assembly. //Note:This will fetch only the public methods
FieldInfo[] fields = types[i].GetFields();
//Add the fields of the type as child nodes of the Fields node.
if(fields == null)
{
AssemblyBox.Nodes[0].Nodes[i].Nodes[0].Nodes.Add(new TreeNode("No Fields Found"));
}
else
{
foreach(FieldInfo f in fields)
{
AssemblyBox.Nodes[0].Nodes[i].Nodes[0].Nodes.Add(new TreeNode(f.Name + "(" + f.FieldType + ")"));
}
}
//Add the child node for Properties.
ssemblyBox.Nodes[0].Nodes[i].Nodes.Add(new TreeNode("Properties"));
//Fetch the properties of the type.
PropertyInfo[] properties = types[i].GetProperties();
//Add the Properties of the type in the assembly as child nodes of the Properties node in the tree.
if(properties == null)
{
AssemblyBox.Nodes[0].Nodes[i].Nodes[1].Nodes.Add(new TreeNode("No Properties Found"));
}
else
{
foreach(PropertyInfo p in properties)
{
AssemblyBox.Nodes[0].Nodes[i].Nodes[1].Nodes.Add(new TreeNode(p.Name + "(" + p.PropertyType + ")"));
}
}
//Add the Methods node in the tree.
AssemblyBox.Nodes[0].Nodes[i].Nodes.Add(new TreeNode("Methods"));
//Fetch the Methods of the type in the assembly.
MethodInfo[] methods = types[i].GetMethods();
//Add the Methods of the type in the assembly as child nodes.
if(methods == null)
{
AssemblyBox.Nodes[0].Nodes[i].Nodes[2].Nodes.Add(new TreeNode("No Methods Found"));
}
else
{
nIndex=0;
foreach(MethodInfo m in methods)
{
AssemblyBox.Nodes[0].Nodes[i].Nodes[2].Nodes.Add(new TreeNode(m.Name));
//For each method, fetch the parameters.
ParameterInfo[] parameters = m.GetParameters();
foreach(ParameterInfo param in parameters)
{
//Add each of the parameters in the tree.
AssemblyBox.Nodes[0].Nodes[i].Nodes[2].Nodes[nIndex].Nodes.Add(new TreeNode param.Name + "(" + param.ParameterType + ")"));
}
nIndex++;
}
}
AssemblyBox.EndUpdate();
}
}
Figure 2: The sample in action