O Manifest resources, where are thou ? 1
Hum, what about going back to work ?
I’ve worked the past day on Manifest Resources for Cecil. Manifest Resources are another tricky thing in the .net framework. System.Reflection.Emit v1.x does not provides methods for embedding them in a fresh new assembly, so mcs have to use some hacks that only work on the Mono runtime. Cecil will be able to emit them, but first, let’s try to read them from a raw assembly.
First of all, they are called Manifest Resources, so I would except them to be saved in the assembly manifest. This is what we can find in the second partition of the ECMA CLI standard:
The ManifestResource table has the following columns:
- Offset (a 4 byte constant)
- Flags (a 4 byte bitmask of type ManifestResourceAttributes, clause )
- Name (index into the String heap)
- Implementation (index into File table, or AssemblyRef table, or null; more precisely, an Implementation coded index)
So if the Implementation metadata token points to a File row, it is a linked resource, if it is null, it is an embedded resource. We can guess that if it points to an AssemblyRef row, it would mean that resource is embedded into another assembly.
My problem is that whatever tool I’ve used, it seems that manifest resources are defined per module, not per assembly manifest. I have implemented a per module resource reading system in Cecil, but I don’t know how to implement the “AssemblyRef” case. Does someone have any idea about this, or any test case where an implementation token points to an AssemblyRef row ?
Cecil talks 4
A long time I have not blogged. Mainly spent my time working on Cecil. Better than a long talk, here is an example of what Cecil is able to do now. There is a name for programs that write themselves, I’ve forget it, and my example is not really one of them. My example is a very simple C# console program, but it will print its CIL representation, almost like ildasm.
I’ve not included the source and the result on the blog, because they are not really short. The source is viewable here : C# source. We simply load an assembly using Cecil, then we print the content of the methods. Easy no ? Let’s take a look at the output : here.
I have still a lot of work to do, and the API is not really stable, but I’m close to what I want. Once I’ll get it, I think I’ll call for contribution on this blog, to achieve the writing part of Cecil. Stay tuned !
Cecil Feed 3
I’ve promised, here are some news of Mono.Cecil :
Mono.Cecil is now a top level module of Mono’s Subversion repository. It may be accessible from here, once the module will be mirrored to the anonymous SVN.
Included in the module is a little Gtk# Application, Cecil Feed, that for now, is just a modification of one of the samples bundled with Gtk#. The old sample was simply looking into assemblies, and diplayed some of their contents in a TreeView. I’ve juste adapted the sample and here is a screen shot of the beast :
And for curious peoples, here is the source of this sample application :
<span class="rem">// TreeView.cs - Fun TreeView demo</span>
<span class="rem">//</span>
<span class="rem">// Author: Kristian Rietveld <kris@gtk.org></span>
<span class="rem">//</span>
<span class="rem">// (c) 2002 Kristian Rietveld</span>
<span class="rem">// A rewrite of the TreeView demo using Cecil</span>
<span class="rem">// Author: Jb Evain <jb@evain.net></span>
<span class="kwrd">namespace</span> Cecil.Feed {
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> Gtk;
<span class="kwrd">using</span> Mono.Cecil;
<span class="kwrd">public</span> <span class="kwrd">class</span> TreeViewDemo : IReflectionStructureVisitor {
<span class="kwrd">private</span> <span class="kwrd">string</span> m_file;
<span class="kwrd">private</span> TreeStore m_store = <span class="kwrd">null</span>;
<span class="kwrd">private</span> <span class="kwrd">int</span> m_count = 0;
<span class="kwrd">private</span> TreeIter m_cursor;
<span class="kwrd">private</span> <span class="kwrd">bool</span> m_newBranch = <span class="kwrd">false</span>;
<span class="kwrd">private</span> <span class="kwrd">bool</span> m_root = <span class="kwrd">true</span>;
<span class="kwrd">public</span> TreeViewDemo(<span class="kwrd">string</span> file) {
m_file = file;
DateTime start = DateTime.Now;
Application.Init ();
PopulateStore ();
Window win = <span class="kwrd">new</span> Window(<span class="str">"Cecil ~ Feed"</span>);
win.DeleteEvent += <span class="kwrd">new</span> DeleteEventHandler(<span class="kwrd">this</span>.DeleteCB);
win.SetDefaultSize(640,480);
ScrolledWindow sw = <span class="kwrd">new</span> ScrolledWindow ();
win.Add (sw);
TreeView tv = <span class="kwrd">new</span> TreeView(m_store);
tv.HeadersVisible = <span class="kwrd">true</span>;
tv.EnableSearch = <span class="kwrd">false</span>;
tv.AppendColumn (<span class="str">"Name"</span>, <span class="kwrd">new</span> CellRendererText (), <span class="str">"text"</span>, 0);
tv.AppendColumn (<span class="str">"Type"</span>, <span class="kwrd">new</span> CellRendererText (), <span class="str">"text"</span>, 1);
sw.Add (tv);
win.ShowAll ();
Console.WriteLine (m_count + <span class="str">" nodes added."</span>);
Console.WriteLine (<span class="str">"Startup time: "</span> + DateTime.Now.Subtract (start));
Application.Run ();
}
<span class="kwrd">public</span> <span class="kwrd">void</span> Visit(IAssemblyDefinition asm) {
AddToTree(m_file, <span class="str">"AssemblyDefinition"</span>);
}
<span class="kwrd">public</span> <span class="kwrd">void</span> Visit(IAssemblyName name) {
AddToTree(name.FullName, <span class="str">"AssemblyName"</span>);
}
<span class="kwrd">public</span> <span class="kwrd">void</span> Visit(IAssemblyNameReferenceCollection names) {
m_newBranch = <span class="kwrd">false</span>;
}
<span class="kwrd">public</span> <span class="kwrd">void</span> Visit(IAssemblyNameReference name) {
AddToTree(name.FullName, <span class="str">"AssemblyNameReference"</span>);
}
<span class="kwrd">public</span> <span class="kwrd">void</span> Visit(IResourceCollection resources) {
m_newBranch = <span class="kwrd">false</span>;
}
<span class="kwrd">public</span> <span class="kwrd">void</span> Visit(IEmbeddedResource res) {
AddToTree(res.Name, <span class="str">"EmbeddedResource"</span>);
}
<span class="kwrd">public</span> <span class="kwrd">void</span> Visit(ILinkedResource res) {
AddToTree(res.Name, <span class="str">"LinkedResource"</span>);
}
<span class="kwrd">public</span> <span class="kwrd">void</span> Visit(IModuleDefinition module) {
AddToTree(module.Name, <span class="str">"ModuleDefinition"</span>);
}
<span class="kwrd">public</span> <span class="kwrd">void</span> Visit(IModuleDefinitionCollection modules) {
m_newBranch = <span class="kwrd">true</span>;
}
<span class="kwrd">public</span> <span class="kwrd">void</span> Visit(IModuleReference module) {
AddToTree(module.Name, <span class="str">"ModuleReference"</span>);
}
<span class="kwrd">public</span> <span class="kwrd">void</span> Visit(IModuleReferenceCollection modules) {
m_newBranch = <span class="kwrd">false</span>;
}
<span class="kwrd">private</span> <span class="kwrd">void</span> PopulateStore () {
m_store = <span class="kwrd">new</span> TreeStore (<span class="kwrd">typeof</span> (<span class="kwrd">string</span>), <span class="kwrd">typeof</span> (<span class="kwrd">string</span>));
IAssemblyDefinition asm = AssemblyFactory.GetAssembly(m_file);
asm.Accept(<span class="kwrd">this</span>);
}
<span class="kwrd">private</span> <span class="kwrd">void</span> AddToTree(<span class="kwrd">string</span> name, <span class="kwrd">string</span> type) {
<span class="kwrd">if</span> (m_newBranch) {
m_cursor = m_store.AppendValues(m_cursor, name, type);
m_newBranch = <span class="kwrd">false</span>;
} <span class="kwrd">else</span> {
m_store.AppendValues(m_cursor, name, type);
}
<span class="kwrd">if</span> (m_root) {
m_cursor = m_store.AppendValues(name, type);
m_root = <span class="kwrd">false</span>;
}
m_count++;
}
<span class="kwrd">private</span> <span class="kwrd">void</span> DeleteCB(<span class="kwrd">object</span> o, DeleteEventArgs args) {
Application.Quit ();
}
<span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> Main (<span class="kwrd">string</span>[] args) {
<span class="kwrd">if</span> (args.Length == 0) {
Console.WriteLine(<span class="str">"usage: cecil-feed.exe assembly"</span>);
<span class="kwrd">return</span>;
}
<span class="kwrd">try</span> {
TreeViewDemo tvd = <span class="kwrd">new</span> TreeViewDemo(args[0]);
} <span class="kwrd">catch</span> (Exception e) {
Console.WriteLine(e);
}
}
}
}
Ok, it is very simple, but once I’ll work on Cecil a bit more, we can imagine to have here on the first Open Source ildasm clone. By the way, this example shows how it is simple with the design pattern visitor to walk through Cecil’s objects. Using this, it should be very easy to write some little tools like peverify, or even write optimizers.
What do you think you would do with a library to manipulate CIL files ?
DotNetGuru contributes to Mono, or the contrary 5
<span class="kwrd">using</span> Mono.Cecil;
Tonight, Cecil will leave SourceForge’s CVS to a better place, in the sun.
Cecil’s development will now be supported by Mono’s SVN. By the way, its name change a little bit. It is now Mono.Cecil, hey, sounds good no ?
Thanks to Miguel De Icaza, I have received a write access to Mono’s SVN last night. Cecil will be a top module until it is mature enough to be bundled with others Mono’s assemblies.
I’m proud, but a lot of work remains, Cecil isn’t really usable, but I hope that its new status will improve the speed of the devel, and will bring more feedbacks and bugs reports.
Last but not least, this blog may be syndicated within the Monologue, by this way the world may discovers AspectDNG, and others DNG activities I’ll blog on.
I’ll post as soon as migration is ended, to give you instructions to access Cecil’s repository. I may blog later on some screen shots of a little Gtk# application, using Cecil, that mimics ildasm, but it is really simple and limited to Cecil progress.
Isn’t the year 2005 beginning very well ?
