Saving DeviceImage crash with System.AccessViolationException

Hi,

I recently distributed Software to our customer and we got a bad image saving issue in their mass production.
Software receives a barcode and a then a trigger via TwinCat Ads Client, sets the ExposureTime for a dark image, snaps an image, set exposuretime to bright image, snaps another image and saves both using the Save() function of DeviceImage.

This however fails for some reason badly by crashing the whole app. I am not even able to catch an exception, it justs crashs the whole process which is very bad in production here. Only info I have is an error log in the windows event log:

    Application: TireDoc.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
Stack:
   at Stemmer.Cvb.ImgLib.WriteLossyImageFile(IntPtr, Double, System.String)
   at Stemmer.Cvb.Image.Save(System.String, Double)
   at TireDoc.ViewModels.MainViewModel+<>c__DisplayClass74_0.<SnapAndSave>b__1()
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   at System.Threading.Tasks.Task.ExecutionContextCallback(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef)
   at System.Threading.Tasks.Task.ExecuteEntry(Boolean)
   at System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

The application can work fine for hours, even days, but sometimes it randomly crashs with these error.
I already got that error once yesterday, I instantly modified my software to sporadically catch any exception occured by Save() function, but even that didnt help.

Why can there be a AccessViolationException?

Hi @johmarjac!

To quote Douglas Adams: Sorry for the inconvenience…

