Omer van Kloeten's .NET Zen

Programming is life, the rest is mere details

News

Note: This blog has moved to omervk.wordpress.com.

Omer van Kloeten's Facebook profile

Omer has been professionally developing applications over the past 8 years, both at the IDF’s IT corps and later at the Sela Technology Center, but has had the programming bug ever since he can remember himself.
As a senior developer at NuConomy, a leading web analytics and advertising startup, he leads a wide range of technologies for its flagship products.

Get Firefox


powered by Dapper 

.NET Resources

Articles :: CodeDom

Articles :: nGineer

Culture

Projects

July 2004 - Posts

Old Skool Trippin', Part III

What the-? Part 3? But it was supposed to be only a two part-er.
Yeah, but there are a couple of things I wanted to talk about.

Wim Hollebrandse remarks that there's the HttpUtility.HtmlEncode method, and that I don't need the MakeSafe method.
My response to that was "Holy hidden methods, Batman!" I was looking for a method like this for a long time now. Thanks. :D Shame about the 15 minutes I had copy/pasting the list.
I read through the method using Reflector and found out that the method takes the easy way: Anything that's above the normal letter range is passed as a numeric representation. So I made a few adjustments, included the whole range of the Unicode spectrum of characters, and now we can get better results, even though it would take a lot longer (creating the table, checking the options at hand, etc.)

Here's the revised code:

#region The Code
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Collections;

namespace AnsiStuff
{
    class AsciiMaker2x2Take2
    {
        class CharDiff : IComparable
        {
            public readonly char Char;
            public readonly float Diff;

            public CharDiff(char c, float diff)
            {
                Char = c;
                Diff = diff;
            }

            public int CompareTo(object obj)
            {
                return Diff.CompareTo(((CharDiff)(obj)).Diff);
            }
        }

        [STAThread]
        static void Main(string[] args)
        {
            if (args.Length == 1)
            {
                string fileName = args[0];

                Console.Write("Creating font table...");
                Hashtable fontTable = CreateFontTable();
                Console.WriteLine("done.");

                using (Image originalImage = Image.FromFile(fileName))
                {
                    using (StreamWriter sw = File.CreateText(fileName + ".htm"))
                    {
                        sw.Write("<pre style=\"font-family: Courier New; font-size: 1px; letter-spacing: 0mm; line-height: 1px; font-weight: bold\">");

                        using (Bitmap newBmp = new Bitmap(originalImage))
                        {
                            int x, y;

                            for (y = 0; y < newBmp.Height; y += 2)
                            {
                                for (x = 0; x < newBmp.Width; x += 2)
                                {
                                    Color topLeft = newBmp.GetPixel(x, y), topRight = Color.White, bottomLeft = Color.White, bottomRight = Color.White;

                                    if (x + 1 < newBmp.Width)
                                    {
                                        topRight = newBmp.GetPixel(x + 1, y);

                                        if (y + 1 < newBmp.Height)
                                        {
                                            bottomRight = newBmp.GetPixel(x + 1, y + 1);
                                        }
                                    }

                                    if (y + 1 < newBmp.Height)
                                    {
                                        bottomLeft = newBmp.GetPixel(x, y + 1);
                                    }

                                    sw.Write(System.Web.HttpUtility.HtmlEncode(FindBestCharacter(fontTable, topLeft, topRight, bottomLeft, bottomRight).ToString()));
                                }

                                sw.WriteLine();
                            }
                        }

                        sw.Write("</pre>");
                    }
                }
            }
        }

        #region FindBestCharacter
        static char FindBestCharacter(Hashtable fontTable, Color topLeft, Color topRight, Color bottomLeft, Color bottomRight)
        {
            float tlb = topLeft.GetBrightness(), trb = topRight.GetBrightness(), blb = bottomLeft.GetBrightness(), brb = bottomRight.GetBrightness();

            ArrayList totalDiffs = new ArrayList(fontTable.Count);
            
            // Create the differences list.
            foreach (DictionaryEntry entry in fontTable)
            {
                totalDiffs.Add(new AsciiMaker2x2Take2.CharDiff(((char)(((int)(entry.Key)))), 
                    Math.Abs(tlb - Convert.ToSingle(((float[,])(entry.Value))[0, 0])) + 
                    Math.Abs(trb - Convert.ToSingle(((float[,])(entry.Value))[1, 0])) +
                    Math.Abs(blb - Convert.ToSingle(((float[,])(entry.Value))[0, 1])) +
                    Math.Abs(brb - Convert.ToSingle(((float[,])(entry.Value))[1, 1]))));
            }

            // Sort it according to the letter that's closest in lighting to the lighting we need.
            totalDiffs.Sort();

            return ((AsciiMaker2x2Take2.CharDiff)(totalDiffs[0])).Char;
        }
        #endregion

