Thursday, December 18, 2008

Dynamically Compiling Assemblies with CodeDomProvider

A few posts ago, I mentioned the use of CodeDomProvider to compile assemblies at runtime. It is really easy to do.

First, you'll need to include a few namespaces you normally wouldn't:

using System.CodeDom.Compiler;
using Microsoft.CSharp;


Next, setting up the assembly compiler:

CodeDomProvider codeProvider = new CSharpCodeProvider();
CompilerParameters compilerParameters = new CompilerParameters();

compilerParameters.GenerateExecutable = false;
compilerParameters.GenerateInMemory = true;
compilerParameters.IncludeDebugInformation = false;
compilerParameters.WarningLevel = 3;
compilerParameters.TreatWarningsAsErrors = false;
compilerParameters.CompilerOptions = "/optimize";


We're using a CSharpCodeProvider here - there are other code providers for other .NET languages.

Our compiler parameters specify that we are not generating an executable, we want to compile the assembly directly in memory, rather than to disk, and we aren't including debugging information. The warning level determines the threshold for generating compiler warnings. I'm not sure exactly what level 3 does, but it's what I've seen referenced by default. It doesn't really matter, anyway, as we also specify that we aren't treating warnings as errors. Finally, we tell the compiler to optimize.

This next bit is really the only tricky part. We need to tell the compiler what extra assemblies we want to reference. The most general method I've found so far is simply to reference all of the assemblies that our parent program references, as well as the parent assembly itself:

Assembly executingAssembly = Assembly.GetExecutingAssembly();
compilerParameters.ReferencedAssemblies.Add(executingAssembly.Location);

foreach (AssemblyName assemblyName in executingAssembly.GetReferencedAssemblies())
{
compilerParameters.ReferencedAssemblies.Add(Assembly.Load(assemblyName).Location);
}


With the setup done, compiling an assembly is easy:

string assemblyCode = "public class MyClass { ...<Code Goes Here>... }";

CompilerResults compileResults =
codeProvider.CompileAssemblyFromSource(compilerParameters, new string[] { assemblyCode });

Assembly myAssembly = compileResults.CompiledAssembly;


That's all there is to it. Note that you should also check to see if and compile errors were generated. The CompileResults class has an Errors property that holds a collection of the compile errors, if any.

To use your shiny new assembly, simply use reflection to extract your class from the assembly:

Type myType = codeAssembly.GetType("MyClass");


You'll end up with a Type you can use like any other - using Activator.CreateInstance() to create a new instance of your class, for example.

Wednesday, December 17, 2008

WPF MediaElement Requires Updated Windows Media Player

I just spent several baffled hours trying to figure out why some WPF media playback code that worked just fine on my previous computer was failing to work on my new computer.

It turns out that it wasn't my code at all. It seems that the WPF MediaElement control (which is used to play content using Windows Media Player) doesn't work with WMP version 9 (which is what came installed on my new PC).

Upgrading to the latest (version 11) fixed the problem.

Monday, December 15, 2008

Interactive Function Plotting Using Runtime Compilation of C# Assemblies



Every once in a while I have a need to plot some arbitrary mathematical function to see what it looks like. It happened to me recently when working on my FM Synth project, so I decided to whip up a quick-and-dirty plotting app.

The main issue with creating the app was how to allow for specifying the function(s) to be plotted. Obviously, I did not want to write a custom mathematical function parser. Instead, I leveraged the ability of .NET to create assemblies at runtime using the CodeDomProvider class.

I already had some code to do this that I used to allow runtime evaluation of C# expressions in the console of my city driving game, and I was able to repurpose it with very little effort.

The result is pretty cool. Any number of functions can be specified as arbitrary C# expressions. I dynamically create a class, and add the expressions as methods within it. I also a a special property, 'X', which returns the current X value of the plot. As you can see below, you can call one function from within another and use multiple lines of code.



The start and end values for X are also arbitrary expressions.

The use of CodeDomProvider to generate assemblies at runtime is definitely a very powerful tool that I'm sure I'll find more uses for.

Friday, December 5, 2008

FM Synth W.I.P.



My little FM synth is coming along nicely. C# codebase with a WPF UI.

The shot above is the main editor page. I'm currently using four oscillators which can either be output directly, or used to modulate each other. Fun stuff.

Thursday, December 4, 2008

Changing the text style of a WPF GroupBox header

I wanted to change the style of the header of a WPF GroupBox today and had to do a little digging to figure out how to do it.

The GroupBox header is not limited to just text - it can be any control. Because of this, no header text styling options are available directly as properties of the GroupBox control. Instead, you need to modify the DataTemplate of the GroupBox header.

