Reflection
Now we’re finally going to spend some time looking at Reflection.
As mentioned in a previous post, .NET assemblies include intermediate language instructions plus metadata about types, members and assembly references. It is possible to access this information through Reflection.
For another definition of Reflection, here’s what MSDN has to say about System.Reflection:
The System.Reflection namespace contains types that retrieve information about assemblies, modules, members, parameters, and other entities in managed code by examining their metadata. These types also can be used to manipulate instances of loaded types, for example to hook up events or to invoke methods. To dynamically create types, use the System.Reflection.Emit namespace.
Just as COM uses Type Libraries to store type information, .NET has type information stored directly in the assemblies themselves (which is also possible with COM, but not essential). This rich type information can be accessed programmatically using Reflection. In fact my first ever AutoCAD .NET sample used reflection to go through and “dump” information about the various types in AutoCAD’s managed API to a tree control: it’s called DrawingBrowser, and can be found under samples/dotNet/DrawingBrowser on the ObjectARX SDK.
Just to use as an example, here's the VB code that makes up the main BROWSEDWG command in this sample (I didn't edit the code to fit the width of this blog, so it looks a little messy):
<Autodesk.AutoCAD.Runtime.CommandMethod("BrowseDWG")> _
Public Sub BrowseDrawing()
Dim dlg As New BrowseDialog()
dlg.Show()
dlg.TreeView1.BeginUpdate()
Cursor.Current = Cursors.AppStarting
dlg.TreeView1.Nodes.Clear()
Dim rootNode As TreeNode = dlg.TreeView1.Nodes.Add("Database")
ParseDWG.fillIcons(dlg.TreeView1)
Dim myT As Transaction = HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction()
rootNode.Text = HostApplicationServices.WorkingDatabase.Filename
ParseDWG.ExtractObjectInfo(rootNode, HostApplicationServices.WorkingDatabase)
myT.Commit()
ParseDWG.Cleanup()
Cursor.Current = Cursors.Default
'dlg.TreeView1.ExpandAll()
dlg.TreeView1.EndUpdate()
End Sub
Reflector
The Reflection sub-system was also originally used to develop the Reflector tool – an incredibly useful piece of software written by Lutz Roeder that is available from http://www.aisto.com/roeder/dotnet/ - that really takes the lid off .NET. Reflector no longer uses reflection, interestingly – apparently it makes use of unmanaged APIs to access the same information, but I’m not sure of the reason for this change.
So what’s so great about this tool? Well, it enables you to:
- Assess what other people might be able to glean from your own assemblies, should they choose to take a look.
- Learn from people who are happy for you to be looking at how they do things (see below for examples of that).
- Optimise your own coding by looking at the IL created by various bits of code – it also helps you understand what programming techniques are functionally equivalent (but perhaps easier to understand - examples are the use of foreach or using, functions that are functionally equivalent to more longwinded approaches).
Here’s what MSDN Magazine says about Reflector:
Using .NET Reflector, you can browse the classes and methods of an assembly, you can examine the Microsoft intermediate language (MSIL) generated by these classes and methods, and you can decompile the classes and methods and see the equivalent in C# or Visual Basic® .NET.
So let's use Reflector to take a look at the code in the DrawingBrowser sample, to see what's visible if we don't obfuscate. I loaded the built assembly into Reflector, and browsed down to the code, using Tools->Disassembler to bring up a view on the disassembled source (or IL, if you'd rather see that):
The code you can retrieve with the tool is almost identical to that found in the source project, other than the fact that comments and variable names have been removed (the variable names have been replaced with generic ones based on the datatype).
For fun, let's see what we can see once we've obfuscated the assembly using the Dotfuscator Community Edition...
A few tips on Dotfuscating AutoCAD .NET assemblies: I found it easiest to set "Copy Local" to false on the references to the standard AutoCAD assemblies in the project (acmgd.dll and acdbmgd.dll), and then set the output location to be the AutoCAD program folder. This allowed the tool to resolve the various references properly.
Otherwise I just used the standard settings and picked the obfuscated assembly from the \Dotfuscated subdirectory of the config folder in the settings.
Loading it in Reflector, you can see straight away it's harder to make sense of. Just finding the appropriate command method took some time:
The code is suddenly a lot harder to follow - especially as it makes use of a number of classes defined in the project (which have been renamed to have incomprehensible names).
So let's look at the second major use for the Reflector - learning from existing assemblies. Here's some more information from the MSDN Magazine article about Reflector:
The .NET Framework offers many different ways to perform similar operations. For example, if you need to read a set of data from XML, there are a variety of different ways to do this using XmlDocument, XPathNavigator, or XmlReader. By using .NET Reflector, you can see what Microsoft used when writing the ReadXml method of the DataSet, or what they did when reading data from the configuration files. .NET Reflector is also an excellent way to see the best practices for creating objects like HttpHandlers or configuration handlers because you get to see how the team at Microsoft actually built those objects in the Framework.
This is a very interesting point, and one that is very relevant to Autodesk’s own managed implementation. The AutoCAD Engineering team does not obfuscate its managed code, for instance, as it sees value in developers being able to take a look at it, to learn by example.
There are clearly potentially cases where there’s a much greater need to obfuscate code – licensing systems are one example (here’s an interesting article on that topic), but as AutoCAD’s licensing subsystem is in unmanaged code that isn't an issue.
If you use the reflector on acmgd.dll and acdbmgd.dll you’ll come across one or two areas of interest – where we’re doing more than a straight pass-through to the underlying ObjectARX code – but it’s generally not very exciting. Here's an example of one of the relatively few actual functions in the standard assemblies:
As you can see, even that is a little difficult to understand, as it uses some quite obscure-looking datatypes and much dereferencing of pointers.
Other managed components yield more interesting information, however (the ones that actually make use of the managed layer). One example is AcLayerTools.dll. Loading this assembly into Reflector shows some quite juicy chunks of code:
So that's about it for this post... Hopefully you're able to see the potential for this tool, and it also reinforces the importance of thinking about which code you should protect and which you need not.
As a final note, for those that are interested Reflector also exposes an API that can be used in applications, according to another MSDN Magazine article.