PDFNet for .Net and AnyCPU

In a previous post we announced the availability of PDFNet in Nuget, and also discussed how we made PDFNet work with the AnyCPU configuration.

Feedback for the AnyCPU setup has been great, but our users still had some technical questions on usage, that we would like to answer here.

Overview

Feel free to skip straight to the Breakdown section if you are in a rush, otherwise please read on.

One of the great advantages of PDFNet is that it is truly cross platform, and our users can easily add PDF support and related business logic to their work flows, and applications, across many different mobile and desktop environments, using essentially the same interface.

One complication of this though is that a native code library must be loaded and for this post we will be discussing .Net and Windows desktop.

For PDFTron to compile a native code library to be compatible with .Net, the Common Language Runtime (CLR), which allows us to create a native code assembly that is usable by C# and VB projects. However, CLR projects cannot be AnyCPU, an architecture needs to be selected (x64, x86, ARM, etc), and the assembly must dynamically link to the C++ Runtime, which means any CLR assembly depends on the VC++ Redistributables.

Furthermore, PDFNet for .Net comes in two versions, one for clients using .Net 2.0-3.5 and another for .Net 4+. These two versions are built using different Visual Studio toolsets, and therefore have different C++ runtime version dependencies; VC++ 2008 and VC++ 2010 respectively.

Breakdown

So with the above information, we have the following breakdown of Individual PDFNet for .Net assemblies.

Download Architecture .Net Framework VC++ Redistributable
PDFNet x86 2.0 – 3.5 VC++ 2008 Redistributable x86
PDFNet x64 2.0 – 3.5 VC++ 2008 Redistributable x64
PDFNetDotNet4 x86 4.0 + VC++ 2010 Redistributable x86
PDFNetDotNet4 x64 4.0 + VC++ 2010 Redistributable x64

VC++ Redistributable

None of the PDFNet downloads, including our Nuget package, include any of redistributable libraries. This is typically not noticeable on the developers computer, as these are included when Visual Studio is installed.

However, when it comes time to distribute your program to a server, or clients, machine, then you will probably need to provide the redistributable.

The links above for the VC++ Redistributables contain the MSVCP[90|100].dll and MSVCR[90|100].dll.

AnyCPU

So how does a .Net developer use PDFNet with the AnyCPU configuration, considering the above conditions?

Essentially, we need to delay the assembly loading until runtime, when it is known what the actual architecture is. To facilitate this we created the PDFNetLoader assembly. This is included in the download for PDFNet, and if look at the sample projects you will see they are all set to AnyCPU.

If you are interested, the source code for PDFNetLoader is available on our GitHub page.

Configuring an Existing Project

So how can you configure your existing project to use PDFNet and AnyCPU? First, download the PDFNet zip from our site.

PDFNet .Net 2.0 – 3.5
PDFNet .Net 4.0+

Next, let us assume your output dir (for example where your exe is created) is called ‘Bin’.

  1. Extract the contents of the PDFNet zip archive you downloaded above.
  2. Copy the ‘Lib’ folder in the PDFNet package to the same folder as your Visual Studio project.
  3. In your Visual Studio project select Properties and Build Events.
  4. In Build Events add the following text into the Post-build event command line window.
    xcopy $(ProjectDir)\Lib\PDFNet $(TargetDir)PDFNet /S /I /Y

    PostBuilEvent

  5. In your Visual Studio project add Lib/PDFNetLoader.dll as a reference.
  6. In your Visual Studio project add Lib/PDFNet/x86/PDFNet.dll as a reference.

    Note: Here we use the 32bit version so the project will work on 64 or 32bit OS.

  7. Right click the newly added PDFNet.dll (not PDFNetLoader.dll) in your Visual Studio project References, and select Properties.
  8. Change the “Copy Local” property to “False”

    Props

  9. Somewhere in your code, as early as possible, and before any PDFNet methods are called, add the following line.
    private static pdftron.PDFNetLoader loader = pdftron.PDFNetLoader.Instance();
  10. Finally, add a call to Initialize PDFNet somewhere in your code that would be called after the line above.
    pdftron.PDFNet.Initialize();
You are now ready to run your project in AnyCPU.

The project references to PDFNet and PDFNetLoader allow you to code normally. At runtime though, the following will happen.

  1. The PDFNetLoader.Instance() method is triggered, which registers itself as an event handler for AppDomain.AssemblyResolve.
  2. JIT encounters a method that uses PDFNet and tries to load PDFNet.dll, but because of the “local copy:false” this fails.
  3. PDFNetLoader gets the AssemblyResolve event, detects if system is 32 or 64 bit, and loads the corresponding PDFNet.dll.

Note, if you want to put the PDFNet assemblies somewhere else, you can, and call PDFNetLoader.Path to set the directory.

Distribution to Your Server or Client

The above helps developers build and test using AnyCPU, but how do you distribute the resulting program, especially considering you need the VC++ Redistributable as described earlier.

If you have an installer for your program, then certainly this would be a great time to detect the OS architecture (x86 or x64), and install the corresponding PDFNet and VC++ Redistributables for that type. In this case there would be no need for the PDFNetLoader.dll assembly.

There are two ways to “install” the VC++ Redistributables. Either you can include the dlls that Microsoft provides in the redistributable and put them with your program binary so they are available to load at runtime. See this Microsoft article for more on possible locations. Or you can run the VC++ Redistributable installer itself, though this would probably require your installer to have elevated rights.

We don’t recommended trying to support x86/x64 runtime loading on the client machine, because you also need to load the correct (x86 or x64) VC++ Redistributables, and this cannot be done using the PDFNetLoader.dll because they are unmanaged assemblies. You can of course install both 32 or 64bit versions of the VC++ Redistributable if you really want to support both architectures. Then the OS will take care of loading the correct one for you.

Can I use PDFNet only in a x86 or x64 Application?

Of course! For example the x86 version is able to run on either 32 or 64bit machines.

Simply add PDFNet.dll to your Visual Studio project references (along with the rest of your referenced assemblies), and make sure that copy local: true is set (the default), and you are ready to develop.

Note, if you just want to use 32bit PDFNet, for both 32 or 64 bit OS, you should know that PDFNet is built LargeAddressAware, but that this is not available by default in a .Net binary. If you execute the following line on your exe as part of your post build process

editbin /LARGEADDRESSAWARE 'your exe' 

then your 32bit process (and PDFNet) will be able to use up to 3GB of memory on a 32bit OS, and 4GB on a 64bit OS. This should be enough to open any PDF you encounter.

Distribution to Your Server or Client

This step is certainly simpler then what is described earlier, but of course you still need to make sure that the correct VC++ Redistributable is available or installed, see above for details.

Further Reading

We hope our .Net users find this useful, and as always you can ask questions in our Forum or on Stackoverflow.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s