3

I've built a WinForms app which uses PDFium to print PDF documents. I installed PDFium from NuGet, and it created two subfolders in my project - x86 and x64 - as expected, each with the relevant version of pdfium.dll inside. My application's target platform is set to Any CPU.

When I run the application in debug on my Windows 10 64-bit machine it works perfectly. However, when I release the application and install it for real on the same computer, PDFium throws an exception:

System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)

After doing some research, I changed my application's target platform to x86 and removed the x64 subfolder from my project. The application now works perfectly after release, so the problem is solved.

However, I'd like to know if there's a way that I can get my application to work so that it supports both x86 and x64, using the appropriate version of PDFium for the target computer. It'd be nice if it'd install a 64-bit version on machines which can support it (which is most of them within our organisation, but I feel that I need to go with 32-bit if I have to choose one or the other, to ensure compatibility).

Philip Stratford
  • 4,513
  • 4
  • 45
  • 71
  • By looking at the library (at least the newer one), if you keep the directory structure it proposes (two subfolders, x86 and x64), it should load correctly the version needed when you call `PdfCommon.Initialize()` – xanatos Mar 27 '17 at 12:58
  • I'm not calling PdfCommon.Initialize(). I wonder if you're looking at a different library to me? I'm referring to this project https://github.com/pvginkel/PdfiumViewer. I see there's something with the same name by a similar name by Patagames which seems related but not the same. Or I could be totally confused. – Philip Stratford Mar 27 '17 at 13:18
  • I was looking at [this](https://www.nuget.org/packages/Pdfium.Net.SDK/). But even on the github, [in this lines](https://github.com/pvginkel/PdfiumViewer/blob/master/PdfiumViewer/NativeMethods.cs#L24) there is handling for x86/x64 that is exactly the code posted by rboe – xanatos Mar 27 '17 at 13:20
  • @xanatos Good point. And yet, it doesn't seem to work for me. Although, as I said in my original question, it works fine in debug, just not when I release it and install it for real. The Release and Debug configurations in my project are identical. I don't understand that! – Philip Stratford Mar 27 '17 at 13:25
  • 1
    Are you installing in a folder with non-ascii characters? Because whoever wrote that code should receive a crash course on Unicode... He is using the Ansi version of LoadLibrary instead of the Unicode version. – xanatos Mar 27 '17 at 13:28
  • 1
    @xanatos No, I'm installing it in a subfolder of Program Files with no non-ascii characters. However, your question put me onto the trail of fixing this as, whilst checking the path where the application was being installed I noticed that the pdfium.dll file was in the root of the installed application's folder, not an x64 subfolder. During install my script was overwriting the x64 version of pdfium.dll with the x32 version. – Philip Stratford Mar 27 '17 at 13:45

2 Answers2

8

I have used this approach. The main idea is to determine if the program is running on 32 or 64bit environments. This is done by checking the size of the pointer. Depending on the result of this check the library pdfium.dll is dynamically loaded from the x86 or the x64 subdirectory of the application path.

private static bool TryLoadNativeLibrary(string path)
{
    if (path == null)
        return false;

    path = Path.Combine(path, IntPtr.Size == 4 ? "x86" : "x64");

    path = Path.Combine(path, "pdfium.dll");

    return File.Exists(path) && LoadLibrary(path) != IntPtr.Zero;
}

[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
private static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
Ralf Bönning
  • 14,515
  • 5
  • 49
  • 67
  • It is important to note that the `TryLoadNativeLibrary` must be done at the first possible point in the program, to be sure that it hasn't been already loaded. – xanatos Mar 27 '17 at 12:45
  • This looks promising. I'd already done some research into this and I came across this answer - I wonder if it'd be a better way to check the host machine's environment? http://stackoverflow.com/a/24297692 – Philip Stratford Mar 27 '17 at 12:55
  • @PhilipStratford It is equivalent checking `IntPtr.Size` and `Is64BitProcess` Perhaps the second is a little faster... But we are speaking of differences between 0 and 0.0 :-) – xanatos Mar 27 '17 at 13:28
  • This works for `x86`, `x64`, `Any CPU`. Tested on Windows 10. You may need to [add some additional nuget packages](https://stackoverflow.com/a/67373337/591285) – clamchoda May 05 '21 at 20:31
1

This turned out not to be anything to do with PDFium. In my script used during installation of the released application, the two versions of pdfium.dll were both set to publish to the application's root folder, rather than subfolders named x32 and x64. As a result, during installation the x32 version of the dll was overwriting the x64 version, resulting in the application not having the x64 dll at all.

Philip Stratford
  • 4,513
  • 4
  • 45
  • 71