ARTICLE
Debugging a Compiled Component
After an assembly is compiled into a dll in a release mode, it is very difficult to gather information from it such as runtime performance, parameters values, etc.
After reading this article and looking at the source code, I hope you realize why to have strong name assemblies (Strong Name Assembly Article).
After an assembly is compiled into a dll in a release mode, it is very difficult to gather information from it such as runtime performance, parameters values, etc. Although there are products that will insert logging functionality into your code, very few will allow for viewing or even MODIFYING the values of the parameters in production builds.
Uses
1) Determining performance of production components.
2) Debugging on production servers by viewing parameters passed
3) Intercepting certain method calls on a 3rd party compiled component and replacing the functionality with your own.
4) Watching how a 3rd party assembly and consumer work together from an interface point of view.
Basic Requirements for WrapIT
1) Use reflection to interrogate the targeted dll.
private Assembly _assemblyInstance;
_assemblyInstance = Assembly.LoadFrom(_assemblyFulePath);
Type[] types = _assemblyInstance.GetTypes();
foreach (Type t in types)
{....}
2) Build a new assembly that has the same method signature. Rename the original one and name the new one the same as the old name. Thus clients that just load up a private assembly by dll name will call the new one. This is the default behavior in .Net without string named assemblies.
In this version I just write the new component using a string bulder. I am converting it now to use the code dom to produce the code.
3) Intercept calls to the assembly and hand of to my Trace Comoponent to perform logging, parameter tracking, etc.
Intercept interC = new Intercept();
interC.methodCallBegin(\"" + methinfo.Name + "\" ,args);
interC.methodCallEnd(\"" + methinfo.Name + "\" ,args,tim);
4) Forward the intercepted request to the original assembly by dynamic invocation.
_prog.Append("" + Environment.NewLine + "\t\t asm = Assembly.LoadFrom (@\"" + "original" + _assemblyBaseName + ".dll" + "\");");
_prog.Append("" + Environment.NewLine + "\t\t myType = asm.GetType(\"" + t.Namespace + "." + t.Name + "\");");
_prog.Append("" + Environment.NewLine + "\t\t localObjRef = Activator.CreateInstance (myType);");
................
//call the method
if (methinfo.ReturnType.ToString() == "System.Void")
{
_prog.Append( Environment.NewLine + "\t\t\t myType.InvokeMember (\"" + methinfo.Name + "\",BindingFlags.Default | BindingFlags.InvokeMethod,null, localObjRef,args);");
}
else
{
_prog.Append( Environment.NewLine + "\t\t\t ret = myType.InvokeMember (\"" + methinfo.Name + "\",BindingFlags.Default | BindingFlags.InvokeMethod,null, localObjRef,args);");
}
Result
I built a simple component A1.dll (included in source code) that had a public method for adding two numbers as follows:
public int add(int a, int b)
{
int C=1;
for(int s = 0;s<10000000; s++)
{
C = s;
}
return a+b;
}
I then created a console based consumer Verify.exe (included in source code).
After running wrapit you get a new A1.dll and a new originala1.dll .
running the verify.exe and a text file is created like the following:
calling add
Params
Int32: 1
Int32: 2
call add ended in 00:00:00.0156252
To test it out yourself:
Open the zip into a folder. Copy the A1.dll to c:\ Open a Dos Prompt Navigate to your folder Run Test.bat
Whats Left to Do ?
Add all base types into Call Tracer for output of there values. Output the return type.
Build a better logging / reporting mechanism.
If you use it, add to it, or want to receive updates to the program drop me an email at wdenton@adelphia.net. I would love to see how it is used by others. If you use it for malicious reasons, dont let me know or Ill probably report it.