This project has moved and is read-only. For the latest updates, please go here.

System.AccessViolationException when comparing two images

Feb 24, 2015 at 11:10 PM
Edited Feb 25, 2015 at 12:13 AM
When comparing two images in my project I often get an AccessViolationException from MagickImage.Compare(MagickImage).

I've validated that both of the MagickImages are not null, and are populated.

This error is occasionally thrown on my dev machine (Win 7 64bit) in both Debug & Release. On the deployment machine (Win Server 2012 R2 64bit), it is thrown consistently.

This is the code flow for the comparison:

First I load the images from the drive
/// <summary>
/// Load in images so that a comparison can be done
/// </summary>
public void LoadImages()
{
    Expected.LoadImage(@"c:\");
    Actual.LoadImage(@"c:\");
    Comparison.LoadImage("");
}

/// <summary>
/// Update the image using location
/// </summary>
public void LoadImage(string drive)
{
    if(!String.IsNullOrWhiteSpace(drive + Location) && File.Exists(drive + Location))
    {
        Image = new MagickImage(drive + Location);
    }
    else
    {
        Image = new MagickImage(Resources.DefaultImage);
    }
}
DefaultImage is a 1x1 white pixel that is loaded into the project through C#'s Resource Manager, under the project's Properties > Resources.resx file. Class file is using the appropriate "using namespace.Properties".

Here is a copy of the file in a zip archive: http://www.filedropper.com/1x1

I then execute the comparison:
/// <summary>
/// Compare the actual to the expected with resulting comparison image and error info
/// </summary>
public void Compare()
{
    Error = Actual.Image.Compare(Expected.Image);
    Actual.Image.Compare(Expected.Image, ErrorMetric.Fuzz, Comparison.Image);
}
The first compare "Error = Actual.Image.Compare(Expected.Image);" is where I'm running into the Access Violation.

Below is the stack trace & additional Event Viewer fault information from my deployment machine:
Application: ProjectName.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
Stack:
   at <Module>.Magick.Image.compare(Magick.Image*, Magick.Image*)
   at <Module>.Magick.Image.compare(Magick.Image*, Magick.Image*)
   at ImageMagick.MagickImage.Compare(ImageMagick.MagickImage)
   at DynamicClass.invoke(System.Object, System.Object[])
   at ImageMagick.MagickImage.Compare(ImageMagick.MagickImage)
   at Namespace.ImageCompare.Compare()
   at Namespace.Compare+<>c__DisplayClass2.<CompareImages>b__0()
   at Namespace.TestResult.CaptureException(System.Action)
   at Namespace.Compare.CompareImages()
   at Namespace.ProjectName.Main(System.String[])
Faulting application name: ProjectName.exe, version: 1.0.0.0, time stamp: 0x54eceeb4
Faulting module name: Magick.NET-Q16-x64.dll, version: 7.0.0.9, time stamp: 0x54c6b9b9
Exception code: 0xc0000005
Fault offset: 0x000000000018337d
Faulting process id: 0x2a4
Faulting application start time: 0x01d050838806959c
Faulting application path: D:\ProjectName\ProjectName.exe
Faulting module path: C:\Users\user\AppData\Local\Temp\Magick.NET.7.0.0.0009\Magick.NET-Q16-x64.dll
Report Id: 2bd78f71-bc79-11e4-80ea-069bf534f868
Faulting package full name: 
Faulting package-relative application ID: 
The compare is passed through a delegate to capture any exceptions in a try/catch, but this one manages to still break execution.
/// <summary>
/// Used to encapsulate tests in a try catch and log any exceptions
/// </summary>
/// <param name="action">The Method to wrap in a try/catch</param>
/// <returns>True: Action was completed without issue.</returns>
/// <returns>False: An exception was encountered and logged.</returns>
public bool CaptureException(Action action)
{
    try
    {
        action();
    }
    catch(Exception exception)
    {
        ExceptionMessage.Add(exception.Message);
        StackTrace.Add(exception.StackTrace);

        return false;
    }

    return true;
}

/// <summary>
/// Compare the images in each TestResult
/// </summary>
public void CompareImages()
{
    foreach(TestResult testResult in testResults)
    {
        testResult.CaptureException( () =>
        {
            testResult.Images.LoadImages();
            testResult.Images.Compare();
            // Do other things ...
            testResult.Images.UnloadImages();
        });
    }
}
At this point, I've exhausted everything I can thing of to try and fix this.

Please let me know if any additional information would be helpful.
Feb 25, 2015 at 2:29 AM
Edited Feb 25, 2015 at 2:30 AM
I'm going to try setting "MagickNET.UseOpenCL = false" when I get to my office in the morning and see if that makes a difference.

I will report back with my findings.
Feb 25, 2015 at 6:14 AM
Do 'Actual.Image' and 'Expected.Image' have the same dimensions? And does the problem disappear when you only check them if they have the same size? This might be a bug in ImageMagick.
Feb 25, 2015 at 4:22 PM
Edited Feb 25, 2015 at 4:23 PM
Actual.image will be an image, in png format, with unknown image dimensions, grabbed using Seleniumn's capture screen functionality.

I have been relying on Magick.Net to determine if the images were different based off the MagickErrorInfo, to keep things DRY and SRP as possible. I can try adding in some guard clauses to see if that prevents the issue.
Feb 25, 2015 at 5:00 PM
I am just asking if that fixes the problem to make it easier for me to track down the problem. This will of course be fixed.
Feb 25, 2015 at 5:50 PM
Edited Feb 26, 2015 at 4:40 AM
I figured as much. Until then, and for verification, I'll keep the guard clauses in :).

Good news. Just got a successful run on my deployment machine (this is a first!).

So, it definitely looks like that comparing images with different dimensions is the issue.

Code I used to guard for reference:
/// <summary>
/// Compare the actual to the expected with resulting comparison image and error info
/// </summary>
public void Compare()
{
    if (ImageDimensionsEqual = CheckImageDimensions())
    {
        Error = Actual.Image.Compare(Expected.Image);
        Actual.Image.Compare(Expected.Image, ErrorMetric.Fuzz, Comparison.Image);
    }
}

private bool CheckImageDimensions()
{
    return (Actual.Image.Width == Expected.Image.Width 
        && Actual.Image.Height == Expected.Image.Height);
}

Feb 26, 2015 at 8:58 PM
I fixed the issue in ImageMagick. This problem will be fixed in the next release of Magick.NET and you can then remove the safe guard.
Feb 26, 2015 at 9:18 PM
Thanks for looking into this and resolving the issue dlemstra.
Mar 3, 2015 at 12:01 AM
Edited Mar 3, 2015 at 12:03 AM
dlemstra,

I removed the guards and installed the latest Magick.Net package using NuGet (verified update).

I am no longer getting the AccessViolationException on MagickImage.Compare(MagickImage), but I am now getting an AccessViolationException on MagickImage.Compare(MagickImage, ErrorMetric, MagickImage).
/// <summary>
        /// Compare the actual to the expected with resulting comparison image and error info
        /// </summary>
        public void Compare()
        {
                // No longer throws AccessViolationException
                Error = Actual.Image.Compare(Expected.Image);
                // Throws AccessViolationException
                Actual.Image.Compare(Expected.Image, ErrorMetric.Fuzz, Comparison.Image); 
        }
Mar 3, 2015 at 6:01 AM
Now that is embarrassing. I could not reproduce the access violation from code so I checked the methods that where called. I thought I checked your method but it seems I missed a couple. It has to do with the different dimensions. I will fix this and try to publish a new release this weekend.
Marked as answer by odelik on 3/9/2015 at 11:29 AM
Mar 3, 2015 at 6:24 PM
No worries, we've all been there! I've made at least a dozen "how did I miss that?" mistakes on this project since I started it.

Looking forward to the next update and posting back with a confirmation!