0

Build environment: Visual Studio 2019, Windows Forms Application, .net 4.7.2

I am attempting to use the QPDF .dll library to read, encrypt, and output a PDF file. See here for the DLL header file listing all exposed functions as declarations.

Notes:

  • QPDF_BOOL is a typedef for 'int'

    qpdf_r3_print_e is an integer enum so I considered it to be 'int' also.

I can successfully initialize the QPDF library, read the file, and output it to a new file by calling the requisite functions, but when I add a call to the qpdf_set_r6_encryption_parameters2() function after initializing and successfully reading the file, it throws an access violation. This is probably because the parameter types are incorrect, but I have perused the QPDF code to the best of my ability to use the correct types.

Here is the DllImport in my C# code: (I followed this advice for all the DllImport lines)

[DllImport("qpdf28.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        static extern void qpdf_set_r6_encryption_parameters2(
            IntPtr qpdf, 
            [MarshalAs(UnmanagedType.LPStr)] string user_password,
            [MarshalAs(UnmanagedType.LPStr)] string owner_password,
            int allow_accessibility,
            int allow_extract,
            int allow_assemble,
            int allow_annotate_and_form,
            int allow_form_filling,
            int allow_modify_other,
            int print,
            int encrypt_metadata

            );

Here is the function call later in the C# code:

qpdf_set_r6_encryption_parameters2(qpdf,
                    txtPassword.Text,
                    txtPassword.Text,
                    0, 0, 0, 0, 0, 0,
                     0,
                     0
                    );

I'm fairly certain the parameter data types are wrong somewhere, but I'm not seeing it. Thanks for any pointers!

UPDATE: Here is the full code which I'm attempting to run, in case this is helpful. Note that the code works perfectly if I disable the Encryption line: it writes a new PDF file containing the same information as the input PDF.

namespace testqpdf
{
    public partial class Form1 : Form
    {
        enum qpdf_r3_print_e
        {
            qpdf_r3p_full = 0,
            qpdf_r3p_low = 1,
            qpdf_r3p_none = 2
        }

        [DllImport("qpdf28.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        static extern IntPtr qpdf_init();

        [DllImport("qpdf28.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        static extern void qpdf_cleanup(ref IntPtr qpdfData);

        [DllImport("qpdf28.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        static extern int qpdf_read(IntPtr qpdfdata, [MarshalAs(UnmanagedType.LPStr)] string fileName, IntPtr password);

        [DllImport("qpdf28.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        static extern void qpdf_set_object_stream_mode(IntPtr qpdf, int mode);

        [DllImport("qpdf28.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        static extern void qpdf_set_qdf_mode(IntPtr qpdf, int value);

        [DllImport("qpdf28.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        static extern int qpdf_init_write(IntPtr qpdf, [MarshalAs(UnmanagedType.LPStr)] string fileName);

        [DllImport("qpdf28.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        static extern int qpdf_write(IntPtr qpdf);

        [DllImport("qpdf28.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        static extern void qpdf_set_r6_encryption_parameters2(
            IntPtr qpdf, 
            [MarshalAs(UnmanagedType.LPStr)] string user_password,
            [MarshalAs(UnmanagedType.LPStr)] string owner_password,
            int allow_accessibility,
            int allow_extract,
            int allow_assemble,
            int allow_annotate_and_form,
            int allow_form_filling,
            int allow_modify_other,
            int print,
            int encrypt_metadata

            );




        public Form1()
        {
            InitializeComponent();
        }

        private void btnGo_Click(object sender, EventArgs e)
        {
            IntPtr qpdf = qpdf_init();

            int result = qpdf_read(qpdf, txtInput.Text, /* password */ IntPtr.Zero);

            Console.WriteLine("Read PDF file " + txtInput.Text + ", result = " + result.ToString());

            if (chkEncrypt.Checked)
            {
                qpdf_set_r6_encryption_parameters2(qpdf,
                    null,
                    null,
                    0, 0, 0, 0, 0, 0,
                     0,
                     0

                    );
            }


            result = qpdf_init_write(qpdf, txtOutput.Text);

            Console.WriteLine("Write PDF file " + txtOutput.Text + ", result = " + result.ToString());

            result = qpdf_write(qpdf);

            qpdf_cleanup(ref qpdf);
        }
    }
}
Ryan Griggs
  • 2,457
  • 2
  • 35
  • 58
  • You should use a debugger to see where the access violation happens. It may be in your code or the DLL file itself. – kiner_shah Jan 20 '22 at 05:34
  • @kiner_shah the visual c# debugger points to the line of code in my app, but the error is going to be originating from the DLL. Can you point me in a specific direction to get more debug info from this DLL call? There doesn't appear to be a PDB file available for the qpdf DLL, so I'm not sure how to get more debug details. Thank you. – Ryan Griggs Jan 22 '22 at 01:11
  • Without debug information, it is not possible to see where inside the DLL the program crashes. What you can do is to check if the arguments passed to that function are all valid and not null. – kiner_shah Jan 22 '22 at 05:42
  • What are you passing as the first argument, the declaration you posted mentions `IntPtr` but when I see the source code, it expects argument of type `qpdf_data` (https://github.com/qpdf/qpdf/blob/370710657a7e7c771668107d1b6407fc350a2891/libqpdf/qpdf-c.cc#L703) which is actually a pointer to `_qpdf_data` (https://github.com/qpdf/qpdf/blob/370710657a7e7c771668107d1b6407fc350a2891/include/qpdf/qpdf-c.h#L149) which is a struct containing many members (https://github.com/qpdf/qpdf/blob/370710657a7e7c771668107d1b6407fc350a2891/libqpdf/qpdf-c.cc#L23). – kiner_shah Jan 22 '22 at 05:52
  • @kiner_shah I am passing the value returned by qpdf_init() function. It is an integer, and I assume it is a pointer to the _qpdf_data structure in memory, created within the DLL. I am able to successfully read the PDF using qpdf_read and output the object to a new PDF file using qpdf_init_write and qpdf_write. Please see above: I have added the full code for reference. Thanks – Ryan Griggs Jan 23 '22 at 05:37
  • From your code, I can see that you are not checking the return value of any function. You should check it and take appropriate action if return value indicates some kind of failure. – kiner_shah Jan 23 '22 at 07:15

1 Answers1

0

Try this:

[DllImport("qpdf29.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr qpdf_init();

[DllImport("qpdf29.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
static extern void qpdf_cleanup(ref IntPtr qpdfData);

[DllImport("qpdf29.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
static extern int qpdf_read(IntPtr qpdfdata, [MarshalAs(UnmanagedType.LPStr)] string fileName, IntPtr password);

[DllImport("qpdf29.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
static extern void qpdf_set_qdf_mode(IntPtr qpdf, int value);

[DllImport("qpdf29.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
static extern int qpdf_init_write(IntPtr qpdf, [MarshalAs(UnmanagedType.LPStr)] string fileName);

[DllImport("qpdf29.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
static extern int qpdf_write(IntPtr qpdf);


IntPtr pdfData = qpdf_init();
qpdf_read(pdfData, @"C:\source file.pdf", IntPtr.Zero);
qpdf_init_write(pdfData, @"C:destination file.pdf");
qpdf_set_qdf_mode(pdfData, 1);
qpdf_write(pdfData);
qpdf_cleanup(ref pdfData);
Xiaohuan ZHOU
  • 478
  • 5
  • 6
  • I appreciate your comment, however I think you missed my point. I can write to the PDF as long as the Encryption option is not enabled. But when I attempt to encrypt the file, the error appears. – Ryan Griggs Sep 18 '22 at 15:46