Here is a style that will make the GroupBox header text black and bold:

<Style x:Key="MyGroupBoxStyle" TargetType="{x:Type GroupBox}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="Black" FontWeight="Bold"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>


Note that this method is a bit of a hack, in that it assumes that your header is text. If it isn't, though, you've added your own custom controls to the header and you don't have a styling problem in the first place...

Wednesday, December 3, 2008

The KORG DS-10: A Pocket-Sized Retro-Synth

KORG DS-10

The KORG DS-10 is a new title for the Nintendo DS. It isn't a game, though - it is a faithful reproduction of the Korg MS-10 analog synthesizer.

It features six two-oscillator voices (two synths and four drums), a 16-step pattern editor and a mini song sequencer.

KORG DS-10

It also lets you patch stuff together and apply some simple effects.

KORG DS-10

The DS stylus works great as an interface - particularly when controlling the "KAOSS" pad (essentially an X/Y axis input device for interactively modifying pattern data and synthesizer parameters).

I've been meaning to code my own FM synth for a while now, and playing with the DS-10 has finally got me started. More on that to come.

Here's a video of the original MS-10:

Thursday, October 23, 2008

More Automatic City Generation



I'm still playing with my city generator. I now segment the city into districts of different types, resulting in a more diversity. I also have traffic moving around, path-following through the streets and obeying traffic lights. Fun stuff.

For car dynamics, I'm using the Farseer physics engine - a fully managed codebase that is really easy to integrate with XNA.

Monday, October 6, 2008

Fun with XNA and Automatic City Generation

Auto-generated cityscape

I've been playing around with Microsoft's XNA recently. It is essentially a flavor of managed DirectX targeted specifically at gaming (both on Windows and the Xbox360).

Part of my current project involves auto-generation of cityscapes. Things are starting to shape up a bit, so I figured I would post a few screenshots.

Auto-generated cityscape

I don't have an Xbox360 at the moment (I'm a happy PS3 owner), but I'm considering picking one up since you can build XNA projects for it and download and play them on the console.

Monday, June 16, 2008

Problems Connecting to Wirless Networks Using Windows Vista

I've been doing a fair bit of traveling recently, and had been having problems getting wireless networking to work in certain hotels. It would connect just fine, but would stay "local only" with no working DNS.

Some searching around came up with a solution here. It seems Vista is trying to be too smart, and has problems with some older routers. I implemented all three fixes listed. Although I'm guessing only one of them was probably necessary, I've been too lazy to investigate further.

Another welcome side-effect I've noticed is that it seems to negotiate a wireless connection faster than it did before.

Wednesday, May 14, 2008

Allocating Unmanaged Memory in Managed Code

I have been doing a bunch of interop stuff with unmanaged interfaces lately as part of an audio project I'm working on, so I have been dealing with unmanaged memory. While allocating unmanaged memory in C++ is fairly straightforward, I find programming in C++ to be pure misery. As a result, I dug around until I figured out how to do it in C#.

It all revolves around the Marshal class in System.Runtime.InteropServices. To allocate a chunk of unmanged memory, do:
 IntPtr myMemory = System.Runtime.InteropServices.Marshal.AllocHGlobal(numberOfBytes);

The 'IntPtr' is C#'s way of giving you a pointer reference. You can't do anything directly with it, but you can pass it around. The Marshall.Copy() function gives you a bunch of ways to copy chunks of various types of managed data to and from unmanaged data you have IntPtr references to.

What if you want to allocate a multi-dimensional unmanaged array? Things get a bit more complicated, but it can be done. Here is an example of allocating a 10 by 100 array of floats:
 int dimension1 = 10;
int dimension2 = 100;

IntPtr arrayPtr = Marshal.AllocHGlobal(dimension1 * Marshal.SizeOf(typeof(IntPtr)));
IntPtr[] dimension2Pointers = new IntPtr[dimension1];

for (int pos = 0; pos < dimension1; pos++)
{
dimension2Pointers[pos] = Marshal.AllocHGlobal(dimension2 * sizeof(float));
}

Marshal.Copy(dimension2Pointers, 0, arrayPtr, dimension1);

With a bit more work, you could probably avoid having to use the managed IntPtr array. In my case, though, I wanted managed pointers to both the full two-dimensional array as well as each of the individual one-dimensional arrays.

The use of Marshal.SizeOf() instead of sizeof() is because sizeof(IntPtr) is indeterminate unless you are in unsafe code.

Don't forget that this is all unmanaged memory, so you are responsible for freeing it using the corresponding Marshall.FreeHGlobal() call.