September 2006 - Posts

Short no-nonsense post: here it is. Associated tools can be found in the Related Resources section of the page. For the Belgians reading this blog: cu tomorrow in Brussels for the .NET Framework 3.0 event (house fully booked).

Cheers!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Introduction

Normally I don't post quizzes over here, but time for a little exception (no prizes to win). A few days ago I was talking to someone who was a little confused on the field of polymorfic constructions in C# (and more general, polymorphism as such). There are three keywords in C# that play prominent roles in this context: virtual, new and override.

Quiz

What will be the output of the following piece of code?

using System;

class
Ns
{
   public static void
Main()
   {
      B b =
new
B();
      b.Bar();
      b.Foo();

      A a = b;
      a.Bar();
      a.Foo();
   }

   class
A
   {
      public virtual void
Bar()
      {
         Console.WriteLine("A.Bar"
);
      }

      public virtual void
Foo()
      {
         Console.WriteLine("A.Foo"
);
      }
   }

   class
B : A
   {
      public override void
Bar()
      {
         Console.WriteLine("B.Bar"
);
      }

      public new void
Foo()
      {
         Console.WriteLine("B.Foo"
);
      }
   }
}

It's remarkable how much people don't know about the new keyword in this context. And maybe that's best, because hiding is in my opinion a somewhat ugly feature that conflicts with object-oriented design.

Extra questions:

  1. Drop the override keyword and compile again; what do you see now?
  2. Where can I apply the sealed keyword on class B and it's members?