        #region CreateFontTable
        static Hashtable CreateFontTable()
        {
            Hashtable charBrightnessMap = new Hashtable();

            // All Courier New 10px letters occupy an 8x16 square at best.
            using (Bitmap newBmp = new Bitmap(8, 16))
            {
                using (Font font = new Font("Courier New", 10f))
                {
                    using (SolidBrush wb = new SolidBrush(Color.White))
                    {
                        using (Graphics g = Graphics.FromImage(newBmp))
                        {
                            // Going for the whole unicode range ;)
                            for (int i = 0; i <= Math.Pow(2.0, 16.0); i++)
                            {
                                char c = (char)i;

                                g.FillRectangle(wb, new Rectangle(new Point(0, 0), newBmp.Size));

                                using (SolidBrush b = new SolidBrush(Color.Black))
                                {
                                    // There's a 2 pixel horizontal padding for each letter.
                                    // We want to get rid of it.
                                    g.DrawString(c.ToString(), font, b, -2, 0);
                                }

                                using (Bitmap smallBmp = new Bitmap(4, 8))
                                {
                                    float[,] brightnessMatrix = new float[2, 2];
                                    for (int horHalf = 0; horHalf < 2; horHalf++)
                                    {
                                        for (int verHalf = 0; verHalf < 2; verHalf++)
                                        {
                                            for (int x = 0; x < 4; x++)
                                            {
                                                for (int y = 0; y < 8; y++)
                                                {
                                                    smallBmp.SetPixel(x, y, newBmp.GetPixel(x + horHalf * 4, y + verHalf * 4));
                                                }
                                            }

                                            using (Image img = smallBmp.GetThumbnailImage(1, 1, new Image.GetThumbnailImageAbort(new AsciiMaker2x2Take2().kek), IntPtr.Zero))
                                            {
                                                brightnessMatrix[horHalf, verHalf] = ((Bitmap)(img)).GetPixel(0, 0).GetBrightness();
                                            }
                                        }
                                    }

                                    charBrightnessMap.Add(i, brightnessMatrix);
                                }
                            }
                        }
                    }
                }
            }

            return charBrightnessMap;
        }
        #endregion

        public bool kek()
        {
            return false;
        }
    }
}
#endregion

Oh, and Peli, the only way for me to create the System.Drawing.Ascii is if I work for Microsoft. *cough cough* At best I could create DotNetZen.Drawing.Ascii.
I'll have to think about it.

In the meantime, does someone know of a fixed-width font for unicode (16 bit)? Courier New isn't really fixed-width for all characters.

p.s. All code was run through webifyOnline for coloring.

Old Skool Trippin', Part II

So what's this part 2 all about?
I have to give it to Raj for finding out, but not even knowing it.

Ami Dudu (no weblog... yet) came into my office the other day and I showed him some output from the app. He then 'challanged' me to step it up a notch and make each pixel into a different letter.
I can't really refuse a challange from Ami, so I sat down for a couple of hours and wrote an application that found out how much each letter's lightness is and created an HTML based on the image from that.

I decided to step it up a notch as well: I would take every letter and check for the brightness in each of its quadrants (dividing the letter into four parts - top left, top right, bottom left and bottom right) and work with 4 pixel groups that get represented by those letters.
True, the algorithm isn't perfect, but I didn't want to spend any more time into it than I already have.

Here's an example of what came out when I ran a lighter version of the image I used in the previous post.

#region 2x2 Code
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Collections;

namespace AnsiStuff
{
    class AsciiMaker2x2
    {
        class CharDiff : IComparable
        {
            public readonly char Char;
            public readonly float Diff;

            public CharDiff(char c, float diff)
            {
                Char = c;
                Diff = diff;
            }

