Few days ago, I had a presentation in the Warsaw .NET Group about PDB files. To make the slideshow more attractive (or rather original:)), I decided to replace the PowerPoint with something more connected with the subject, such as… the Visual Studio debugger. The only question was how to actually achieve this. The idea grew in my head after reading a brilliant Mike Stall’s post about debugging dynamically generated code. Mike shows in it how to emit sequence points for a text file of your choice and so bind this file with the generated MSIL. If done correctly, the debugger will load the text file, allowing you to step through its content and at the same time, execute the emitted MSIL. So what if we could use the presentation table of contents as our source file and for each title (line of the table of contents) emit MSIL which will present a corresponding slide in the console window? This will actually make the debugger play the role of the PowerPoint. :)
Prepare the Content
Let’s start from a table of contents for a hypothetical 3-slides presentation:
Welcome
1. Slide1
1.1 Slide1.1
2. Slide2
Thank you
Save this content as a toc.txt file – this will be our source file. Now let’s prepare slides. You may create a sub-directory slides
or even create separate directories for each group of slides – as you will shortly see, there is no restriction here. In our case, let’s stay with the one-directory-for-all approach. Let’s now assume that we want some contents to be presented for slides entitled: Welcome, 1.1 Slide1.1, 2. Slide2 and Thank you (we omit 1. Slide1 on purpose). As mentioned previously, the slides will be presented on the console so we may safely store their contents in text files named adequately: welcome.txt, slide1.1.txt and slide2.txt. Example content for a welcome.txt might be as follows:
SAMPLE PRESENTATION
===================
Sebastian Solnica
http://lowleveldesign.wordpress.com
Now, we need to prepare a mapping file that will map the slide title with its corresponding content. Create a new text file and fill it with the following text:
Welcome
slides\welcome.txt
1.1 Slide1.1
slides\slide1.1.txt
2. Slide2
slides\slide2.txt
Thank you
thank-you.txt
Each indented line stores a path to the content of the slide with the title above it. The titles must be the same as in the toc.txt file and the file paths must be valid (however, as you can see, not all titles need to be mapped).
Create the Presentation Compiler
Having the presentation content ready, we now need to create an EXE file that will be run by the debugger. I suppose that the easiest approach is to use classes from the System.Reflection.Emit
namespace. The pseudo code for the compiler (or rather emitter) will be as follows:
var asm = new DynamicAssembly()
MakeDebuggable(asm)
var mappingsContent = ReadFile(toc_slides.txt)
var mappings = StoreMappingsInDictionary(mappingsContent)
for each slideTitle in GetContent(toc.txt) {
if (mappings.Contains(slideTitle)) {
EmitMsilCodeThatWillReadAndPrintTheSlide(mappings[slideTitle]);
MarkSequencePoint(currentLineNum);
}
}
SaveAssembly(asm)
I will now explain few methods that occur in the code above that might have confused you (the C# source code that implements the above logic can be downloaded from here). MakeDebuggable
is responsible for adding a DebuggableAttribute
to the assembly. This attribute informs the JIT compiler that it should produce assembly code that will be debuggable (more here). To make the debugging process possible, we also need to create a PDB file that will store the debug information for our module. ModuleBuilder
contains a special method called DefineDocument
that will do this for us. Here’s a C# code that implements all the logic described:
Type daType = typeof(DebuggableAttribute);
ConstructorInfo daCtor = daType.GetConstructor
(new Type[] { typeof(DebuggableAttribute.DebuggingModes) });
CustomAttributeBuilder daBuilder = new CustomAttributeBuilder(daCtor, new object[] {
DebuggableAttribute.DebuggingModes.DisableOptimizations |
DebuggableAttribute.DebuggingModes.Default });
assemblyBuilder.SetCustomAttribute(daBuilder);
ModuleBuilder module = assemblyBuilder.DefineDynamicModule
(outputFileName, true);
ISymbolDocumentWriter doc = module.DefineDocument
(tableOfContentsPath, Guid.Empty, Guid.Empty, Guid.Empty);
EmitMsilCodeThatWillReadAndPrintTheSlide
emits MSIL that will open the slide file, read it at once and print the content to the console. Ildasm the code below and you will get it :) (or check emitReadAndPrintSlideMethod
in Program.cs from the slide compiler source code):
using (StreamReader reader = new StreamReader
(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)))
{
Console.Clear();
Console.Write(reader.ReadToEnd());
}
To make the assembly smaller, you may consider putting this logic in a separate method and emit just calls to this method with adequate parameters (this is what I do in the sample code). For each slide printed, we need to inform the debugger which line of the source code (or table of contents in our case:)) should be highlighted. For this purpose, we use MarkSequencePoint
method of the ILGenerator
instance:
ilGenerator.MarkSequencePoint(doc, currentLineNum, 1, currentLineNum, 100);
To make the debugging even more feasible, I emit System.Diagnostics.Debugger.Break()
call after the first sequence point (so even when starting with F5, the debugger should stop on the first slide).
Compile and Show :)
By now, we should have the presentation compiler and the presentation content (table of contents, slide files and slide title <-> slide file mapping file) ready. So it’s time to compile it and debug it. :) If you are using my compiler, run the following in the command window:
SlideCompiler.exe -files toc_slides.txt -o toc.exe toc.txt
This should produce a toc.exe file (and toc.pdb) that you can now open in Visual Studio (File->Open->Project/Solution…). After pressing F5, you should see something like:
By pressing F11 or F10, you may advance the slides. To move back, just select the line before the slide you want to show, press Ctrl+Shift+F10 and then F10. You may also place breakpoints on your slides.
The code (and the sample presentation as well as my symbol presentation from WG.NET) can be found on my blog samples site.
Have fun and surprise your listeners on your next presentation!
Filed under: CodeProject, Debugging, Visual Studio