Solved

Return MD5Hash from .NET DLL - different on Windows 7

Posted on 2011-02-22
14
566 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 6
14 Comments
 
LVL 23

Expert Comment

by:wdosanjos
ID: 34954525
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
ID: 34954622
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
ID: 34954676
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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:dlsimic
ID: 34954693
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
ID: 34955183
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
ID: 34955343
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
ID: 34955683
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
 
LVL 23

Expert Comment

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

Expert Comment

by:wdosanjos
ID: 34958599
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
ID: 34958646
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
ID: 34964753
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
ID: 34964956
Can you provide a few sample images and the expected MD5 Checksum?
0
 

Author Comment

by:dlsimic
ID: 34964992
Here is the image we have been playing with.

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

Expert Comment

by:nffvrxqgrcfqvvc
ID: 34970592
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

On Demand Webinar - Networking for the Cloud Era

This webinar discusses:
-Common barriers companies experience when moving to the cloud
-How SD-WAN changes the way we look at networks
-Best practices customers should employ moving forward with cloud migration
-What happens behind the scenes of SteelConnect’s one-click button

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Have you ever wanted to restrict the users input in a textbox to numbers, and while doing that make sure that they can't 'cheat' by pasting in non-numeric text? Of course you can do that with code you write yourself but it's tedious and error-prone …
Background What I'm presenting in this article is the result of 2 conditions in my work area: We have a SQL Server production environment but no development or test environment; andWe have an MS Access front end using tables in SQL Server but we a…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
This lesson covers basic error handling code in Microsoft Excel using VBA. This is the first lesson in a 3-part series that uses code to loop through an Excel spreadsheet in VBA and then fix errors, taking advantage of error handling code. This l…

732 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