            public int CompareTo(object obj)
            {
                return Diff.CompareTo(((CharDiff)(obj)).Diff);
            }
        }

        [STAThread]
        static void Main(string[] args)
        {
            if (args.Length == 1)
            {
                string fileName = args[0];

                Hashtable fontTable = CreateFontTable();

                using (Image originalImage = Image.FromFile(fileName))
                {
                    using (StreamWriter sw = File.CreateText(fileName + ".htm"))
                    {
                        sw.Write("<pre style=\"font-family: Courier New; font-size: 1px; letter-spacing: 0mm; line-height: 1px; font-weight: bold\">");

                        using (Bitmap newBmp = new Bitmap(originalImage))
                        {
                            int x, y;

                            for (y = 0; y < newBmp.Height; y += 2)
                            {
                                for (x = 0; x < newBmp.Width; x += 2)
                                {
                                    Color topLeft = newBmp.GetPixel(x, y), topRight = Color.White, bottomLeft = Color.White, bottomRight = Color.White;

                                    if (x + 1 < newBmp.Width)
                                    {
                                        topRight = newBmp.GetPixel(x + 1, y);

                                        if (y + 1 < newBmp.Height)
                                        {
                                            bottomRight = newBmp.GetPixel(x + 1, y + 1);
                                        }
                                    }

                                    if (y + 1 < newBmp.Height)
                                    {
                                        bottomLeft = newBmp.GetPixel(x, y + 1);
                                    }
                                    sw.Write(MakeSafe(FindBestCharacter(fontTable, topLeft, topRight, bottomLeft, bottomRight).ToString()));
                                }

                                sw.WriteLine();
                            }
                        }

                        sw.Write("</pre>");
                    }
                }
            }
        }

        #region FindBestCharacter
        static char FindBestCharacter(Hashtable fontTable, Color topLeft, Color topRight, Color bottomLeft, Color bottomRight)
        {
            float tlb = topLeft.GetBrightness(), trb = topRight.GetBrightness(), blb = bottomLeft.GetBrightness(), brb = bottomRight.GetBrightness();

            ArrayList totalDiffs = new ArrayList(fontTable.Count);
            
            // Create the differences list.
            foreach (DictionaryEntry entry in fontTable)
            {
                totalDiffs.Add(new AsciiMaker2x2.CharDiff(((char)(((int)(entry.Key)))), 
                    Math.Abs(tlb - Convert.ToSingle(((float[,])(entry.Value))[0, 0])) + 
                    Math.Abs(trb - Convert.ToSingle(((float[,])(entry.Value))[1, 0])) +
                    Math.Abs(blb - Convert.ToSingle(((float[,])(entry.Value))[0, 1])) +
                    Math.Abs(brb - Convert.ToSingle(((float[,])(entry.Value))[1, 1]))));
            }

            // Sort it according to the letter that's closest in lighting to the lighting we need.
            totalDiffs.Sort();

            // If we are about to return any type of space, let's just return the conventional space.
            if (Char.IsWhiteSpace(((AsciiMaker2x2.CharDiff)(totalDiffs[0])).Char))
            {
                return ' ';
            }

            return ((AsciiMaker2x2.CharDiff)(totalDiffs[0])).Char;
        }
        #endregion

        #region CreateFontTable
        static Hashtable CreateFontTable()
        {
            Hashtable charBrightnessMap = new Hashtable();

            // All Courier New 10px letters occupy an 8x16 square at best.
            using (Bitmap newBmp = new Bitmap(8, 16))
            {
                using (Font font = new Font("Courier New", 10f))
                {
                    using (SolidBrush wb = new SolidBrush(Color.White))
                    {
                        using (Graphics g = Graphics.FromImage(newBmp))
                        {
                            // I didn't want to exceed the 256 letters range.
                            for (int i = 0; i <= 256; i++)
                            {
                                char c = (char)i;

                                g.FillRectangle(wb, new Rectangle(new Point(0, 0), newBmp.Size));

                                using (SolidBrush b = new SolidBrush(Color.Black))
                                {
                                    // There's a 2 pixel horizontal padding for each letter.
                                    // We want to get rid of it.
                                    g.DrawString(c.ToString(), font, b, -2, 0);
                                }

                                using (Bitmap smallBmp = new Bitmap(4, 8))
                                {
                                    float[,] brightnessMatrix = new float[2, 2];

                                    for (int horHalf = 0; horHalf < 2; horHalf++)
                                    {
                                        for (int verHalf = 0; verHalf < 2; verHalf++)
                                        {
                                            for (int x = 0; x < 4; x++)
                                            {
                                                for (int y = 0; y < 8; y++)
                                                {
                                                    smallBmp.SetPixel(x, y, newBmp.GetPixel(x + horHalf * 4, y + verHalf * 4));
                                                }
                                            }

                                            using (Image img = smallBmp.GetThumbnailImage(1, 1, new Image.GetThumbnailImageAbort(new AsciiMaker2x2().kek), IntPtr.Zero))
                                            {
                                                brightnessMatrix[horHalf, verHalf] = ((Bitmap)(img)).GetPixel(0, 0).GetBrightness();
                                            }
                                        }
                                    }

                                    charBrightnessMap.Add(i, brightnessMatrix);
                                }
                            }
                        }
                    }
                }
            }

            return charBrightnessMap;
        }
        #endregion