PS: The guy I was talking to has spent a few years in the world of Java and is relatively new to .NET. Nothing bad about Java of course, but the differences of default behavior (C#'s explicitness of a "virtual" method versus Java's implicitness for virtual methods for instance) in various OO languages can be quite confusing indeed.

I agree, for most people this will be peanuts, but nevertheless I thought it could be valuable to some of you, maybe still climbing the learning curve of .NET.

For those of you who can't get enough of this, check out this post by Gregory Young. About a month ago, this issue was escalated on the MVP C# newsgroup and is currently under investigation at Microsoft. Just to show how OO can lead to real brainteasers (especially when there is some nasty bug that makes you doubt about the completeness of your OO skills :-)).

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Introduction

I think the Encrypting File System is familiar to a lot of you. If not, check out this page. In a few simple words, EFS is an NTFS feature that allows you to encrypt files using a symmetric key. That symmetric key (the FEK) is secured by DPAPI on a per-user basis. Therefore, an EFS encrypted file is only accessible by the user that encrypted it. In this post, I'll show you how to perform EFS encryption on a set of files using Windows PowerShell. No rocket sciene if you know you PS best friend get-member, but still worth to blog about.

Discover the PS world - get-member all the time

Without words...

PS C:\temp\PS demo> get-childitem | get-member -m Methods | format-table Definition

Definition
----------
System.IO.StreamWriter AppendText()
System.IO.FileInfo CopyTo(String destFileName), System.IO.FileInfo CopyTo(St...
System.IO.FileStream Create()
System.Runtime.Remoting.ObjRef CreateObjRef(Type requestedType)
System.IO.StreamWriter CreateText()
System.Void Decrypt()
System.Void Delete()
System.Void Encrypt()
System.Boolean Equals(Object obj)
System.IO.FileAttributes get_Attributes()
System.DateTime get_CreationTime()
System.DateTime get_CreationTimeUtc()
System.IO.DirectoryInfo get_Directory()
System.String get_DirectoryName()
System.Boolean get_Exists()
System.String get_Extension()
System.String get_FullName()
System.Boolean get_IsReadOnly()
System.DateTime get_LastAccessTime()
System.DateTime get_LastAccessTimeUtc()
System.DateTime get_LastWriteTime()
System.DateTime get_LastWriteTimeUtc()
System.Int64 get_Length()
System.String get_Name()
System.Security.AccessControl.FileSecurity GetAccessControl(), System.Securi...
System.Int32 GetHashCode()
System.Object GetLifetimeService()
System.Void GetObjectData(SerializationInfo info, StreamingContext context)
System.Type GetType()
System.Object InitializeLifetimeService()
System.Void MoveTo(String destFileName)
System.IO.FileStream Open(FileMode mode), System.IO.FileStream Open(FileMode...
System.IO.FileStream OpenRead()
System.IO.StreamReader OpenText()
System.IO.FileStream OpenWrite()
System.Void Refresh()
System.IO.FileInfo Replace(String destinationFileName, String destinationBac...
System.Void set_Attributes(FileAttributes value)
System.Void set_CreationTime(DateTime value)
System.Void set_CreationTimeUtc(DateTime value)
System.Void set_IsReadOnly(Boolean value)
System.Void set_LastAccessTime(DateTime value)
System.Void set_LastAccessTimeUtc(DateTime value)
System.Void set_LastWriteTime(DateTime value)
System.Void set_LastWriteTimeUtc(DateTime value)
System.Void SetAccessControl(FileSecurity fileSecurity)
System.String ToString()

Putting EFS to work

So, you can do the following:

PS> dir *.txt | foreach { Write-Host $_.Name; $_.Encrypt() }

There's one little caveat: the EFS encryption takes place in the context of the user running the PowerShell instance of course. So, make sure you're running in the right security context, e.g. by runas-ing the Windows PowerShell.

Btw, on Windows Vista you'll see a notification popping up from the notification area in the taskbar which recommends you to backup your encryption keys:

Clicking it displays the following dialog:

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Introduction

You might have hear about the concept of a tail call already. It's already quite some time ago I found myself browsing through the entire ECMA 335 spec of the CLI (to be precise, from mid July this summer for some Rotor 2.0 experiments). A few days ago however, I was playing around with F# (Wikipedia) again, triggered by two Channel 9 videos of Mike Hall interviewing Don Syme (blog) (Don has been involved in .NET generics in the FX 2.0 timeframe). Basically, F# is a modern variant of ML languages running on the CLR, created by the Microsoft Research folks. Watch the Channel 9 videos here and there. Back to the original subject: tail calls.

Tail calls

Tail calls are - how can you guess? - calls at the tail of a method. Let's start by taking a look at the concept of a call. Normally a call instruction (call, calli, callvirt) grows the stack. That is, the current method's state is kept and a new activation frame is pushed on the call stack which is used by the called method. Just try this one:

class So
{
   public static void Main()
   {
      Main();
   }
}

No wonder it will crash with a "Process is terminated due to StackOverflowException" message (on my machine after approx. 80,000 calls). (Quiz: Is there any way to catch this exception? Why? Why not? Tip: The answer is very close.)

This code emits a simple call instruction:

call void So::Main()

A tail call on the other hand releases the current stack frame before making the call, therefore keeping the stack low. This is a common 'requirement' in dynamic languages (which C# isn't but F# has chacteristics of this). This means that after a tail call nothing else than the ret instruction should appear in the current method ("Call Terminates Current Method", ECMA 335 - Partition III - 2.1).

Ildasm the executable compiled out of the previous piece of code and patch it as follows (red = drop; green = add):

.class private auto ansi beforefieldinit So
       extends [mscorlib]System.Object
{
  .method public hidebysig static void  Main() cil managed
  {
    .entrypoint
    // Code size       8 (0x8)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  tail. call       void So::Main()
    IL_0006:  nop
    IL_0007:  ret
  } // end of method So::Main

Then ilasm again. Now the app will run till the end of times.

I agree that this sample isn't particularly useful (not to say it's worth just nothing :-)) but I thought it might be useful to some of you to know a) what a tail call is; b) that it is supported by the CLI/CLR; c) how cool dynamic languages should be...

Tip: for a more useful (or better: less useless) example, take a look at Partition V of ECMA 335 which contains an example of tail calls. It's basically the equivalent of this piece of C# (but which doesn't run out of stack space):

class EvenOdd
{
   static bool IsEven(int n)
   {
      if (n == 0)
         return true;
      else
         return IsOdd(n-1);
   }

   static bool IsOdd(int n)
   {
      if (n == 0)
         return false;
      else
         return IsEven(n-1);
   }

   static void Main()
   {
      Console.WriteLine(IsEven(1000000));
   }

}

It might be a good exercise for anyone who has time left to try to patch the IL of this program to use tail calls (== not straightforward).

