Tuesday, September 19, 2006 2:21 AM
bart
An ASP.NET .ashx HTTP handler for Code 39 barcode generation
Introduction
In my previous blog post on Code 39 barcodes, I've shown you guys how to generate Code 39 barcodes using managed code. Today, we'll take it another step forward and make barcode generation available through an .ashx ASP.NET 2.0 HTTP handler.
About ASP.NET 2.0 Generic Handlers
One of the great things in ASP.NET is the concepts of "generic handlers". Basically you can look at it as just another kind of object that can process HTTP requests, however outside the scope of a page (which targets 'classic' HTML-based output). You might have heard about HTTP handlers too and basically these are the same except for the fact that you can bind an HTTP handler to any file extension (as long as you IIS configuration permits) whileas a generic handler just lives behind the .ashx extension and is directly supported in Visual Studio 2005 web site projects. In the end all handlers implement System.Web.IHttpHandler. Last but not least, you'll be able to host any ASP.NET HTTP Handler directly in IIS 7 too (which I'll blog about later on).
Our online Code39 barcode generator
First of all create a new web site project in Visual Studio 2005:

Next, add a new generic handler called "Code39Generator.ashx" to the project:

This will generate the following piece of code:
<%@ WebHandler Language="C#" Class="Code39Generator" %>
using System;
using System.Web;
public class Code39Generator : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
context.Response.Write("Hello World");
}
public bool IsReusable {
get {
return false;
}
}
}
Exactly, "Hello World" made it to the built-in code snippets in Visual Studio 2005. Our task is to create a suitable ProcessRequest method that returns an image with a requested barcode. Requested by means of the querystring that is. The IsReusable property is used by the ASP.NET runtime to find out whether the instance of our handler can be reused for multiple requests. That's fine for us, as we don't have intentions to turn our handler instance to garbage once a request has passed through the pipeline:
public bool IsReusable {
get {
return true;
}
}
On to the real stuff now. First of all make sure the Code39 and Code39Settings classes (see previous post) are available in the context of the web site project. You can do that by CTRL-C,V-ing the code to a class file or by referencing a class library project. I've chosen to walk the former path as shown below:

Make sure to declare the classes as follows:
using
System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
public class Code39Settings
{
...
}
public class Code39
{
...
}
Back to our .ashx file. First let's fix some namespace imports:
using
System;
using System.Web;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
Real stuff happens a little further, in the ProcessRequest method which we define like this:
public void ProcessRequest(HttpContext context)
{
string code = context.Request["code"];
if (code != null)
{
Image img = new Code39(code.ToUpper()).Paint();
context.Response.ContentType = "image/png";
byte[] b = GetImageBytes(img);
context.Response.OutputStream.Write(b, 0, b.Length);
}
}
The basic idea is simple: just take the generated image from our Code39 class's Paint method and send it back to the client as an image/png type of HTTP response. In order to do so, we'll need the bytes of the image.
Note: It would be easier if we could write this:
img.Save(context.Response.OutputStream,
ImageFormat.Png);
However this causes the following exception to occur:
[ExternalException (0x80004005): A generic error occurred in GDI+.]
System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams) +568
System.Drawing.Image.Save(Stream stream, ImageFormat format) +33
Code39Generator.ProcessRequest(HttpContext context) +157
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +390
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +65 |
There are lots of forum threads on this issue (just Live Search for it), most of which have to do with permissions when trying to save images on the server's disk. However, I decided to create a code-based workaround as I have some indications permissions might not be the issue in this case (and even if they were, I rather like to have the solution just work in xcopy deployment scenarios). Therefore, here it is, the GetImagesBytes method:
private byte[] GetImageBytes(Image image)
{
ImageCodecInfo codec = null;
foreach (ImageCodecInfo e in ImageCodecInfo.GetImageEncoders())
{
if (e.MimeType == "image/png")
{
codec = e;
break;
}
}
using (EncoderParameters ep = new EncoderParameters())
{
ep.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, codec, ep);
return ms.ToArray();
}
}
}
This one just works fine. So, press F5 to run the web site project and see the ASP.NET Development Server getting ready to serve you:

Right-click the icon and choose "Open in Web Browser" if the system didn't do this already by itself. You should see the directory listing now:

Click the Code39Generator.ashx file and patch the browser address bar by appending ?code=BART to it (e.g. http://localhost:49587/Code39/Code39Generator.ashx?code=BART - the port will much likely be different on your machine). There we go:

Stay tuned for even more barcode fun soon!
Del.icio.us |
Digg It |
Technorati |
Blinklist |
Furl |
reddit |
DotNetKicks
Filed under: ASP.NET v2.0, Visual Studio 2005, C# 2.0