        #region MakeSafe
        static string MakeSafe(string value)
        {
            return value
                .Replace("&", "&amp;")
                .Replace("<", "&lt;")
                .Replace(">", "&gt;")
                .Replace("¡", "&iexcl;")
                .Replace("¢", "&cent;")
                .Replace("£", "&pound;")
                .Replace("¤", "&curren;")
                .Replace("¥", "&yen;")
                .Replace("¦", "&brvbar;")
                .Replace("§", "&sect;")
                .Replace("¨", "&uml;")
                .Replace("©", "&copy;")
                .Replace("ª", "&ordf;")
                .Replace("«", "&laquo;")
                .Replace("¬", "&not;")
                .Replace("­", "&shy;")
                .Replace("®", "&reg;")
                .Replace("¯", "&macr;")
                .Replace("°", "&deg;")
                .Replace("±", "&plusmn;")
                .Replace("²", "&sup2;")
                .Replace("³", "&sup3;")
                .Replace("´", "&acute;")
                .Replace("µ", "&micro;")
                .Replace("¶", "&para;")
                .Replace("·", "&middot;")
                .Replace("¸", "&cedil;")
                .Replace("¹", "&sup1;")
                .Replace("º", "&ordm;")
                .Replace("»", "&raquo;")
                .Replace("¼", "&frac14;")
                .Replace("½", "&frac12;")
                .Replace("¾", "&frac34;")
                .Replace("¿", "&iquest;")
                .Replace("À", "&Agrave;")
                .Replace("Á", "&Aacute;")
                .Replace("Â", "&Acirc;")
                .Replace("Ã", "&Atilde;")
                .Replace("Ä", "&Auml;")
                .Replace("Å", "&Aring;")
                .Replace("Æ", "&AElig;")
                .Replace("Ç", "&Ccedil;")
                .Replace("È", "&Egrave;")
                .Replace("É", "&Eacute;")
                .Replace("Ê", "&Ecirc;")
                .Replace("Ë", "&Euml;")
                .Replace("Ì", "&Igrave;")
                .Replace("Í", "&Iacute;")
                .Replace("Î", "&Icirc;")
                .Replace("Ï", "&Iuml;")
                .Replace("Ð", "&ETH;")
                .Replace("Ñ", "&Ntilde;")
                .Replace("Ò", "&Ograve;")
                .Replace("Ó", "&Oacute;")
                .Replace("Ô", "&Ocirc;")
                .Replace("Õ", "&Otilde;")
                .Replace("Ö", "&Ouml;")
                .Replace("×", "&times;")
                .Replace("Ø", "&Oslash;")
                .Replace("Ù", "&Ugrave;")
                .Replace("Ú", "&Uacute;")
                .Replace("Û", "&Ucirc;")
                .Replace("Ü", "&Uuml;")
                .Replace("Ý", "&Yacute;")
                .Replace("Þ", "&THORN;")
                .Replace("ß", "&szlig;")
                .Replace("à", "&agrave;")
                .Replace("á", "&aacute;")
                .Replace("â", "&acirc;")
                .Replace("ã", "&atilde;")
                .Replace("ä", "&auml;")
                .Replace("å", "&aring;")
                .Replace("æ", "&aelig;")
                .Replace("ç", "&ccedil;")
                .Replace("è", "&egrave;")
                .Replace("é", "&eacute;")
                .Replace("ê", "&ecirc;")
                .Replace("ë", "&euml;")
                .Replace("ì", "&igrave;")
                .Replace("í", "&iacute;")
                .Replace("î", "&icirc;")
                .Replace("ï", "&iuml;")
                .Replace("ð", "&eth;")
                .Replace("ñ", "&ntilde;")
                .Replace("ò", "&ograve;")
                .Replace("ó", "&oacute;")
                .Replace("ô", "&ocirc;")
                .Replace("õ", "&otilde;")
                .Replace("ö", "&ouml;")
                .Replace("÷", "&divide;")
                .Replace("ø", "&oslash;")
                .Replace("ù", "&ugrave;")
                .Replace("ú", "&uacute;")
                .Replace("û", "&ucirc;")
                .Replace("ü", "&uuml;")
                .Replace("ý", "&yacute;")
                .Replace("þ", "&thorn;")
                .Replace("ÿ", "&yuml;");
        }
        #endregion