C ya in the F# universe soon?

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Okay, here we go again. Another Vista build available through Microsoft Connect. Build number is 5728, build date is September 17 and additional information on the downloads is shown below:

0x94243215      vista_5728.16387.060917-1430_x86fre_client-LRMCFRE_EN_DVD.iso
0x01643ECC     vista_5728.16387.060917-1430_x64fre_client-LRMCxFRE_EN_DVD.iso

The same for Windows Server codename "Longhorn":

0xB052D4D8     wsl_5728.16387.060917-1430_x86fre_server-LRMSFRE_EN_DVD.iso
0x5454681C      wsl_5728.16387.060917-1430_x64fre_server-LRMSxFRE_EN_DVD.iso

Symbols are available too, as well as the WDK.

0x7FF814F3      vista_5728.16387.060917-1430-LRMWDK_EN.iso
0xE9CC7919     vista_5728.16387.060917-1430-LRMAIK_EN.iso

Since I'm running RC1 as my main operating system and moving on to the next build is quite disruptive for productivity, this release will end up in Virtual Server 2005 R2 SP1. But don't let this statement hold you down of installing this build of course.

Enjoy!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

I blogged about "Windows Vista and the Windows key" before. A few days ago I read the Windows Vista Secret #5: Running Quick Launch Items by Tim Sneath. Pretty cool stuff indeed. Tim mentions the following:

Do you have a few applications that you're always firing up? Would you like a system-wide keyboard shortcut to run them? Here's what you do. Simply add shortcuts to the Quick Launch toolbar, as shown in the screenshot below.

Now you can simply use Win+1, Win+2, Win+3 and so on to launch each application. For example, on my machine, Win+3 launches Notepad and Win+6 launches the VS command prompt. This works no matter what application has the focus.

More coolness coming your way soon, courtesy of Windows Vista!

However, there's a small caveat. What is 1, 2, 3? Especially on AZERTY.

So, let's start by telling you what it isn't on my Dell laptop. It is not Win-Fn-J,K,L (the J key contains the 1 of the numerical keyboard, K contains 2 and L contains 3). It isn't Win-Shift-&,é," either (the & is the regular character of the key containing the 1, é on 2 and " on 3).

So it is ... drums ... Win-&, Win-é, Win-", etc on my keyboard, as shown below:

Sure this is a great feature; no doubt on that. But when documented poorly for non-QWERTY users, people might get frustrated because calling it Win-1,2,3 is far too ameliorated due to keyboard layout. Or, there's some other solution too, fixing it in order to make it work with the numeric keyboard too (and/or the keys containing the numbers regardless of the SHIFT mask)!

Anyway, happy quicklaunching!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Interested to learn about LINQ and C# 3.0 features directly from the C# godfather himself? Check out this Channel 9 video!

Anders Hejlsberg - Lang.Net 2006 Compiler Symposium

Tip: Due to high resolution of the video, I recommend to download the file and play it offline in WMP.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Introduction

Well, kind of a short follow-up to my previous post on EAN-13 codes. As I promised, I'd blog about the conversion from an ISBN code to the corresponding EAN-13 code. No rocket science after all, but because it doesn't have so much to do with EAN-13 by itself, I decided to put this stuff in a separate post.

The code

ISBN codes are codes of 10 digits (will grow to 13 digits in the near future). The first digit (or two) represent the country code (0 = US, 90 = Belgium), the next four (or three) digits are the publisher (321 = Addison-Wesley), the next four or five digits are a unique identifier for the publisher's book (15493 = The Common Language Infrastructure Annotated Standard) and the last one digit is the checksum digit.

A normal form for ISBN numbers

Quite often you see things like 0-321-15493-2 or 0 321 15493 2 in order to distinguish between the different "fields" composing the ISBN code. A first handy method sanitizes such a representation and turns it into a normal form:

static string NormalizeIsbn(string isbn)
{
   return isbn.Replace("-", "").Replace(" ", ""
);
}

Check the checksum

