Search code examples
c#winformscallbackmarshalling

Generic error occured in GDI+ calling Image.Save


The image is actually a byte array pumped from my native DLL (as Byte *).

I am converting it into an image as follows.

Img = new Bitmap(g_Width, g_Height, g_Width * 3, PixelFormat.Format24bppRgb, ptr);

Note: The "Image" is in the global scope. "ptr" is an IntPtr(local scope).

I am trying to save this image from the BackgroundWorker method. When I do so, I get "A generic error occurred in GDI+" exception.

What is the I am missing?

Edit:

I am trying to save the image like

Img.Save("test.bmp", ImageFormat.Bmp);

Also, I have tried to save the image as soon as I create the image. The file is saved successfully. I am able to display as well. But I am not able to save in from the worker.

Edit 2: Globals:

BackgroundWorker Worker;
Image img = null; 

The callback is as follows,

private unsafe void GrabFrame(Byte* InputBuff, ref Int32 lBufferLen) 
{
   Img = new Bitmap(g_Width, g_Height, g_Width * 3, 
         PixelFormat.Format24bppRgb, (IntPtr)InputBuff);


   //Img.Save("test.bmp", ImageFormat.Bmp);    // This works fine.

   Worker = new BackgroundWorker();
   Worker.DoWork += new DoWorkEventHandler(Worker_DoWork);                              
   Worker.RunWorkerAsync();
}

void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    try
    {
        Img.Save("test.bmp", ImageFormat.Bmp);  //This is were i get the exception
    }
    catch (Exception ex)
    {
        Trace.WriteLine("Exception at Worker_DoWork: " + ex.Message);
    }
}

Modified code:

Globals:

BackgroundWorker Worker;
Image img = null; 
Byte[] g_Buffer;
Intptr ptr;

The callback is as follows,

private unsafe void GrabFrame(Byte* InputBuff, ref Int32 lBufferLen) 
{
   Img = new Bitmap(g_Width, g_Height, g_Width * 3, 
         PixelFormat.Format24bppRgb, (IntPtr)InputBuff);


   g_Buffer = new byte[lBufferLen];
   Marshal.Copy((IntPtr)InputBuff, g_Buffer, 0, lBufferLen);

   //Img.Save("test.bmp", ImageFormat.Bmp);    // This works fine.

   Worker = new BackgroundWorker();
   Worker.DoWork += new DoWorkEventHandler(Worker_DoWork);                              
   Worker.RunWorkerAsync();
}

void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    try
    {
        ptr = Marshal.AllocHGlobal(g_Buffer.Length);
        Marshal.Copy(g_Buffer, 0, ptr, g_Buffer.Length);

        Img = new Bitmap(g_Width, g_Height, g_Width * 3, PixelFormat.Format24bppRgb, ptr);

        Img.Save("test.bmp", ImageFormat.Bmp);  //still issue persists

        // Call unmanaged code
        Marshal.FreeHGlobal(ptr);
    }
    catch (Exception ex)
    {
        Trace.WriteLine("Exception at Worker_DoWork: " + ex.Message);
    }
}

Edit 4: Working Code

Please find the modified code.,

In call back,

 Byte[] Buffer = new Byte[lBufferLen];
 Marshal.Copy((IntPtr)InputBuff, Buffer, 0, lBufferLen);

 Worker = new BackgroundWorker();
 Worker.DoWork += (obj, e) => Worker_DoWork(obj, e, Buffer);
 Worker.RunWorkerAsync();

And the worker,

void Worker_DoWork(object sender, DoWorkEventArgs e, Byte[] Buffer)
{
    try
    {
        Image Img = null;
        IntPtr ptr = Marshal.AllocHGlobal(Buffer.Length);

        Marshal.Copy(Buffer, 0, ptr, Buffer.Length);

        if (ptr != IntPtr.Zero)
        {
            Img = new Bitmap(g_Width, g_Height, g_Width * 3, PixelFormat.Format24bppRgb, ptr);
        }

        Img.Save("test.bmp", ImageFormat.Bmp);

        Marshal.FreeHGlobal(ptr);
    }
    catch (Exception ex)
    {
        Trace.WriteLine("Exception at Worker_DoWork: " + ex.Message);
    }
}

Solution

  • Thank you @Vasek. As suggested in the comment, I had to create a local buffer and pass it to the worker.

    Please find the working code in the edited question.