        public bool kek()
        {
            return false;
        }
    }
}
#endregion

Old Skool Trippin', Part I

I never was a good ANSI artist, even when I tried, back in the mid-90's. All I got when I loaded ACiDDraw was pretty fair results, but nothing great. However, I still have a warm spot deep inside for old skool ansi and ascii art. (Being a member of iCE kind of helps, too ;)

One of the signatures I made for my emails was a hand made colored ascii version of The Project's logo (since people started to complain about my sending an image with every new email). It made me long for the good old days, so I went and made a little program that takes an image and creates a colored ascii representation of it using the '$' (dollar) sign in HTML. Came out rather nice, but I couldn't make out the css line-height to font-size ratios. Oh, well.

So here it is, but first, an example so you understand what I'm talking about:
I made a couple of examples for the first image I found:

So here's a converted image with a factor of 1, of 3 and of 5.

Here's the code (click on the region to collapse):

#region The Code
using System;
using System.Text;
using System.IO;
using System.Drawing;

namespace AnsiStuff
{
    class ColoredAsciiMaker
    {
        [STAThread]
        static void Main(string[] args)
        {
            if (args.Length == 2)
            {
                // The factor is the size of the original image in comparison to the size of the output
                // image. 1 is for best quality and largest file size. more for lesser quality.
                int factor = Convert.ToInt32(args[1]);
                Image img = Image.FromFile(args[0]);

                using (StreamWriter sw = File.CreateText(args[0] + ".htm"))
                {
                    if (img is Bitmap)
                    {
                        // Create a thumbnail accoding to the factor.
                        using (Bitmap bmp = ((Bitmap)(img)).GetThumbnailImage(img.Width / factor,
                                   img.Height / factor,
                                   new System.Drawing.Image.GetThumbnailImageAbort(new ColoredAsciiMaker().kek),
                                   IntPtr.Zero) as Bitmap)
                        {
                            StringBuilder sb = new StringBuilder();
                            Color oldColor = Color.Empty;

                            for (int y = 0; y < bmp.Height; y++)
                            {
                                for (int x = 0; x < bmp.Width; x++)
                                {
                                    Color color = bmp.GetPixel(x, y);

                                    // The old color is saved so we don't repeat a font tag twice
                                    // for consecutive same color pixels.
                                    if (oldColor != color)
                                    {
                                        // If the previous color was white, there's no reason to
                                        // close a font tag.
                                        if (!((oldColor.R == Color.White.R) && (oldColor.G == Color.White.G) && (oldColor.B == Color.White.B)))
                                        {
                                            sb.Append("</font>");
                                        }

                                        // If the current color is not white, create a font tag.
                                        if (!(color.R == Color.White.R && color.G == Color.White.G && color.B == Color.White.B))
                                        {
                                            sb.Append("<font color=\"#" +
                                                (color.R < 16 ? "0" + color.R.ToString("X") : color.R.ToString("X")) + 
                                                (color.G < 16 ? "0" + color.G.ToString("X") : color.G.ToString("X")) + 
                                                (color.B < 16 ? "0" + color.B.ToString("X") : color.B.ToString("X")) + 
                                                "\">");
                                        }

                                        oldColor = color;
                                    }

                                    if (!(color.R == Color.White.R && color.G == Color.White.G && color.B == Color.White.B))
                                    {
                                        sb.Append('$');
                                    }
                                    else
                                    {
                                        // When the color is white, to save space,
                                        // we just place a whitespace.
                                        sb.Append(' ');
                                    }
                                }

                                sb.Append(Environment.NewLine);
                            }

                            string output = sb.ToString();

                            // Clear the last font tag.
                            if (output.StartsWith("</font>")) output = output.Substring("</font>".Length);

                            sw.Write("<pre style=\"font-size: " + factor + "px; letter-spacing: 0mm; line-height: " + (factor + 1) / 2 + "px; font-weight: bold\">" + output + "</pre>");
                        }
                    }
                }
            }
        }