Next, what about the checksum? The 10th digit of the ISBN code is the checksum and is calculated as follows:

  1. Multiply the value of digits 1-9 (non zero-based indices) by their position (i.e. 1 to 9).
  2. Sum the results from step 1.
  3. Calculate the remainder after division by 11; if the result is 10, the checksum digit is 'X'.

An example:

 0   3   2   1   1   5   4   9   3   ?
 1   2   3   4   5   6   7   8   9
--  --  --  --  --  --  --  --  --

 0 + 6 + 6 + 4 + 5 +30 +28 +72 +27 = 178 (% 11 = 2)

The code to check the checksum can be found below:

static bool CheckIsbn(string isbn)
{
   if (isbn == null
)
      return false
;

   isbn = NormalizeIsbn(isbn);
   if
(isbn.Length != 10)
      return false
;

   int
res;
   for (int
i = 0; i < 9; i++)
      if (!int.TryParse(isbn[i].ToString(), out
res))
         return false
;

   int
sum = 0;
   for (int
i = 0; i < 9; i++)
      sum += (i + 1) *
int
.Parse(isbn[i].ToString());

   int
r = sum % 11;
   if
(r == 10)
      return isbn[9] == 'X'
;
   else
      return isbn[9] == (char)('0'
+ r);
}

Note: An example of checksum 'X' is the ISBN code 0-596-00351-X of "Shared Source CLI Essentials" (David Stutz, Ted Neward, Geoff Shilling - O'Reilly).

ISBN-to-EAN13

The last step: conversion from ISBN to EAN-13 for barcode representation. It's fairly easy:

  1. Drop the ISBN checksum digit.
  2. Append 978 in front of the remainder of the code (978 = ISBN Bookland Code)
  3. Calculate the EAN-13 checksum.

Notice this drops the possible 'X' in the code (which isn't representable in EAN-13). Here's the conversion code:

static string IsbnToEan13(string isbn)
{
   isbn = NormalizeIsbn(isbn);
   string code = "978"
+ isbn.Substring(0, 9);
   code += (
char)('0' + Ean13
.CalculateChecksum(code));
   return
code;
}

See my previous post for the definition of Ean13::CalculcateChecksum. Of course exposing the ISBN (or EAN-13) code generator through the web using .ashx handlers in ASP.NET is also possible. Follow the instructions on An ASP.NET .ashx HTTP handler for Code 39 barcode generation for more information (just replace all Code 39 stuff by Ean13 stuff).

Happy (bar)coding!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Just to let you know: Tom Mertens has started blogging on blogs.msdn.com too; you can find his writings over here. Don't know which posts will end up at which location, but one subscription more or less in your RSS reader shouldn't be a showstopper, is it?Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Introduction

A couple of days ago I posted about Code 39 barcode generation in C# (and here). Today another more famous barcode is the subject of my blogpost: EAN-13. EAN stands for European Article Number and is a way to number products. It's an extension of UPC (Universal Product Code). More information over here.

One particular interesting application of EAN-13 is the fact that ISBN (International Standard Book Number) codes are also represented as EAN-13 codes. I'll blog about this too.

Mission goal

As we did with Code 39, we want to be able to do something like this:

Ean13 barcode = new Ean13("9780201734843", null);
barcode.Paint().Save("c:\\temp\\test.png", ImageFormat.Png);

The second parameter to the Ean13 constructor is an optional title to display on top of the barcode (will be handy for ISBN numbers).

The skeleton

Let's start by defining the code skeleton of our Ean13 class:

class Ean13
{
   private Ean13Settings
settings;
   private string
code;
   private string
title;

  
public Ean13(string code, string title)
      :
this(code, title, new Ean13Settings
())
   {
   }

   public Ean13(string code, string title, Ean13Settings
settings)
   {
     
this
.settings = settings;
      this
.code = code;
      this
.title = title;

      if
(!CheckCode(code))
         throw new ArgumentException("Invalid EAN-13 code specified."
);
   }

   public Image Paint()
   {
      ...
   }
}

Again we have an additional class to specify settings, this time called Ean13Settings. It's nothing more than a container for a series of properties (<type> <property name> <default value>):

  • int BarCodeHeight = 120
  • int LeftMargin = 10
  • int RightMargin = 10
  • int TopMargin = 10
  • int BottomMargin = 10
  • int BarWidth = 2
  • Font Font = new Font(FontFamily.GenericSansSerif, 10)

A checksum checker

A helper method is required to check the code's checksum. This one is called from the constructor to ensure the code is valid. Here it is (CheckCode):

private bool CheckCode(string code)
{
   if (code == null
|| code.Length != 13)
      return false
;

   int
res;
   foreach (char c in
code)
      if (!int.TryParse(c.ToString(), out
res))
         return false
;

   char
check = (char)('0' + CalculateChecksum(code.Substring(0, 12)));
   return
code[12] == check;
}

Which is on its turn again relying on another helper method CalculateChecksum:

public static int CalculateChecksum(string code)
{
   if (code == null
|| code.Length != 12)
      throw new ArgumentException("Code length should be 12, i.e. excluding the checksum digit"
);

   int
sum = 0;
   for (int
i = 0; i < 12; i++)
   {
      int
v;
      if (!int.TryParse(code[i].ToString(), out
v))
         throw new ArgumentException("Invalid character encountered in specified code."
);
      sum += (i % 2 == 0 ? v : v * 3);
   }
   int
check = 10 - (sum % 10);
   return
check % 10;
}

The checksum is calculated as follows:

  1. Take the sum of all digits on even positions.
  2. Take the sum of all digits on odd positions and multiply by 3.
  3. Sum the results of step 1 and 2.
  4. Find the number (0-9) you have to add to the result from step 3 in order to make the remainder of the division by 10 equal to 0 (i.e. divisible by 10 :-)).

An example:

978020173484?
9+8+2+1+3+8     = 31
(7+0+0+7+4+4)*3 = 66
--------------------
                  97 + 3 = 100 (% 10 = 0)
9780201734843

CalculcateChecksum is defined as a public static method so that we can use it in order to calculate the EAN-13 code for a given ISBN later on (see later post).

EAN-13 coding explained

EAN-13 coding is not as easy as Code 39 coding. Therefore it's useful to have some explanation first. As you should have noticed by now, an EAN-13 code consists of 13 digits. These 13 digits are divided in 3 "groups":

9 780201 734843

The first digit is part of the number system, a code to represent the country of origin. In the code shown above, this number system is 978 or ISBN Book Land, the artifical country code for books. An example of a real country code is 54 for Belgium and Luxemburg (e.g. 5413356623321 on a CD of "De Kreuners"). The next group of 6 digits is the "left group" (remainder number system code + manufacturer code), the last group of 6 bytes is the "right group" (product code + checksum digit).

Encoding works as follows:

  1. The first digit (9 in the example above) determines the encoding scheme for the left group or the manufacturer code. This encoding is based on odd or even parity as explained below.
  2. The first group is encoded based on the encoding scheme found in step 1.
  3. The right group is encoded which is independent from the encoding scheme mentioned above.

Let's start by taking a look at the EAN parity encoding schemes:

oooooo
1
ooeoee
2
ooeeoe
3
ooeeeo
4
oeooee
5
oeeooe
6
oeeeoo
7
oeoeoe
8
oeoeeo
9
oeeoeo

In here, the first column represents the first digit of the code (e.g. 9). The second column indicated the encoding parity scheme for the next 6 digits (e.g. 780201), where e stands for even and o stands for odd.

The 6 next digits ("left group") are then encoded corresponding to the parity scheme:

# Odd     Even
0
0001101 0100111
1
0011001 0110011
2
0010011 0011011
3
0111101 0100001
4
0100011 0011101
5
0110001 0111001
6
0101111 0000101
7
0111011 0010001
8
0110111 0001001
9 0001011 0010111

For example, our code starts with 9, therefore the partity scheme for the first group of six is oeeoeo. The next digits are 780201:

  • 7 - o - 0111011
  • 8 - e - 0001001
  • 0 - e - 0100111
  • 2 - o - 0010011
  • 0 - e - 0100111
  • 1 - o - 0011001

In these codes, 0 stands for a white bar and 1 for a black bar. All bars have the same width (differs from Code 39 where bars can be narrow or wide) and there are no intercharacter gaps. Therefore, the encoding of 9 780201 will be:

011101100010010100111001001101001110011001

Notice all digit encodings for the left group start by 0 (white bar) and end by 1 (black bar). All partity codes start by o, meaning that the second number in the code is always encoded with Odd parity.

For the right group (product code and checksum digit) things are a little easier as the encoding doesn't follow any "parity scheme":

# Encoding
0
1110010
1
1100110
2
1101100
3
1000010
4
1011100
5
1001110
6
1010000
7
1000100
8
1001000
9 1110100

Now every encoding starts by 1 (black bar) and ends by 0 (white bar). In our example, the right part is "734843", resulting in:

100010010000101011100100100010111001000010

So, now we have the encoding of the left group and the encoding of the right group. These two encodings are concatenated but separated by guard bars (which are slightly higher than the others, see picture below):

101 011101100010010100111001001101001110011001 01010 100010010000101011100100100010111001000010 101

Static code table initialization

Some static code tables are required for convenience. These include the code patterns (010...) and the parity codes (eoe...):

static Dictionary<int, Pattern> codes = new Dictionary<int, Pattern>();
static Dictionary<int, Parity> parity = new Dictionary<int, Parity>();

Initialization code looks like this:

static Ean13()
{
   //      #  LEFT ODD   LEFT EVEN  RIGHT
   AddCode(0,
"0001101", "0100111", "1110010"
);
   AddCode(1,
"0011001", "0110011", "1100110"
);
   AddCode(2,
"0010011", "0011011", "1101100"
);
   AddCode(3,
"0111101", "0100001", "1000010"
);
   AddCode(4,
"0100011", "0011101", "1011100"
);
   AddCode(5,
"0110001", "0111001", "1001110"
);
   AddCode(6,
"0101111", "0000101", "1010000"
);
   AddCode(7,
"0111011", "0010001", "1000100"
);
   AddCode(8,
"0110111", "0001001", "1001000"
);
   AddCode(9,
"0001011", "0010111", "1110100"
);

   AddParity(0,
"ooooo"
);
   AddParity(1,
"oeoee"
);
   AddParity(2,
"oeeoe"
);
   AddParity(3,
"oeeeo"
);
   AddParity(4,
"eooee"
);
   AddParity(5,
"eeooe"
);
   AddParity(6,
"eeeoo"
);
   AddParity(7,
"eoeoe"
);
   AddParity(8,
"eoeeo"
);
   AddParity(9,
"eeoeo"
);
}

static void AddCode(int digit, string lhOdd, string lhEven, string
rh)
{
   Pattern p = new Pattern
();
   p.LhOdd = lhOdd; p.LhEven = lhEven; p.Rh = rh;
   codes.Add(digit, p);
}

static void AddParity(int digit, string
par)
{
   parity.Add(digit,
new Parity
(par));
}

Notice that the first odd partity ("o") indicator is omitted in the AddPartity calls because it's always odd (see previous remark). I could have dropped the 0...1 for left group codes and the 1...0 for right group codes as well but didn't do it for clarity.

Two helper classes

I've created two helper classes to aid in working with parities (Parity) and with encoding patterns (Pattern):

class Pattern
{
   private string
lhOdd;

   public string
LhOdd
   {
      get { return
lhOdd; }
      set { lhOdd = value
; }
   }

   private string
lhEven;

   public string
LhEven
   {
      get { return
lhEven; }
      set { lhEven = value
; }
   }

   private string
rh;

   public
string Rh
   {
      get { return
rh; }
      set { rh = value
; }
   }
}

class
Parity
{
   private string
par;

   internal Parity(string
par)
   {
      this
.par = par;
   }

   internal bool IsOdd(int
i)
   {
      return par[i] == 'o'
;
   }

   internal bool IsEven(int
i)
   {
      return par[i] == 'e'
;
   }
}

Painting it

Time to take a look at the painting code. The core Paint method is here:

private int top;

public
Image
Paint()
{
   top = settings.TopMargin;

   Graphics g = Graphics.FromImage(new Bitmap
(1, 1));

   int
width = (3 + 6 * 7 + 5 + 6 * 7 + 3) * settings.BarWidth + settings.LeftMargin + settings.RightMargin + (int)g.MeasureString(code[0].ToString(), settings.Font).Width;
   int
height = settings.BarCodeHeight;

   if (title != null
)
   {
      int h = (int
)g.MeasureString(title, settings.Font).Height;
      height += h;
      top += h;
   }

   Bitmap bmp = new Bitmap(width, height, PixelFormat
.Format32bppArgb);
   g =
Graphics
.FromImage(bmp);
   int
left = settings.LeftMargin;

   //LEFT GUARD
   left = DrawLeftGuard(settings, g, code[0].ToString(), left);

   //LEFT GROUP
   int first = int
.Parse(code[0].ToString());
   Parity
par = parity[first];
   string
digit = code[1].ToString();
   left = Draw(settings, g, left, digit, codes[
int.Parse(digit)].LhOdd);
//Odd
   for (int
i = 2; i <= 6; i++)
   {
      digit = code[i].ToString();
      Pattern p = codes[int
.Parse(digit)];
      left = Draw(settings, g, left, digit, (par.IsOdd(i - 2) ? p.LhOdd : p.LhEven));
   }

   //MIDDLE GUARD
   left = DrawCenterGuard(settings, g, left);

   //RIGHT GROUP
   for (int
i = 7; i <= 12; i++)
   {
      digit = code[i].ToString();
      Pattern p = codes[int
.Parse(digit)];
      left = Draw(settings, g, left, digit, p.Rh);
   }

   //RIGHT GUARD
   left = DrawRightGuard(settings, g, left);

   return
bmp;
}

Rather self-explaining code that should be. The used helper methods Draw and Draw*Guard are listed below:

private static Pen pen = new Pen(Color.Black);
private static Brush brush = Brushes
.Black;

private int Draw(Ean13Settings settings, Graphics g, int left, string digit, string
s)
{
   int h = (int
)(settings.BarCodeHeight * 0.8);
   g.DrawString(digit, settings.Font, brush, left, h + top);
   foreach (char c in
s)
   {
      if (c == '1'
)
         g.FillRectangle(brush, left, top, settings.BarWidth, h);
      left += settings.BarWidth;
   }
   return
left;
}

private int DrawLeftGuard(Ean13Settings settings, Graphics g, string digit, int
left)
{
   int h = (int
)(settings.BarCodeHeight * 0.8);
   g.DrawString(digit, settings.Font, brush, left, h + top);
   left += (
int
)g.MeasureString(digit, settings.Font).Width;
   //TITLE
   if (title != null
)
   g.DrawString(title, settings.Font, brush, left, settings.TopMargin);
   g.FillRectangle(brush, left, top, settings.BarWidth, settings.BarCodeHeight);
//1
   left += settings.BarWidth;
   left += settings.BarWidth;
//0
   g.FillRectangle(brush, left, top, settings.BarWidth, settings.BarCodeHeight);
//1
   left += settings.BarWidth;
   return
left;
}

private int DrawRightGuard(Ean13Settings settings, Graphics g, int
left)
{
   g.FillRectangle(brush, left, top, settings.BarWidth, settings.BarCodeHeight);
//1
   left += settings.BarWidth;
   left += settings.BarWidth;
//0
   g.FillRectangle(brush, left, top, settings.BarWidth, settings.BarCodeHeight);
//1
   left += settings.BarWidth;
   return
left;
}

private int DrawCenterGuard(Ean13Settings settings, Graphics g, int
left)
{
   left += settings.BarWidth;
//0
   g.FillRectangle(brush, left, top, settings.BarWidth, settings.BarCodeHeight);
//1
   left += settings.BarWidth;
   left += settings.BarWidth;
//0
   g.FillRectangle(brush, left, top, settings.BarWidth, settings.BarCodeHeight);
//1
   left += settings.BarWidth;
   left += settings.BarWidth;
//0
   return
left;
}

The result

Below is a sample EAN-13 bar code based on the ISBN (0-7356-1917-4) of the book "Microsoft Windows Internals Fourth Edition" by Mark E. Russinovich and David A. Solomon, a must-have for every Windows developer.

And the original code:

Oh yes, you can download the code here. Have fun!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

More Posts « Previous page - Next page »