0

I am desperatly finding a solution for my code. I run it in a thread using backgroundworker. and img.Save frequently(not always) gives this error.

image1.Save(ms1, imageCodecInfo, parametreler);

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

at System.Drawing.SafeNativeMethods.Gdip.GdipSaveImageToStream(HandleRef image, IStream stream, Guid& classId, HandleRef encoderParams) at System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams) at Project.Forms.Degerlendirme.EditProductPhotosForm.backgroundWorker_uploader2_DoWork(Object sender, DoWorkEventArgs e) in C:\Users\Umut\NETProjects\Project\Forms\Katalog\EditProductPhotosForm.cs:line 654 at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument) at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Object[]& outArgs) at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink) at System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.ThreadPoolCallBack(Object o) at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() at System.Threading.ThreadPoolWorkQueue.Dispatch() at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

I read and tried every post, but none of them helped me. I guess I am missing a point. Here is my code.

    private void backgroundWorker_uploader1_DoWork(object sender, DoWorkEventArgs e)
    {
        object[] parameters = e.Argument as object[];
        string fileName = parameters[1] as string;
        string filePath = parameters[4] as string;

        using (Image image1 = parameters[0] as Image)
        using (MemoryStream ms1 = new MemoryStream())
        {
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
            ImageCodecInfo imageCodecInfo = null;

            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.MimeType == "image/jpeg")
                    imageCodecInfo = codec;
            }

            EncoderParameters parametreler = new EncoderParameters(1);
            parametreler.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)100);
            //Object imageLock1 = new Object();

            lock (image1)
            {
                image1.Save(ms1, imageCodecInfo, parametreler);
            }

            using (Image img1 = Image.FromStream(ms1))
            {
                bool sonuc = HelperActions.UploadImgToFTPfromImage(fileName, img1);
                if (sonuc == true)
                {
                    e.Result = new object[6] { this, prevForm, true, (int)parameters[5], (int)parameters[2], (int)parameters[3] };
                }
                else
                {
                    e.Result = new object[6] { this, prevForm, false, (int)parameters[5], (int)parameters[2], (int)parameters[3] };
                }
            }
        }
    }

