I came across an interesting problem recently that reminded about why I both love and remain deeply suspicious of the .NET framework. A web server was retrieving images from some service and almost over night those images refused to render. For the sake of brevity (and security) I am missing out much of the architecture but essentially we were taking a valid Base64 encoded string, converting it into a byte array, then creating an image from that byte array (pretty standard stuff). I was able to replicate the portion of the code causing the issue and It seemed to stem from an innocent looking static method on the Image object called FromStream. Here is the code:
class Program
{
static void Main(string[] args)
{
byte[] backImageBytes = null;
try
{
string val = GetBase64String();
backImageBytes = Convert.FromBase64String(val);
using (MemoryStream m = new MemoryStream(backImageBytes))
using (Image backImage = Image.FromStream(m,false, false))
{}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
Here was the exception I was getting on the web Servers, and it was only occurring on the web server, I simply could not replicate it on my developer laptop any other machine for that matter.
Exception: System.ArgumentException
Message: Invalid parameter used.
Source: System.Drawing
at System.Drawing.Image.FromStream(Stream stream, Boolean useEmbeddedColorManagement, Boolean validateImageData)
at System.Drawing.Image.FromStream(Stream stream, Boolean useEmbeddedColorManagement)
at System.Drawing.Image.FromStream(Stream stream)
at ConsoleApp1.Main(string [] args) in C:\dev\ConsoleApp1\Main.cs:line 132
My immediate assumption was that this was some problem with an older version of the framework that the web server was using but I quickly realized that appropriate versions existed on all the servers and desktop machines I was testing . So at this point I wanted to see what the Image.FromStream() method was actually doing (image from Reflector).
So I immediately see SafeNativeMethods.Gdip.GdipLoadImageFromStream() and roll my eyes, this is not going to end well (or here for that matter). So what is it? *It* is a DllImport, a platform invoke that allows managed code to call unmanaged functions from DLLs that *should* exist on your server.
[DllImport("gdiplus.dll", CharSet=CharSet.Unicode, SetLastError=true, ExactSpelling=true)]
internal static extern int GdipLoadImageFromStream(UnsafeNativeMethods.IStream stream, out IntPtr image);
So what I now appreciate is that the framework only provides a relatively thin layer of abstraction over a native Win32 assembly by the name of gdiplus.dll (GDI+) for image related manipulation. The above error is actually occurring at a level that it is somewhat more fundamental to Windows. The truth is that the server was probably a little older than it should be, and even a subtle change in the image type (say gif to tiff) would result in this kind of failure, and without a GDI+ update this error would have probably continued to occur.
So …what do I love about .NET framework? The consistency you are presented with no matter what server or machine you are running on. What am I suspicious about? The vast portions of “core” code that rely exclusively on unmanaged code that have nothing to do with the version of the .NET framework you have installed. You should take a look one layer down it may actually surprise you how many PInvoke calls you are currently making.
Comments are closed.