Aborted status returned by ReleaseGdPictureImage

General discussions about GdPicture.NET.
Post Reply
accudave
Posts: 21
Joined: Tue May 12, 2015 6:07 pm

Aborted status returned by ReleaseGdPictureImage

Post by accudave » Mon Oct 03, 2016 10:02 pm

Is this a status code we should be treating as an error, or can it be ignored?

We have code to convert a PDF to a TIF that works great, but we noticed that this method is returning an Aborted status code when releasing the multi-page TIFF id. The concern here is a possible memory leak, and we are using GdPicture 13.0.37.

The issue was also present on GdPicture 13.0.36. I noticed the error after refactoring our code to use a helper class I wrote to ensure we throw an exception if the status code is not OK. It's usage is:

Code: Select all

using (var gdpicture = new GdPictureImaging())
using (var command = new GdPictureImagingCommand(gdpicture)
{
    try
    {
        command.Invoke(g => g.ReleaseGdPictureImageId(imageId);
    }
    catch (GdPictureException ex)
    {
        GdPictureStatus status = ex.Status; // Is GdPictureStatus.Aborted
    }
}
Here is the full code listing.

Code: Select all

public static void ConvertFileToTIF(string inFile, string outFile)
{
    string extension = Path.GetExtension(inFile);

    if (!File.Exists(inFile))
    {
        throw new FileNotFoundException("The file specified to be converted does not exist.", inFile);
    }

    if (!extension.Equals(".pdf", StringComparison.OrdinalIgnoreCase))
    {
        throw new ArgumentException("Converting a document to a TIF is only supported for a PDF document.");
    }

    using (var gdi = new GdPictureImaging())
    using (var gdiCommand = new GdPictureImagingCommand(gdi))
    using (var pdf = new GdPicturePDF())
    using (var pdfCommand = new GdPicturePDFCommand(pdf))
    {
        pdfCommand.Invoke(p => p.LoadFromFile(inFile, false));

        const int dpi         = 300;
        const int jpegQuality = 100;
        int pageCount         = pdfCommand.Invoke(p => p.GetPageCount());
        int tiffId            = 0;

        for (int i = 1; i <= pageCount; i++)
        {
            pdfCommand.Invoke(p => p.SelectPage(i));

            int pageId = pdfCommand.Invoke(p => p.RenderPageToGdPictureImageEx(dpi, true));

            int bitDepth = gdiCommand.Invoke(g => g.GetBitDepth(pageId));

            gdiCommand.Invoke(g => g.ColorDetection(pageId, true, false, true));

            if (i == 1)
            {
                tiffId = pageId;

                gdiCommand.Invoke(g => g.TiffSaveAsMultiPageFile(tiffId,
                                                                    outFile,
                                                                    bitDepth == 1 ? TiffCompression.TiffCompressionCCITT4
                                                                                : TiffCompression.TiffCompressionJPEG,
                                                                    jpegQuality));
            }
            else
            {
                gdiCommand.Invoke(g => g.TiffAddToMultiPageFile(tiffId, 
                                                                pageId,
                                                                bitDepth == 1 ? TiffCompression.TiffCompressionCCITT4
                                                                                : TiffCompression.TiffCompressionJPEG,
                                                                jpegQuality));
            }

            // Using GdPictureImagingCommand here causes an error because:
            // This is returning an Aborted status for some reason, but has no impact on the
            // conversion of the file. It may cause a memory leak, but will go out of scope
            // so hopefully the GC will pick it up.
            // 
            // Needs investigated or could be a GdPicture bug (v12.0.36/37).
            gdi.ReleaseGdPictureImage(pageId);
        }

        gdiCommand.Invoke(g => g.TiffCloseMultiPageFile(tiffId));
        gdiCommand.Invoke(g => g.ReleaseGdPictureImage(tiffId));
        pdfCommand.Invoke(p => p.CloseDocument());
    }

    if (!File.Exists(outFile))
    {
        throw new FileNotFoundException("The conversion encountered an error and did not produce an output file.",
                                        outFile);
    }
}

User avatar
Loïc
Site Admin
Posts: 5881
Joined: Tue Oct 17, 2006 10:48 pm
Location: France
Contact:

Re: Aborted status returned by ReleaseGdPictureImage

Post by Loïc » Tue Oct 04, 2016 2:57 pm

I don't think there is an error within GdPicture. Could you share a complete code snippet reproducing the issue?

My guess is you have to close the multipage tiff handle.

accudave
Posts: 21
Joined: Tue May 12, 2015 6:07 pm

Re: Aborted status returned by ReleaseGdPictureImage

Post by accudave » Tue Oct 04, 2016 3:26 pm

The second code snippet in my post above is the full method signature used to convert the file, and it seems to fail when releasing the multipage tiff id. The code is almost identical to the example https://www.gdpicture.com/guides/gdpicture/Co ... anism.html, and you can strip out the calls with the GdPictureImagingCommand and GdPicturePDFCommand objects and it stills returns an Aborted status. I also noticed yesterday afternoon that this is causing the file to still be in use by the process.

Here is the code for the command object. The PDF one is almost identical, they are just helper wrappers.

Code: Select all

public sealed class GdPictureImagingCommand : IDisposable
{
    private readonly GdPictureImaging imaging;
    private bool disposed;

    public GdPictureImagingCommand(GdPictureImaging imaging)
    {
        this.imaging = imaging;
    }

    public T Invoke<T>(Func<GdPictureImaging, T> command)
    {
        T value;

        try
        {
            value = command(imaging);
        }
        catch (Exception ex)
        {
            throw new GdPictureException(imaging.GetStat(),
                                            $"A unhandled exception was thrown by GdPicture with a status code of {imaging.GetStat()}. See the inner exception for more detailed information.",
                                            ex);
        }

        GdPictureStatus status = imaging.GetStat();

        switch (status)
        {
            case GdPictureStatus.OK: return value;
            default: throw new GdPictureException(imaging.GetStat(), $"Unexpected status code of {imaging.GetStat()} was encountered.");
        }
    }

    public void Invoke(Action<GdPictureImaging> command)
    {
        try
        {
            command.Invoke(imaging);
        }
        catch (Exception ex)
        {
            throw new GdPictureException(imaging.GetStat(),
                                            $"A unhandled exception was thrown by GdPicture with a status code of {imaging.GetStat()}. See the inner exception for more detailed information.",
                                            ex);
        }

        GdPictureStatus status = imaging.GetStat();

        switch (status)
        {
            case GdPictureStatus.OK: return;
            default: throw new GdPictureException(imaging.GetStat(), $"Unexpected status code of {imaging.GetStat()} was encountered.");
        }
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (disposed || !disposing)
            return;

        imaging.Dispose();

        disposed = true;
    }
}
Update
After revisiting the example linked above, I noticed a small probable flaw in the code:

Code: Select all

tiffId = pageId;

gdiCommand.Invoke(g => g.TiffSaveAsMultiPageFile(tiffId, ...
Which I have adjusted to match the example now:

Code: Select all

gdiCommand.Invoke(g => g.TiffSaveAsMultiPageFile(pageId, ...]
The problem still persists. I do want to also note that this is happening on the very first page it must release. I'm not sure if it happens on other pages because our code causes an exception to be thrown.
Last edited by accudave on Tue Oct 04, 2016 4:41 pm, edited 3 times in total.

User avatar
Loïc
Site Admin
Posts: 5881
Joined: Tue Oct 17, 2006 10:48 pm
Location: France
Contact:

Re: Aborted status returned by ReleaseGdPictureImage

Post by Loïc » Tue Oct 04, 2016 3:36 pm

Could you try to provide the full code please?

accudave
Posts: 21
Joined: Tue May 12, 2015 6:07 pm

Re: Aborted status returned by ReleaseGdPictureImage

Post by accudave » Tue Oct 04, 2016 4:07 pm

It is 30MB, how would you like me to send this to you?

User avatar
Loïc
Site Admin
Posts: 5881
Joined: Tue Oct 17, 2006 10:48 pm
Location: France
Contact:

Re: Aborted status returned by ReleaseGdPictureImage

Post by Loïc » Tue Oct 04, 2016 4:10 pm

I don't need your whole project, I need a code snippet, that I can use "as is", reproducing the error.

accudave
Posts: 21
Joined: Tue May 12, 2015 6:07 pm

Re: Aborted status returned by ReleaseGdPictureImage

Post by accudave » Tue Oct 04, 2016 4:17 pm

You will need to add the GdPicture references, but there is also an example PDF, though it seems to happen on any file.
Attachments
GdPicture.12.Example.zip
(285.93 KiB) Downloaded 374 times

User avatar
Loïc
Site Admin
Posts: 5881
Joined: Tue Oct 17, 2006 10:48 pm
Location: France
Contact:

Re: Aborted status returned by ReleaseGdPictureImage

Post by Loïc » Tue Oct 04, 2016 5:15 pm

Here the correction that you need to apply to your code:

Code: Select all

                for (int i = 1; i <= pageCount; i++)
                {
                    pdfCommand.Invoke(p => p.SelectPage(i));

                    var pageId      = pdfCommand.Invoke(p => p.RenderPageToGdPictureImageEx(dpi, true));
                    var bitDepth    = gdiCommand.Invoke(g => g.GetBitDepth(pageId));
                    var compression = bitDepth == 1 ? TiffCompression.TiffCompressionCCITT4
                                                    : TiffCompression.TiffCompressionJPEG;

                    gdiCommand.Invoke(g => g.ColorDetection(pageId, true, false, true));

                    if (i == 1)
                    {
                        gdiCommand.Invoke(g => g.TiffSaveAsMultiPageFile(pageId, outFile, compression, jpegQuality));
                        tiffId = pageId;
                    }
                    else
                    {
                        gdiCommand.Invoke(g => g.TiffAddToMultiPageFile(tiffId, pageId, compression, jpegQuality));
                        gdiCommand.Invoke(g => g.ReleaseGdPictureImage(pageId));
                    }                  
                }

accudave
Posts: 21
Joined: Tue May 12, 2015 6:07 pm

Re: Aborted status returned by ReleaseGdPictureImage

Post by accudave » Tue Oct 04, 2016 6:09 pm

This did resolve the issue and our api is working great now. I understand that I was releasing the image id of the multi-page tiff too early by having that call outside the else statement, but could you explain more about what TiffSafeAsMultiPageFile does internally that requires us to do tiffId = pageId?

Thank you for your help as always.

Post Reply

Who is online

Users browsing this forum: No registered users and 2 guests