this is how I call this backgroundworker. I have 6 of them, their dowork event almost same.

       for (int i = 0; i < mappings.Count; i++)
        {
            Picture picture = mappings[i].Picture as Picture;
            if (picture == null)
                continue;

            mappings[i].DisplayOrder = (int)numUpDowns[i].Value - 1;
            entities.Product_Picture_Mapping.AddOrUpdate(mappings[i]);

            string fileName = HelperActions.CreateImgFileName(picture);
            string filePath = "";

            object obje11 = picEdits[i].Image as Image;
            object obje12 = fileName;
            object obje13 = picture.Id;
            object obje14 = i + 1;
            object obje15 = filePath;
            object obje16 = productId;
            object[] parameters = new object[6] { obje11, obje12, obje13, obje14, obje15, obje16 };
            try
            {
                switch (i)
                {
                    case 0:
                        backWorker1_uploader.RunWorkerAsync(parameters);
                        break;
                    case 1:
                        backWorker2_uploader.RunWorkerAsync(parameters);
                        break;
                    case 2:
                        backWorker3_uploader.RunWorkerAsync(parameters);
                        break;
                    case 3:
                        backWorker4_uploader.RunWorkerAsync(parameters);
                        break;
                    case 4:
                        backWorker5_uploader.RunWorkerAsync(parameters);
                        break;
                    case 5:
                        backWorker6_uploader.RunWorkerAsync(parameters);
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                if (!backgroundWorkerYedek.IsBusy)
                {
                    backgroundWorkerYedek.RunWorkerAsync(parameters);
                    Console.WriteLine("Yedek Çalıştırıldı");
                }
                else
                {
                    MessageBox.Show(i + " nolu - " + ex.Message);
                }   
            }
            System.Threading.Thread.Sleep(200);
        }
        entities.SaveChanges();
smoothumut
  • 3,423
  • 1
  • 25
  • 35
  • Why do you have a lock statement there - there are no other threads executing this code so there's no need for any synchronisation ? – auburg Feb 23 '18 at 15:21
  • @auburg Actually I have different threads. I have changed the variable names I have tried that without it but it didnt make a change – smoothumut Feb 23 '18 at 15:22
  • What if you lock on not the image but a dummy object i.e. lock(lockObj) - see https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement – auburg Feb 23 '18 at 15:24
  • omg use classes instead of arrays of object, you are seriously abusing boxing – TheGeneral Feb 23 '18 at 15:24
  • @MichaelRandall I have done that first, but after getting errors during full day, I have tried this way to see if it changes – smoothumut Feb 23 '18 at 15:27
  • @auburg thanks for your attention, nop, I have tried that befor and it didnt work. Now I tried again and it works first , it didnt work in secand attempt – smoothumut Feb 23 '18 at 15:43
  • I suggest you initially simplify your code : first have it with no background worker (and no locking) and see if you get the error. Then have a single background worker working on one image file. – auburg Feb 23 '18 at 15:58
  • @auburg I got a point. When I increase the thread sleep amount to 1500 it works in most cases. but still not guaranteed. What could be the problem – smoothumut Feb 23 '18 at 16:08
  • And you have attached the debugger? The exception(s) and callstack(s) usually are most informative. – Chris O Feb 23 '18 at 16:08
  • Have you tried using Tasks.Run instead of BackgroundWorker ? – auburg Feb 23 '18 at 16:17
  • @auburg no I havent, I will try – smoothumut Feb 23 '18 at 16:26
  • @ChrisO I have added stacktrace – smoothumut Feb 23 '18 at 16:31
  • Thank you, your question is much improved. As an experiment, can you try `lock`ing on a static object? I don't know if the GDI+ call internally is using resources that cannot be accessed concurrently. So if you serialize the calls to `Save` and the exception goes away, that might be a big clue. Sometimes the MSDN give hints on whether .NET objects can be called concurrently. – Chris O Feb 23 '18 at 16:55
  • @ChrisO do you mean one global static object? – smoothumut Feb 23 '18 at 17:00

1 Answers1

1

Just looked in the docs: MS post on GDI+ and concurrency

According to that post, you cannot use GDI+ concurrently, but can instead use WPF for your imaging operations. All of your GDI+ operations do have WPF counterparts, but you'll have to look around on how to do that.

EDIT: Or, as a workaroud, lock on a static object to effectively serialize all usage of the GDI+

static object _syncRoot = new object();

And Use that in your lock statements for the bacground worker thread

lock (_syncRoot)

That syncRoot naming is a bit of a pattern these days

EDIT 2: you don't need the static if you can ensure that only one single instance of _syncRoot is being used by the multiple threads. The _syncRoot can be a private member of the class that also starts the background threads.

Chris O
  • 5,017
  • 3
  • 35
  • 42
  • Hi Cris It worked 5-6 times then it didnt work on 7th one. the same error again – smoothumut Feb 23 '18 at 17:23
  • @smoothumut Do you have a new exception/callstack? – Chris O Feb 23 '18 at 17:25
  • Hi, thanks again for your help. It looks it is working But I am not sure. I did get one time and now I tried many times but didnt get any yet. Thats good news but I can check only tomorrow. I will let you know for the progress. Thanks again my friend. – smoothumut Feb 23 '18 at 17:48
  • Hi Chris, I got the same error with the same stacktrace. But I have successfully run it 10 times or so. But then I dont know why it gives the same error – smoothumut Feb 24 '18 at 06:19
  • @smoothumut I'm afraid that workaround is not good enough then, you'll have to use the WPF equivalent of saving a Jpeg. – Chris O Feb 25 '18 at 19:53