Archive for February, 2013

Tuesday, February 19th, 2013

One of the most vivid memories I have from reading the “The Pragmatic Programmer” is the tidbit about code generation it felt like my eyes were opened to this new and exotic world. All of a sudden the DRY principle had a new weapon (to be fair I think this was the point of that section of “The Pragmatic Programmer”). Up until that point I never thought about writing a code generator.

The biggest reason for amazement was that at the time I was in the middle of developing a Windows service to kick off Microsoft Office automation tasks. I came up with a decent pattern for spinning up an external program, doing work, and ending the external process. There was retry logic with a WatchDog that added some reliability to an inherently unreliable task. The problem was that the pattern was not abstracted very well (if I was solving the problem today I would have gone for a completely different pattern, probably implementing something more like CQRS like the eMoney Nexus; live and learn). In order to keep DRY I needed to

  1. Rewrite my service (that I just demoed as working) with better encapsulation or
  2. Use code generation!

I opted for code generation.

From memory the pseudocode for the basic pattern that I wanted to reuse for different external programs:


// Initialize the office process
private Process word = new Process(...);
public void DoWork(string method)
{
// Start up the office program
word.Start();

// Spin up the watch dog
ThreadPool.QueueUserWorkItem(new WaitCallback(Watchdog, word));

// Find the method the caller wants to invoke via reflection
MethodInfo m = this.GetType().GetMember(method)[0];
m.Invoke(this, null);
}

static void ThreadProc(Watchdog stateInfo)
{
// waits for a timeout, then check process state
// if process is still running first try to stop
// the process, if that doesn't work try to kill
// the process. Then check to see if the worker
// thread is still around, and tell it to quit
// as a last resort use Thread.Abort()
}

Basically the pattern started an external program, started a watchdog, then called a method via reflection. Anytime somebody wanted to write a new task for the external program to do they would just need to write a new method in the class and call DoWork with the name of their method. It worked great when dealing with the same process. The issue was when wanting to use excel, or acrobat, or any other external program this whole pattern would need to be reimplemented with the logic that knew how to spin up that particular program, and have method that worked with that program as well.

What I came up with was this whole pattern could be injected into the class as compile time. I changed the basic files to look like this:


private Process word = new Process(...);
public void DoWork(string method) { /***** DOWORK *****/ }

This allowed anybody writing a method inside the class to still get IntelliSense on the object they wanted to work with, and they could still call DoWork. The rest of the pattern I moved to an external console program that knew how to open the class file, find the “public void DoWork(string method) { /***** DOWORK *****/ }” string and drop in custom code per external process. Something like this:


private static readonly string wordWatchDog = @"
private Process word = new Process(...);
public void DoWork(string method)
{
// Start up the office program
word.Start();

// Spin up the watch dog
ThreadPool.QueueUserWorkItem(new WaitCallback(Watchdog, word));

// Find the method the caller wants to invoke via reflection
MethodInfo m = this.GetType().GetMember(method)[0];
m.Invoke(this, null);
}

static void ThreadProc(Watchdog stateInfo)
{
// waits for a timeout, then check process state
// if process is still running first try to stop
// the process, if that doesn't work try to kill
// the process. Then check to see if the worker
// thread is still around, and tell it to quit
// as a last resort use Thread.Abort()
}"
private static readonly magicString = "public void DoWork(string method) { /***** DOWORK *****/ }";
public void UpdateWordDotCS()
{
string wordDotCSAsString = ReadWordFile();
int magicStringStart = wordDotCSAsString.IndexOf(magicString);
using (FileStream fs = File.Open(wordDotCSFile, FileMode.Create))
{
byte[] file = ASCII.ASCIIEncoding.GetBytes(wordDotCSAsString);
// Write beginning of file
fs.Write(file, 0, magicStringStart);

// Inject our custom code
byte[] customCode = ASCII.ASCIIEncoding.GetBytes(wordWatchDog);
fs.Write(customCode, 0, customCode.Length);

// Write beginning end file
fs.Write(file, magicStringStart + magicString.Length, file.Length - magicStringStart - magicString.Length);
}
}

Then our release build process was able to first call the console program that did the code generation before it started to build the actual solution.

This first foray into code generation while crude cemented code generation as a tool in my toolbox of how to get stuff done. Since this first pass I went on to make a few more code generators.

  • A custom C# project templates with default code.
  • A program that reads a DB and creates C# classes to populate the tables.
  • An app that could read a copy/pasted table for a spec on how to read a 3rd party file and generate a class that would allow the 3rd party file to be serialized into our class.

None of these generators were particularly difficult to write and the pay off in terms of productivity was noticeable. Which makes me wonder why I don’t see more people writing generators. My theory is twofold, first I think code generation just does not come to people’s mind (which is odd since 99% of the coding we do is to simplify other work, why not simplify our own coding) and second I think the term ‘code generation’ sounds scary to people.

My next few posts will go into more details on the aforementioned code generators to illustrate how they really are not scary, and to show how there is more than one way to generate code.