        public bool kek()
        {
            return true;
        }
    }
}
#endregion

One final note: The GetThumbnailImage method has a couple of rather foolish parameters. One's said to be for future use and the other should always be IntPtr.Zero. What's this supposed to mean?

To be continued...

Changelog for .NET Framework 1.0 SP3 and .NET Framework 1.1 SP1

.NET Framework 1.0 SP3 [Changelog]
.NET Framework 1.1 SP1 [Changelog]

Too bad I keep seeing "No Public KB article available at this time." all over the place. If you don't have a KB article, at least give me a summary...

This Doesn't Make Sense

I believe I found an inconsistency problem with the definition of the syntactic grammar in C#.
This problem remains with the interim version of the c# 2.0 spec. and worries me a great deal.

<!-- BEGIN TECHNICAL MUMBO JUMBO -->

Chapter 5 of the specification starts with the following phrase:

"... The lexical grammar defines how characters can be combined to form tokens (§9.4), the minimal lexical elements of the language. The syntactic grammar defines how tokens can be combined to make valid C# programs."

The lexical element token is defined:

token::
    identifier
    keyword
    integer-literal
    real-literal
    character-literal
    string-literal
    operator-or-punctuator
This means that a token may be one of the following: an identifier, a keyword, an integer literal, a real literal, a character literal, a string literal, an operator or a punctuator. Please note the fact that the lexical element is literal is not one of those options.

The grammatical element primary-no-array-creation-expression is defined:

primary-no-array-creation-expression:
    literal
    simple-name
    parenthesized-expression
    member-access
    invocation-expression
    element-access
    this-access
    base-access
    post-increment-expression
    post-decrement-expression
    object-creation-expression
    delegate-creation-expression
    typeof-expression
    checked-expression
    unchecked-expression
    pointer-member-access
    pointer-element-access
    sizeof-expression
This means that primary-no-array-creation-expression may be a literal, in addition to the other options specified.

Now, giving that all grammatical elements must be made out of lexical tokens, it surprises me that the primary-no-array-creation-expression might be a literal, which is a lexical element which is not a token nor any descendant of a token!

<!-- END TECHNICAL MUMBO JUMBO -->

What all this means is that the specification is in err.

However, there are two ways to correct this:

  1. Replace the list of literals token may be with just the literal lexical element.
    This may have some implications that I have not yet foreseen, but it seems like the most logical of choises.
  2. Replace literal with the list of valid literals (those that can be tokens).
    The downside of that is that you could not, taking it literally (no pun intended), do this:
    bool b1 = true;
    bool b2 = false;
    object o = null;
    since null, true and false will not be valid values as assignees.

Oh, the burden of decision. Glad it's not mine (ok, not really; I actually want to be a part of the decision making).
I submitted the problem to ECMA TC39-TG2's Convenor, Microsoft's Jon Jagger, who has confirmed that this is indeed a problem.

My only remaining question is: How has no one found out about this up until now? There have been too many compilers written to ignore this mistake.
My guess is that everyone just cut corners and were done with it. No idea why, though.

CodeDOM Best Practice: Use Reflection To Get Names
The use of constants instead of strings in code is well known. Code such as this:
public class MyClass
{
	public void MyMethod()
	{
		string myString = "Omer";

		// Do Some Magic
	}
}
Should be transformed to this:
public class MyClass
{
	private const string Omer = "Omer";

	public void MyMethod()
	{
		string myString = Omer;

		// Do Some Magic
	}
}
But what do you do when you need to generate code that calls the GetType method on your local variable?

