Pitfall: using var and async together

Very poorPoorAverageGoodExcellent (No Ratings Yet) 
Loading...

A few days ago at work, I stumbled upon a sneaky bug in our main app. The code looked innocent enough, and at first glance I couldn’t understand what was wrong… The code was similar to the following:

public async Task<bool> BookExistsAsync(int id)
{
    var store = await GetBookStoreAsync();
    var book = store.GetBookByIdAsync(id);
    return book != null;
}

// For completeness, here are the types and methods used in BookExistsAsync:

private Task<IBookStore> GetBookStoreAsync()
{
    // actual implementation irrelevant
    // ...
}


public interface IBookStore
{
    Task<Book> GetBookByIdAsync(int id);
    // other members omitted for brevity
}

public class Book
{
    public int Id { get; set; }
    // other members omitted for brevity
}

The BookExistsAsync method always returns true. Can you see why ?

Look at this line:

var book = store.GetBookByIdAsync(id);

Quick, what’s the type of book? If you answered Book, think again: it’s Task<Book>. The await is missing! And an async method always returns a non-null task, so book is never null.

When you have an async method with no await, the compiler warns you, but in this case there is an await on the line above. The only thing we do with book is to check that it’s not null; since Task<T> is a reference type, there’s nothing suspicious in comparing it to null. So, the compiler sees nothing wrong; the static code analyzer (ReSharper in this case) sees nothing wrong; and of course the feeble human brain reviewing the code sees nothing wrong either… Obviously, it could easily have been detected with adequate unit test coverage, but unfortunately this method wasn’t covered.

So, how to avoid this kind of mistake? Stop using var and always specify types explicitly? But I like var, I use it almost everywhere! Besides, I think it’s the first time I ever found a bug caused by the use of var. I’m really not willing to give it up…

Ideally, I would have liked ReSharper to spot the issue; perhaps it should consider all Task-returning methods to be implicitly [NotNull], unless specified otherwise. Until then, I don’t have a silver bullet against this issue; just pay attention when you call an async method, and write unit tests!

Visual Studio Online + Git integration with Team Explorer

Very poorPoorAverageGoodExcellent (No Ratings Yet) 
Loading...

I recently started using Visual Studio Online for personal projects, and I must say it’s a pretty good platform, although it would be nice to be able to host public projects as well as private ones. The thing I like the most is the integration with Visual Studio Team Explorer to manage work items and builds.

However, I noticed a little gotcha when using Git for source control : the remote for VS Online must be named origin, otherwise Team Explorer won’t detect that it’s a VS Online project, and it won’t show the “Builds” and “Work items” pages.

 When VSO remote is named "origin" When VSO remote is named "vso"

This is obviously a bug (although a minor one), since the name origin is just a convention and a git remote can have any name; I reported it on Connect. If you encounter it you can easily work around it by renaming your remote to origin:

git remote rename vso origin

[WPF] Paste an image from the clipboard (bug in Clipboard.GetImage)

Very poorPoorAverageGoodExcellent (6 votes) 
Loading...

Oops… 2 months already since my previous (and first) post… I really have to get on a more regular schedule 😉

If you’ve ever tried to use the Clipboard.GetImage method in WPF, you probably had an unpleasant surprise… In fact, this method returns an InteropBitmap which, in some cases (most cases actually), can’t be displayed in an Image control : no exception is thrown, the image size is correct, but the image either appears empty or unrecognizable.

However, if we save that image to a stream and re-read it from the stream, we get a perfectly usable image… So this could be an acceptable workaround, but I think its pretty bad for performance, because the image gets decoded, re-encoded, and re-decoded. It is also possible to use the Clipboard class from Windows Forms, which works fine, and convert the System.Drawing.Image to a System.Windows.Media.ImageSource, but I don’t like the idea of referencing the Windows Forms assembly in a WPF app… So I decided to manually retrieve the image from the clipboard and handle the decoding myself.

If we look at the image formats available from the clipboard (Clipboard.GetDataObject().GetFormats()), we can see that they depend on the origin of the image (screenshot, copy from Paint…). The only format that is always available is DeviceIndependentBitmap (DIB). So I tried to retrieve the MemoryStream for this format and decode it into a BitmapSource :

        private ImageSource ImageFromClipboardDib()
        {
            MemoryStream ms = Clipboard.GetData("DeviceIndependentBitmap") as MemoryStream;
            BitmapImage bmp = new BitmapImage();
            bmp.BeginInit();
            bmp.StreamSource = ms;
            bmp.EndInit();
            return bmp;
        }

