Delay's Blog is the blog of David Anson, a Microsoft developer who works with the Silverlight, WPF, Windows Phone, and web platforms.
http://dlaa.me/
@DavidAns
There's plenty to say about the IDisposable interface and the using statement, but you probably don't have time to read it all (and I don't have time to write it all!), so I'm going to try to keep this short and simple.
First, let's make sure we're all on the same page. If you're not familiar with the relevant concepts, please take a moment to learn about .NET Garbage Collection, the IDisposable interface, the using statement, and the use of objects that implement IDisposable. (If you're a bibliophile, I understand that the book "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" (ISBN 0321246756) contains additional material in section 10.3, "Dispose Pattern".)
Now that we're all familiar with the concepts, I'd like to call attention to a few things:
With these points in mind, I propose following guidelines whenever dealing with an object that implements the IDisposable interface:
Conveniently, the using statement makes it easy to do *all* of these things! The using statement is a simple programming construct that's very readable and that helps your code perform reliably, predictably, and efficiently. It doesn't get much better than that, so if you aren't already, please start using using today!
In Part 1 we investigated a curious tool failure and discovered that it's possible for something to be both IN and NOT IN the GAC at the same time. The results of the investigation so far have been informative, but unrevealing. So let's try another approach...
The Sysinternals Filemon tool should let us see exactly where the tool is looking for vjslib.dll and maybe that will help figure out why it can't be found. Run Filemon, specify a filter of "*tool_name*" to limit the output, then run the program of interest. Filemon will capture a whole bunch of stuff, so save the output to a file where it can be searched more easily. We'll start with a simple string search for "vjslib" to see what turns up:
D:\Temp>findstr vjslib Filemon.LOG 327 6:58:10 PM GACBlog.exe:212 QUERY INFORMATION C:\WINDOWS\assem bly\GAC_64\vjslib\2.0.0.0__b03f5f7f11d50a3a PATH NOT FOUND Attributes: Erro r 328 6:58:10 PM GACBlog.exe:212 QUERY INFORMATION C:\WINDOWS\assem bly\GAC_MSIL\vjslib\2.0.0.0__b03f5f7f11d50a3a PATH NOT FOUND Attributes: Erro r 329 6:58:10 PM GACBlog.exe:212 QUERY INFORMATION C:\WINDOWS\assem bly\GAC\vjslib\2.0.0.0__b03f5f7f11d50a3a PATH NOT FOUND Attributes: Erro r ...
Oooh, that's interesting, there seem to be multiple GACs: GAC_64, GAC_MSIL, and GAC each get probed unsuccessfully. But we recall from Part 1 that gacutil told us vjslib was in the GAC, so what GAC is it in??
C:\WINDOWS\assembly>dir vjslib.dll /s Volume in drive C is C Volume Serial Number is 0C48-9782 Directory of C:\WINDOWS\assembly\GAC_32\vjslib\2.0.0.0__b03f5f7f11d50a3a 2006-02-21 11:18 AM 3,661,824 vjslib.dll 1 File(s) 3,661,824 bytes Total Files Listed: 1 File(s) 3,661,824 bytes ...
Ah-ha! There's a fourth GAC, GAC_32, that contains the required assembly, but that GAC isn't getting checked when the tool is run and so the tool is failing.
At this point we can begin to guess that the problem is unique to my machine because I'm the only person who's trying to run the tool under a 64-bit OS. (Which also explains why most people won't have been able to reproduce this problem with the sample code in Part 1.) Now that we understand the problem a little better, we can pretty easily find more detailed information about what's going on by doing some quick web searching. In this case, Junfeng Zhang's post GAC, Assembly ProcessorArchitecture, and Probing explains the motivation behind multiple GACs.
Okay, so we've figured out what the problem is: the tool is compiled to be architecture-independent and the GAC probing sequence for architecture-independent programs on 64-bit OSes does not include the architecture-dependent 32-bit GAC where vjslib.dll is actually installed. The question now becomes how to fix the tool so that it will run successfully on both 32- and 64-bit OSes.
My first thought was to find out how to modify the probing sequence for the tool in order to include the 32-bit GAC. While that may be possible and that approach may work, a little more thought convinced me it wasn't the right approach to take here. The way I reasoned, if the tool has a dependency on a 32-bit reference, then the tool is not really architecture-independent after all! The problem is that the tool is claiming to be something it's not and that's what's causing it problems. If, when we compiled the tool, we were to specify the platform target of "x86" instead of "Any CPU" in Visual Studio (or using the /platform C# compiler option), then the tool should run successfully because it would naturally probe the 32-bit GAC where vjslib.dll lives.
And, indeed, that simple change fixes the tool, solves the problem, and answers the question of how something can be both IN and NOT IN the GAC at the same time! This investigation was a neat learning experience for me - I hope it's as much fun to read about as it was to experience! :)
I recently had occasion to use a particular tool for the first time and found that it didn't work on my machine. This was weird, because nobody else seemed to have any problems running the same tool on their machines. So I set out to determine what was wrong...
Simplifying things ridiculously for the purposes of this example, I'll note that the tool manipulates ZIP files, has a reference to "vjslib", and is compiled from code that looks something like this:
using java.util.zip; class Program { static void Main(string[] args) { new ZipFile("file.zip"); } }
(Aside: In case you're wondering what's up with the "java.util.zip" namespace and the reference to "vjslib.dll", I'll suggest that the author of this tool was probably following the recommendations of the article "Using the Zip Classes in the J# Class Libraries to Compress Files and Data with C#" which recommends exactly this approach. You may be aware that .NET 2.0 offers support for compressed streams via the classes in the new System.IO.Compression namespace. However, support for compressed streams is not the same thing as supporting the ZIP file format, so I believe this technique is still relevant.)
When run on my machine, the tool produces the following output (inadvertent profanity due to default 80-column wrapping of "assembly" removed for your protection):
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or as* embly 'vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ' or one of its dependencies. The system cannot find the file specified. File name: 'vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d 50a3a' at Program.Main(String[] args)
That's odd, because my computer DOES have the Microsoft Visual J# Version 2.0 Redistributable Package installed as required (it comes with a Visual Studio Team Suite full install). But it's worth checking the GAC (Global Assembly Cache) anyway, just to be sure that vjslib is present there as we expect:
C:\Program Files\Microsoft.NET\SDK\v2.0 64bit>gacutil -l vjslib Microsoft (R) .NET Global Assembly Cache Utility. Version 2.0.50727.42 Copyright (c) Microsoft Corporation. All rights reserved. The Global Assembly Cache contains the following assemblies: vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, pro cessorArchitecture=x86 Number of items = 1
Yup, it's in there. So why can't it be found by the tool? To try to answer that question, we turn to the Assembly Binding Log Viewer (Fuslogvw.exe). Just run the viewer, enable the "Log bind failures to disk" setting, run the tool again, then refresh the viewer and open the failed binding entry to see the following (abbreviated) output:
*** Assembly Binder Log Entry (2006-03-23 @ 10:52:44 AM) *** The operation failed. Bind result: hr = 0x80070002. The system cannot find the file specified. ... LOG: Post-policy reference: vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a LOG: GAC Lookup was unsuccessful. ... LOG: All probing URLs attempted and failed.
Hum, that's really odd... We know vjslib is in the GAC, yet it can't be found in the GAC. My machine is correctly configured, has the necessary components installed, and appears to be working fine in every other respect.
So what's going on here??
(Stay tuned for the exciting answer in Part 2!)
In a previous post, I referred to some MSDN sample code and outlined the process of creating an ASP.NET page to display an image instead of HTML. As is often the case, the relevant sample code was written to demonstrate a concept rather than to be "production-ready". In one of the comments for that post, Heath Stewart suggested the use of a .ashx file as a more efficient way to do the same thing. This seems like a great opportunity to learn a little more about ASP.NET, so let's give it a try!
First, a little research is in order - I recommend starting with the documentation for Creating HttpHandlers and following up by learning a little about the corresponding IHttpHandler Interface. Armed with that knowledge, we should be able to convert the modified sample code over to the IHttpHandler interface.
Begin by creating a new file in the web site directory you were already using. I used Visual Studio's "Add New Item" action to add a "Generic Handler" page that was automatically named Handler.ashx. I then pasted in the existing code from my earlier post, tweaked a few minor things, and had a working IHttpHandler in considerably less time than it's taken me to write this post. :)
Here's what I ended up with in my Handler.ashx:
<%@ WebHandler Language="C#" Class="Handler" %> using System.Drawing; using System.Drawing.Imaging; using System.Web; public class Handler : IHttpHandler { public void ProcessRequest(HttpContext context) { // Set the page's content type to JPEG files context.Response.ContentType = "image/jpeg"; // Create integer variables. int height = 100; int width = 200; // ... // Create a bitmap and use it to create a // Graphics object. using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb)) { using (Graphics g = Graphics.FromImage(bmp)) { // ... g.Clear(Color.LightGray); // ... } // Save the bitmap to the response stream and // convert it to JPEG format. bmp.Save(context.Response.OutputStream, ImageFormat.Jpeg); } } public bool IsReusable { get { return true; } } }
As you can see, the code is almost identical to the initial Page-based implementation! The only thing I think is worth calling out is that I've chosen to return "true" for the IsReusable property. The documentation for the IHttpHandler.IsReusable Property suggests that this property can be used to prevent concurrent use of the instance. Since our simple implementation doesn't make use of any global state, it should be safely reentrant, and therefore returns "true" to help ASP.NET avoid creating unnecessary instances of the class.
There you have it: a simple change that gives us an implementation consuming fewer resources and consequently scaling better on a busy web server. Thanks for the great suggestion, Heath!!