Well, normally you could do this:

public class MyClass
{
	private const string MyVariable = "myVariable";
	private const string GetType = "GetType";

	public void MyMethod()
	{
		CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(MyVariable), GetType);

		// Do Some Magic
	}
}
This would work well and some would agree that this is the best form.
However, when generating code using CodeDOM, there's two more 'times' in which your code may break. There's the standard compile-time and run-time, but they are the compile-time and run-time of your generator's code. There are also the compile-time and the run-time of your generated code, both of which you can not control. Changes in member/type names may break your code during the third 'time', the generated code's compile time.

What do we do, then? We use Reflection, which allows us to write code like this:

public class MyClass
{
	private const string MyVariable = "myVariable";
	private static readonly string GetType = typeof(System.Object).GetMember("GetType")[0].Name;

	public void MyMethod()
	{
		CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(MyVariable), GetType);

		// Do Some Magic
	}
}
Sure, it takes more time for your generator to load, but it transfers the problem from the generated code to the generator's run-time. This way, your code would not even run which is way better, in my opinion, than run and produce errorneous code.

Introducing nGineer's Lexical Analyzer

nGineer is a language analysis and code generation kit for .NET languages and specifically C# which is currently being developed.
Components of nGineer are released separately and more components will be released soon.

The Lexical Analyzer component of the nGineer toolkit is used to analyze the lexical grammar of C# code files.
The component offers great extensibility and one of its derivatives is webify which is a syntax coloring tool that can be downloaded along with the binaries and source of the component, or used in its online version.

I have also created a new articles category for nGineer and have posted two articles about the lexical analyzer:

  1. Designing The Lexical Analyzer.
  2. Implementing a Lexical Walker.

I am aware that the .NET Framework contains a stub for a C# parser, but it is only a stub and I have been reassured by Jay that there are no plans to implement it at this point.

The next component to be released is the grammatical parser, but I have no date for its release, since there are several issues to be resolved first.
I'll keep you posted.

Please feel free to comment or contact me about this with any questions.

References In Visual Studio.NET

Hey, everyone! It's the "Omer Asks For A Feature" time again! ;)

This time, it's regarding a project's references list (in addition to my previous copy/move reqeust):

  1. "Remove Unused References" - You could right click the project, click on this and get all the references your project isn't really using deleted from it. This way, if you're not using any of the pre-supplied references (System, System.Data, System.Xml, etc.), they would get removed so your assembly will not depand on them.
    By the way, the catch is not in finding out which assemblies you use, but on remembering to check which assemblies the references refer to. :)
  2. Conditional References - I can compile my project with conditional symbols so that they affect what gets compiled in my code. I want to be able to specify the symbols required for a reference to actually get into the manifest.
    Why do I need this? Imagine me having my test cases in my main assembly. I can use conditionals to remove the unit testing code so that it's not in my released assembly (#if DEBUG), but I can't remove the reference to the testing framework's assemblies. Yes, JIT will not try to load the referenced assembly, because there's no code referring to it, but then again, it's garbage in my assembly's manifest.

Posted: Jul 16 2004, 05:12 PM by Omer van Kloeten | with 2 comment(s)
Filed under:
Request From The Windows Guys

I want to make a request to the guys working on Windows: Manually Ordered Taskbar Buttons.

What does that mean? It means that when I have a taskbar button for Window A, Window B and Window C, I want to be able to drag Window B before Window A, like I do with the QuickStart icons.

It's a small thing (I think), but it could allow me to have my taskbar sorted the way I want it to be sorted and not the way windows get opened. For instance, I like having my Outlook window the leftmost window all the time, but when it gets closed, I have to re-open it and it's the rightmost window! Argh! When I'm really pissed, I just close all of the windows that's buttons are to the left of the Outlook one.


Ah, all's well...

Argh! I closed Outlook! No, I didn't really mean it! Crap!

If you could incorporate this into a Windows that's before Longhorn, I'd appreciate it a lot.

CodeDOM Articles Update

I've taken a bit of time to update the CodeDOM articles set:

  1. Part V: Other General Items Of Interest is now up.
  2. The code for Part IV: The Structure of the Code Document Object Model has been updated, removing several inconsistencies.
Part VI: Statements and Expressions will be up soon.

Hope you find this informative.

More Posts Next page »