System.NullReferenceException when loading GhostscriptRasterizer from memory on Server 2012 R2

Dec 16, 2013 at 6:17 PM
Edited Dec 16, 2013 at 6:20 PM
Hi,

First - thanks for the great library, it works very well on my win7 machine. I use it to convert PDFs to images, and the load from memory feature is particularly handy as it enables me to thread my application.

I've been trying to deploy this code onto a Windows Server 2012 R2 machine, but have been running into a problem when trying to load the GS library from memory.

To test - I've built the provided 'Ghostscript.NET.Samples' targeting x64 and .NET 4.5. The only thing that I changed in the whole project is the input/output path, and this line:
_rasterizer.Open(inputPdfPath, _lastInstalledVersion, true);
The built application works fine on a windows 7 x64 machine, with .NET 4.5 installed, but fails on a Server 2012 R2 x64 machine with the following stack trace:
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
   at Ghostscript.NET.Interpreter.GhostscriptInterpreter.Initialize() in c:\dev\Ghostscript.NET.v.1.1.2\Ghostscript.NET\Interpreter\GhostscriptInterpreter.cs:li
ne 152
   at Ghostscript.NET.Interpreter.GhostscriptInterpreter..ctor(GhostscriptVersionInfo version, Boolean fromMemory) in c:\dev\Ghostscript.NET.v.1.1.2\Ghostscript
.NET\Interpreter\GhostscriptInterpreter.cs:line 94
   at Ghostscript.NET.Viewer.GhostscriptViewer.Open(String path, GhostscriptVersionInfo versionInfo, Boolean dllFromMemory) in c:\dev\Ghostscript.NET.v.1.1.2\Ghostscript.NET\Viewer\GhostscriptViewer.cs:line 282
   at Ghostscript.NET.Rasterizer.GhostscriptRasterizer.Open(String path, GhostscriptVersionInfo versionInfo, Boolean dllFromMemory) in c:\dev\Ghostscript.NET.v.1.1.2\Ghostscript.NET\Rasterizer\GhostscriptRasterizer.cs:line 192
   at Ghostscript.NET.Samples.RasterizerSample.Start() in c:\dev\Ghostscript.NET.v.1.1.2\Ghostscript.NET.Samples\Samples\RasterizerSample.cs:line 67
   at Ghostscript.NET.Samples.Program.Main(String[] args) in c:\dev\Ghostscript.NET.v.1.1.2\Ghostscript.NET.Samples\Program.cs:line 55
If I change the above line to:
_rasterizer.Open(inputPdfPath, _lastInstalledVersion, false);
then everything works without any problems. However, then I can't thread the application.

I saw here that there might be an issue with .NET 4.5?

Any suggestions for a solution/workaround would be greatly appreciated.

I'm using ghostscript 9.10 and have tried the above with both versions 1.1.1 and 1.1.2 of Ghostscript.NET.

Many thanks,
Antony
Coordinator
Dec 28, 2013 at 3:26 PM
Hi Antony,

This sounds like DynamicNativeLibrary problem which is part of the Ghostscript.NET.

I will need to set up "Server 2012 R2" environment to debug this problem.

If you like and you can give me access to your 2012 R2 machine via TeamViewer and have VS installed up there so I can debug it directly on your machine, that would save me a lot of time. You can let me know via habjan@gmail.com.

Cheers,
Josip
Feb 5, 2014 at 12:09 PM
Edited Feb 5, 2014 at 12:10 PM
Hi Josip,

I've had a go at debugging the code, and you were right - there was an issue with DynamicNativeLibrary, but it looks like the issue isn't Server 2012 R2, but rather the memory size difference.

The code on the server was failing at this point:
GhostsciptInterpreter.Initialize()
int rc_ins = _gs.gsapi_new_instance(out _gs_instance, IntPtr.Zero);
_gs.gsapi_new_instance was set to null, so I had a look at where it gets initialized:
GhostscriptLibrary.Initialize()
this.gsapi_new_instance = _library.GetDelegateForFunction("gsapi_new_instance", typeof(gsapi_new_instance)) as gsapi_new_instance;
All the functions in the library were returning as null. Looking at GetDelegateForFunction in DynamicNativeLibrary, it uses the following method to set the addresses:
DynamicNativeLibrary.GetProcAddress(procName)
            ...
            // search function name in list of exported names
            nameRef = (uint*)(codeBase + exports->AddressOfNames);
            ordinal = (ushort*)(codeBase + exports->AddressOfNameOrdinals);

            for (i = 0; i < exports->NumberOfNames; i++, nameRef++, ordinal++)
            {
                IntPtr procNameHandle = (IntPtr)((byte*)((uint)codeBase + *nameRef));
                string testProcName = Marshal.PtrToStringAnsi(procNameHandle);

                if (testProcName == procName)
                {
                    idx = *ordinal;
                    break;
                }
            }
nameRef / ordinal were pointing to the right place, but the procNameHandle was always pointing to an inaccessible memory block. (uint)codeBase was not evaluating correctly, so I changed it to
IntPtr procNameHandle = (IntPtr)((byte*)((UInt64)codeBase + *nameRef));
And it works! So it looks like the codeBase pointer was being truncated.

If you think this is the right solution, would you be able to incorporate this into the next release please?

Many thanks,
Antony
Coordinator
Feb 5, 2014 at 6:12 PM
Hi Antony,

Thank you for your time on fixing this problem.

I included this fix in today's Ghostscript.NET v.1.1.3. release.

Cheers,
Josip