Solved

Return MD5Hash from .NET DLL - different on Windows 7

Posted on 2011-02-22
14
545 Views
Last Modified: 2012-05-11
I have a VB6 app that calls a .NET dll (written in C#).  The .NET dll has a function that accepts a pathway to an image and returns the MD5 Hash.  

The .NET code works fine on all operating systems and returns the same md5 for the test image.

Calling this function from within vb6 on a XP or Vista machine produces the same md5hash as running the .NET code alone would produce.  However, when the VB6 code is run on a windows 7 box the md5 hash is different.

Do anyone know why this is happening?


VB6 CODE:
  Dim myObject As MD5Hash.cHash
  Set myObject = New MD5Hash.cHash
  MsgBox myObject.MD5Hash("c:\orig_106451_001.jpg")

.NET CODE:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.IO;

namespace MD5Hash
{
    public class cHash
    {
        static MD5 md5 = new MD5CryptoServiceProvider();

        public cHash()
            {
            }

        public string MD5hash(string path)
        {            
            string hexaHash = "";
            foreach (byte b in md5.ComputeHash(BmpToBytes_Unsafe(new Bitmap(path))))
            {
                hexaHash += String.Format("{0:x2}", b);
            }
           
            using (StreamWriter osw = new StreamWriter(@"c:\md5hash.log", true))
            {
                osw.WriteLine(String.Format("{0} - {1}", System.DateTime.Now, hexaHash));
            }
            return hexaHash;
        }

        static private unsafe byte[] BmpToBytes_Unsafe(Bitmap bmp)
        {
            BitmapData bData = bmp.LockBits(new Rectangle(new Point(), bmp.Size),
                ImageLockMode.ReadOnly,
                PixelFormat.Format24bppRgb);
            // number of bytes in the bitmap
            int byteCount = bData.Stride * bmp.Height;
            byte[] bmpBytes = new byte[byteCount];

            // Copy the locked bytes from memory
            Marshal.Copy(bData.Scan0, bmpBytes, 0, byteCount);

            // don't forget to unlock the bitmap!!
            bmp.UnlockBits(bData);

            return bmpBytes;
        }
    }
}
0
Comment
Question by:dlsimic
  • 7
  • 6
14 Comments
 
LVL 23

Expert Comment

by:wdosanjos
Comment Utility
I suspect it has something to do with 32-bit and 64-bit Windows and your unsafe method.  Check if the C# dll is built with Platform Target = 'Any CPU' or 'x86'.  If it is set to 'Any CPU', try building it with 'x86' and check if the discrepancy is resolved.

 Build Configuration
0
 

Author Comment

by:dlsimic
Comment Utility
It was set to Any CPU.  I changed it to x86 and it has not made a difference.

Any other suggestions?
0
 
LVL 23

Expert Comment

by:wdosanjos
Comment Utility
Alternatively you could change your cHash class to avoid the unsafe method as follows:

public class cHash
{
	static MD5 md5 = new MD5CryptoServiceProvider();

	public cHash()
		{
		}

	public string MD5hash(string path)
	{            
		string hexaHash = "";
		using (FileStream image = new FileStream(path, FileMode.Open))
		{
			foreach (byte b in md5.ComputeHash(image))
			{
				hexaHash += String.Format("{0:x2}", b);
			}
		
			using (StreamWriter osw = new StreamWriter(@"c:\temp\md5hash.log", true))
			{
				osw.WriteLine(String.Format("{0} - {1}", System.DateTime.Now, hexaHash));
			}
		}
		return hexaHash;
	}
}

Open in new window

0
 

Author Comment

by:dlsimic
Comment Utility
I will check if this will make a difference.  The unsafe block of code is to rip out the image portion from the jpeg.  We change exif data and I didn't want that affecting my hash.

I will let you know shortly if this matters.
0
 

Author Comment

by:dlsimic
Comment Utility
If I remove the unsafe block the md5 is the same whether going through just plain .NEt or with the vb6 wrapper.  So this at least narrows down where the problem is happening.

However, we really need the md5 to only be of the image data itself.

Any ideas why vb6 is changing the data?  Maybe you know of different code for my function bmpToBytes_Unsafe to would work through vb6 also (on a windows 7 machine).
0
 
LVL 23

Expert Comment

by:wdosanjos
Comment Utility
Please try the following code.  It extracts the image from the file without using the unsafe code.  I successfully tested here with a couple of .jpg's where the only difference was the exif data, and they returned the same MD5 checksum.

public class cHash
{
	static MD5 md5 = new MD5CryptoServiceProvider();

	public cHash()
		{
		}

	public string MD5hash(string path)
	{            
		string hexaHash = "";

		Image image = Image.FromFile(path);
		
		MemoryStream buffer = new MemoryStream();
		
		image.Save(buffer, ImageFormat.Bmp);
		
		buffer.Seek(0, SeekOrigin.Begin);

		foreach (byte b in md5.ComputeHash(buffer))
		{
			hexaHash += String.Format("{0:x2}", b);
		}
	
		using (StreamWriter osw = new StreamWriter(@"c:\temp\md5hash.log", true))
		{
			osw.WriteLine(String.Format("{0} - {1}", System.DateTime.Now, hexaHash));
		}

		return hexaHash;
	}
}

Open in new window

0
 

Author Comment

by:dlsimic
Comment Utility
No, still get different hash results through VB6.

It is funny it you write the whole file to byte array you get the same results.  But once you start getting just portions of the file it stops working.
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 23

Expert Comment

by:wdosanjos
Comment Utility
I'll test it on my home Windows 7 machine later today.  I'll post my finds.
0
 
LVL 23

Expert Comment

by:wdosanjos
Comment Utility
I was able to reproduce the issue on my Windows 7 machine.

The Windows 7 GDI+ library seems to be writing different values on the bitmap header fields (more specifically the vertical & horizontal resolution of the image). Thus causing the discrepancy.  The code below skips the bitmap headers and generates the MD5 only over the Pixel Array.

I tested the code on XP and 7. On both systems the MD5 values are now matching.

public class cHash
{
    static MD5 md5 = new MD5CryptoServiceProvider();

    public cHash()
    {
    }

    public string MD5hash(string path)
    {
        string hexaHash = "";

        Image image = Image.FromFile(path);

        MemoryStream buffer = new MemoryStream();

        image.Save(buffer, ImageFormat.Bmp);

        image.Dispose();

        // offset (ie, starting address of byte where the bitmap image data (Pixel Array) can be found
        buffer.Seek(0x0A, SeekOrigin.Begin);

        byte[] offsetField = new byte[4];
        int offset;
        
        buffer.Read(offsetField, 0, offsetField.Length);

        offset = (offsetField[2] << 24) 
               | (offsetField[3] << 16)
               | (offsetField[0] << 8)
               | offsetField[1];

        buffer.Seek(offset, SeekOrigin.Begin);

        // Compute the md5
        foreach (byte b in md5.ComputeHash(buffer))
        {
            hexaHash += String.Format("{0:x2}", b);
        }

        // Log
        using (StreamWriter osw = new StreamWriter(@"c:\temp\md5hash.log", true))
        {
            osw.WriteLine(String.Format("{0} - {1}", System.DateTime.Now, hexaHash));
        }

        return hexaHash;
    }
}

Open in new window


Please let me know if it works for you.
0
 
LVL 23

Accepted Solution

by:
wdosanjos earned 500 total points
Comment Utility
To troubleshoot this issue, I generated a bmp from jpeg using the Image class (Image.Save) on XP and 7. I then compared the files for differences.  There are only 4 mismatching bytes on the bmp header vertical and horizontal resolution fields.  All the other bytes on the bmp files match.

 Difference
0
 

Author Comment

by:dlsimic
Comment Utility
Sorry, we have been playing around with this a little more and here is what we have come up with.  I think your first guess about the x86 compile was on the right track.

We can reproduce this error from within .NET itself if you change the target platform to x86.  Granted I am on a x64 machine, but when I run as any cpu or x64 it works fine.  However, if I change the target platform to x86 is when I have the problem.

This leads me to believe the vb6 is forcing it to run as x86 (which is why I believe you asked to compile it for x86 to start with).

The code you gave will work to get around the bits that are changing, however; our problem is that we have a distributed application that already is using the md5 for years based on the byte range we were previously looking at.  Any changes will cause the program to not work properly because we are expecting the md5 as a certain output.

It seems to me that there is a bug in the x86 framework that is changing the vertical and horizontal resolution when the bytes are read (doing some kind of auto-rotation upon reading the bytes).

We really need to find away around this so that we can get the expected results.

Thanks for your help.
0
 
LVL 23

Expert Comment

by:wdosanjos
Comment Utility
Can you provide a few sample images and the expected MD5 Checksum?
0
 

Author Comment

by:dlsimic
Comment Utility
Here is the image we have been playing with.

MD5 should be: 4371f1a86186da2077ee65495c5b1594
orig-106451-001.jpg
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
Comment Utility
Here is result running your code. The byte size is the same so there must be a change in the bytes when copying as the above posts mentions.
Windows 7 64 bit OS

// Running x64
4371f1a86186da2077ee65495c5b1594

// Running x86
62a46935ff2178bee1db6c477e1dc032

Open in new window

0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Most everyone who has done any programming in VB6 knows that you can do something in code like Debug.Print MyVar and that when the program runs from the IDE, the value of MyVar will be displayed in the Immediate Window. Less well known is Debug.Asse…
When designing a form there are several BorderStyles to choose from, all of which can be classified as either 'Fixed' or 'Sizable' and I'd guess that 'Fixed Single' or one of the other fixed types is the most popular choice. I assume it's the most p…
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

7 Experts available now in Live!

Get 1:1 Help Now