Unfortunately, this code throws a nasty NotSupportedException : « No imaging component suitable to complete this operation was found ». In other words, it doesn’t know how to decode the contents of the stream… That’s quite surprising, because DIB is a very common format. So I had a look at the structure of a DIB in MSDN documentation. Basically, a « classical » bitmap file (.bmp) is made of the following sections :

  • File header (BITMAPFILEHEADER structure)
  • Bitmap header (BITMAPINFO structure)
  • Palette (array of RGBQUAD)
  • Raw pixel data

If we observe the content of the DIB from the clipboard, we can see that it has the same structure, without the BITMAPFILEHEADER part… so the trick is just to add that header at the beginning of the buffer, and use this complete buffer to decode the image. Doesn’t seem so hard, does it ? Well, the trouble is that we have to fill in some of the header fields… for instance, we must provide the location at which the actual image data begins, so we must know the total size of the headers and palette. These values can be read or calculated from the content of the image. The following code performs that task and returns an ImageSource from the clipboard :

        private ImageSource ImageFromClipboardDib()
        {
            MemoryStream ms = Clipboard.GetData("DeviceIndependentBitmap") as MemoryStream;
            if (ms != null)
            {
                byte[] dibBuffer = new byte[ms.Length];
                ms.Read(dibBuffer, 0, dibBuffer.Length);

                BITMAPINFOHEADER infoHeader =
                    BinaryStructConverter.FromByteArray<BITMAPINFOHEADER>(dibBuffer);

                int fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
                int infoHeaderSize = infoHeader.biSize;
                int fileSize = fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage;

                BITMAPFILEHEADER fileHeader = new BITMAPFILEHEADER();
                fileHeader.bfType = BITMAPFILEHEADER.BM;
                fileHeader.bfSize = fileSize;
                fileHeader.bfReserved1 = 0;
                fileHeader.bfReserved2 = 0;
                fileHeader.bfOffBits = fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4;

                byte[] fileHeaderBytes =
                    BinaryStructConverter.ToByteArray<BITMAPFILEHEADER>(fileHeader);

                MemoryStream msBitmap = new MemoryStream();
                msBitmap.Write(fileHeaderBytes, 0, fileHeaderSize);
                msBitmap.Write(dibBuffer, 0, dibBuffer.Length);
                msBitmap.Seek(0, SeekOrigin.Begin);

                return BitmapFrame.Create(msBitmap);
            }
            return null;
        }

Definition of the BITMAPFILEHEADER and BITMAPINFOHEADER structures :

        [StructLayout(LayoutKind.Sequential, Pack = 2)]
        private struct BITMAPFILEHEADER
        {
            public static readonly short BM = 0x4d42; // BM

            public short bfType;
            public int bfSize;
            public short bfReserved1;
            public short bfReserved2;
            public int bfOffBits;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct BITMAPINFOHEADER
        {
            public int biSize;
            public int biWidth;
            public int biHeight;
            public short biPlanes;
            public short biBitCount;
            public int biCompression;
            public int biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public int biClrUsed;
            public int biClrImportant;
        }

Utility class to convert structures to binary :

    public static class BinaryStructConverter
    {
        public static T FromByteArray<T>(byte[] bytes) where T : struct
        {
            IntPtr ptr = IntPtr.Zero;
            try
            {
                int size = Marshal.SizeOf(typeof(T));
                ptr = Marshal.AllocHGlobal(size);
                Marshal.Copy(bytes, 0, ptr, size);
                object obj = Marshal.PtrToStructure(ptr, typeof(T));
                return (T)obj;
            }
            finally
            {
                if (ptr != IntPtr.Zero)
                    Marshal.FreeHGlobal(ptr);
            }
        }

        public static byte[] ToByteArray<T>(T obj) where T : struct
        {
            IntPtr ptr = IntPtr.Zero;
            try
            {
                int size = Marshal.SizeOf(typeof(T));
                ptr = Marshal.AllocHGlobal(size);
                Marshal.StructureToPtr(obj, ptr, true);
                byte[] bytes = new byte[size];
                Marshal.Copy(ptr, bytes, 0, size);
                return bytes;
            }
            finally
            {
                if (ptr != IntPtr.Zero)
                    Marshal.FreeHGlobal(ptr);
            }
        }
    }

The image returned by that code can be safely used in an Image control.

That goes to show that, even with a state-of-the-art technology like WPF, we still have to get our hands dirty sometimes ;). Let’s hope Microsoft will fix this in a later version…

css.php