That is hard to say without looking at the finer details. This is how much I can tell from looking at the managed stack trace: As WriteLossyImageFile is the last function on the call stack I presume that the problem occurs inside the CVCFile.dll (this DLL handles the saving of image formats that use lossy compression, currently JPEG and JPEG2000). The CVCFile.dll uses a number of open source libraries to implement various file formats (see OpenSourceLicenses.chm in the %CVB%\Doc folder for the complete list and apparently somewhere between the DllImport function Stemmer.Cvb.ImgLib.WriteLossyImageFile and those libraries something seems to go terribly wrong.

To dig deeper one or both of the following will be needed:

  • an image that triggers this access violation (saved as a lossless format like *.bmp or *.mio!); I know this is probably hard to produce with an error that only occurs intermittently, so the next option might be more easily workable…
  • a crash dump from the moment the uncaught exception occurs; if this is difficult to collect via the TaskManager because nobody is around the moment it happens, the registry settings described in this article should help

Debugging something like that is beyond the scope of the forum, so I would recommend to contact support@stemmer-imaging.de directly. I’ll post the result here once we got the bottom of it.

@illusive Thanks for the help, I have setup the registry keys to write a full dump to %localappdata%\CrashDumps. In the time I wait for another crash I try to replicate this locally on my development machine.

Regarding your first point, getting an image is impossible as it happens while saving this image … So there is no image yet. :joy:

:grin:
I was thinking along the lines of always first saving the image in *.bmp or *.mio format, then in the - potentially crashing - lossy format (*.bmp and *.mio are handled directly by the CVCImg.dll, so if the assumption is correct that the access violation happens in CVCFile.dll saving *.bmp or *.mio first should work).

@illusive I dont know if I understood correctly, you mean I should use the other overload of Save() which just takes the filename and saves the image lossless?
It would be good to find out if really the overload with quality parameter is the issue, but saving images lossless is not applicable in the production. Images with a bigger size than specified are being rejected.

By the way, I have been able to collect a crash dump. Unfortunately its size is 500MB and I am not able to pull it down right now from the machine. Tomorrow evening between 22:00 o’clock and saturday 06:00 o’clock I am hopefully able to download the dump from the machine as it is not in a production state then.

I´ve also made some changes in my code as a temporary (automated) solution.
Created a static Extension class implementing a bool HandledSave(this Image img, string filename, double quality)
which has these two attributes and a naked try {} catch {}

using Stemmer.Cvb;
using System.Runtime.ExceptionServices;
using System.Security;

namespace TireDoc.Utils
{
	public static class ImageExt
	{
		[SecurityCritical]
		[HandleProcessCorruptedStateExceptions]
		public static bool HandledSave(this Image img, string fileName, double quality)
		{
			try
			{
				img.Save(fileName, quality);
				return true;
			}
			catch
			{
				return false;
			}
		}
	}
}

I´ve created a small console application forcing a SystemAccessViolationException and I was able to catch these exception without a application termination.

However in the real software I implemented that on catching these exception, the application will tell the treadmill to stop, restart itself to prevent even worse exceptions, and telling to continue the treadmill. I am not sure if it will work properly as I can not reproduce it on my development machine, but I did that to eventually make sure it does not crash, instead restarting itself

I hope on monday I am able to transfer the dump to you

Hi @johmarjac

Use of Save() without a quality parameter as opposed to Save() with a quality parameter does not imply lossless saving of images. The format that will actually be used for saving is determined based on the file name’s extension, and when calling Image.Save("test.jpg"), the image data still undergoes lossy jpeg compression - but with a default quality value, whereas when calling Image.Save("test.jpg", 0.2) you have control over the quality/size tradeoff of the save operation. Or, to put it differently, the problem does not really have anything to do with what overload you called and would occur identically in both cases.

What I did want to suggest is that alongside with the jpeg image you also save a (lossless) bmp image so that we might be able to investigate if there is a problem caused by the actualy image content - but I have by now received your crash dump, and I do not think that will be necessary any more.

It is a bit harder to figure out what went wrong than I hoped. Here’s what I found out so far:

  • in none of the threads that were active the moment your application crashed was the CVCFile.dll that I did originally suspect based on your report, so a crash inside CVCFile.dll currently seems unlikely.
  • in one thread the CVCImg.dll is on the top of the call stack, but due to a configuration problem on our end I cannot precisely tell what function has been called most recently
  • the thread in whose context the crash appeared has been in the clr.dll function ThreadpoolMgr::GateThreadStart() and only has core CLR or OS runtime functions on the call stack.

Without context I’d be doubting that this is actually happening inside :cvb: functionality, but then again what you describe is a significant correlation between saving the images and the crash occurring after a while. I would therefore like to suggest two things:

  1. Please head to this post and put a LoadLibrary("CVCFile.dll") somewhere in your code to make sure that the CVCFile.dll is always loaded and stays in memory. This is admittedly a bit of a hack for the moment, but what I am aiming at is this: When calling Save() with one of the formats handled through CVCFile.dll then the DLL is loaded dynamically, the save function is called and the DLL gets unloaded (as described in the discussion that I linked to). This has performance implications (see there), but there is a wee chance that the repetitive and manifold load/unload cycles might be otherwise unhealthy and contribute to the problem you’re seeing.
    So my first recommendation would be to try out this workaround. If it works, I’d leave it in (and we put a similar statement somewhere in our code), if not we’ll have to dig deeper.
  2. If we have to dig deeper can you please give this CVCImg.dll a try: CVCImg.zip (591.8 KB); just replace the one that exists in your %CVB% directory with this one (which is basically the same but built with more debug info) and wait for a new crash dump.

Thanks a lot!

Hi @illusive and thanks for your response and analysis of the crash dump.

Now to the two mentioned things:

  1. The CVCFile.dll is located in the %CVB% directory right? Our application does not ship the CVCFile.dll but it is installed using the CVB installation I guess. However it is not in the application directory. Will LoadLibrary() be able to load using just the filename or does it require the full path to the file? Also it is enough to call this once right?

  2. I will first go with step 1 to not mix up things. If I get another crash dump I will try the CVCImg replacement.

I am going to implement the LoadLibrary thing now, wait for pause and try to update my application. I dont know yet when to expect the next dump. The current machine did work for last 4 days now without crashing. Anyways, I’ll do that now. Thanks

Hi @johmarjac

yes, CVCFile.dll is installed by the :cvb: installer into the %CVB% directory. LoadLibrary applies standard DLL resolution rules and the %CVB% directory is by default added to %PATH%, so a simple LoadLibrary("CVCFile.dll") should suffice (check the return value for IntPtr.Zero if you want to make sure it has been loaded).

Otherwise I’ll wait for you findings. Thanks and best regards!

1 Like

@illusive Hi. Software is now loading CVCFile.dll on startup, and LoadLibrarie()'s return value seems to be a valid module handle. I´ve also copied a portable debugger on the machine to verify the module is loaded. It is! :slight_smile: So the application didnt crash since the 15th. Now that I upgraded it to the LoadLibrary version, lets hope for a quick crash… Kind of annoying to wait at least 5 days everytime :smiley:

Edit: @illusive Unfortunately another crash happened yesterday. I am able to replace CVCImg.dll this evening and hope for a quick crash dump again.

@illusive Hi, I´ve uploaded another Crash Dump with the replaced CVCImg.dll on Saturday and sent the dump via secure.imaging.de

Does it help any further in my issue? Thanks in advance