Using ABCpdf in a Windows Service

April 16, 2012

One of my long-term projects is a web application which can generate PDF documents on the fly, but bigger PDFs can also be generated offline using a Windows service. Both the web application and the service use the same code base to invoke the ABCpdf library.

Recently, an update of Server 2008 to IE9 forced me to switch to ABCpdf’s Gecko engine, and this switch caused the service, which runs under the Local System account, to fail unexpectedly.

The Event Log contained various error entries:

Source ABCpdf

The following information was included with the event:
Failed to add HTML: Page load timed out.

Source Application Error

Faulting application MyService.exe, version 1.0.0.0, time stamp 0x4f883a56,
faulting module KERNEL32.dll, version 6.0.6002.18005, time stamp 0x49e037dd,
exception code 0xe0434f4d, fault offset 0x0003fbae, process id 0x%9, application start time 0x%10.

Source ASP.Net 2.0.50727.0

An unhandled exception occurred and the process was terminated.

After adding some logging, the following exceptions were reported

Exception: System.Runtime.Serialization.SerializationException

Message: Type ‘WebSupergoo.ABCpdf8.Internal.Spawntaneous.SpawntaneousException’ in Assembly ‘ABCpdf, Version=8.1.0.1, Culture=neutral, PublicKeyToken=a7a0b3f5184f2169’ is not marked as serializable.

StackTrace:    at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter)
at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter)
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
at System.Runtime.Remoting.Channels.CrossAppDomainSerializer.SerializeObject(Object obj, MemoryStream stm)
at System.AppDomain.Serialize(Object o)
at System.AppDomain.MarshalObject(Object o)

WebSupergoo.ABCpdf8.Internal.PDFException: Failed to add HTML: Page load timed out.

   at WebSupergoo.ABCpdf8.Internal.Gecko.GeckoCallback.MyCallback(IntPtr closure, IntPtr serializedOpts, UInt32 serializedOptsLen, Double widthInMm, Double heightInMm, String url, UInt32& outNumCommands, IntPtr& outSerializedData, UInt32& outSerializedDataLen)
at WebSupergoo.ABCpdf8.Internal.NDoc.N.AddImageUrlGecko_32(IntPtr doc, String url, Int32 width, IntPtr callback, String& outErr)
at WebSupergoo.ABCpdf8.Internal.Gecko.DocAddGecko.AddUrl(Doc doc, String url, Boolean paged, Int32 width, String& err)
at WebSupergoo.ABCpdf8.Internal.Gecko.DocAddGecko.AddHtml(Doc doc, String html, Boolean paged, Int32 width, String& err)
at WebSupergoo.ABCpdf8.Doc.AddImageHtml(String html, Boolean paged, Int32 width, Boolean disableCache)
at WebSupergoo.ABCpdf8.Doc.AddImageHtml(String html)

System.TypeInitializationException: The type initializer for ‘WebSupergoo.ABCpdf8.Internal.Gecko.GeckoCallback’ threw an exception. —> WebSupergoo.ABCpdf8.Internal.Spawntaneous.SpawntaneousException: Failed to generate worker process.

   at WebSupergoo.ABCpdf8.Internal.Spawntaneous.ProcessGenerator.GenerateLocalInvoker(MethodInfo method, String filename, Platform platform, TargetFramework framework)
at WebSupergoo.ABCpdf8.Internal.Spawntaneous.WorkerProcessExe.Init(String filename, Platform platform, TargetFramework framework)
at WebSupergoo.ABCpdf8.Internal.Spawntaneous.WorkerProcessExe..ctor(String filename, Platform platform, TargetFramework framework)
at WebSupergoo.ABCpdf8.Internal.Gecko.GeckoCallback..cctor()
— End of inner exception stack trace —
at WebSupergoo.ABCpdf8.Internal.Gecko.DocAddGecko.AddUrl(Doc doc, String url, Boolean paged, Int32 width, String& err)
at WebSupergoo.ABCpdf8.Internal.Gecko.DocAddGecko.AddHtml(Doc doc, String html, Boolean paged, Int32 width, String& err)
at WebSupergoo.ABCpdf8.Doc.AddImageHtml(String html, Boolean paged, Int32 width, Boolean disableCache)
at WebSupergoo.ABCpdf8.Doc.AddImageHtml(String html)

As you can see, the last two exceptions were caused by calling AddImageHtml(), which is the entry point to the invocation of the Gecko rendering engine, therefore my reasoning was that the Gecko engine required something that the MSHTML rendering engine did not need.

I remembered that on IIS, the AppPool’s “Load User Profile” needs to be set, so probably it had something to do with the service running as Local System which does not have a user profile?

As it turns out, setting the service to run under a local user in Control Panel/Administration/Services fixed the problem, and the service was again able to generate PDF files.


Windows Update killed my ABCpdf

April 2, 2012

An application I develop (dev environment: Win7 Pro, IE9, ASP.Net 3.5) suddenly showed problems running on Windows Server 2008: the web application could not create PDF documents anymore, and

  • The browser only displayed a “Service Unavailable” message
  • The browser user was automatically logged out of the web application
  • The web request did not show in the IIS log
  • No error in the Application events

A process serving application pool ‘MyAppPool’ suffered a fatal communication error with the Windows Process Activation Service. The process id was ‘2688’. The data field contains the error number.

The details tab displayed the following information:

ProcessID 2688
6D000780

Binary data:

In Words

0000: 8007006D

In Bytes

0000: 6D 00 07 80

Teh Internets wanted me to debug IIS and my .Net application with IIS Diagnostics Toolkit, and Debug Diagnostic Tool, and IISState, and awe-inspiring how-to instructions.

Instead, I chose to add logging statements to the code that was affected by the error, and found that cause to be the statement

int theID = doc.AddImageHtml(html);

As it turns out (thank you SO, also here), you need to activate the Gecko engine in ABCpdf to work on (some) IE9 machines (as I said, the code works ok on Win7Pro, and I had recently updated the server):

doc.HtmlOptions.Engine = EngineType.Gecko;

Trying out the Gecko engine, the next result I got was

WebSupergoo.ABCpdf8.Internal.PDFException: ABCpdf cannot detect any printers. Gecko Engine requires a printer installed in the system. Usually, XPS Document Writer would suffice. Try also running the service as an interactive user. —> System.ComponentModel.Win32Exception: The RPC server is unavailable
at System.Drawing.Printing.PrinterSettings.get_InstalledPrinters()
at WebSupergoo.ABCpdf8.Internal.Gecko.DocAddGecko.get_DefaultPrinter()
— End of inner exception stack trace —
at WebSupergoo.ABCpdf8.Internal.Gecko.DocAddGecko.get_DefaultPrinter()
at WebSupergoo.ABCpdf8.Internal.Gecko.DocAddGecko.Preflight(Doc doc)
at WebSupergoo.ABCpdf8.Internal.Gecko.DocAddGecko.AddHtml(Doc doc, String html, Boolean paged, Int32 width, String& err)
at WebSupergoo.ABCpdf8.Doc.AddImageHtml(String html, Boolean paged, Int32 width, Boolean disableCache)
at WebSupergoo.ABCpdf8.Doc.AddImageHtml(String html)

After creating an XPS printer in control panel, the web application was finally able to generate PDF documents again.

According to the ABCpdf support page (6.29), the MSHTML rendering engine should be avoided for future applications:

Unfortunately with the official release of IE9 Microsoft released new documentation which says the IHTMLElementRender::DrawToDC function that was required has been deprecated. This is especially unfortunate given that there is no replacement for this function.

Given that Microsoft appears to be unwilling to support these interfaces we would strongly recommend that on new deployments you consider a move to the new Gecko-based HTML engine available in ABCpdf 8.


Test if Directory exists in Batch file (.cmd)

February 16, 2012

My previous post on testing network drives led me to further research the topic, and I came to quite surprising (at least for me) results: the result if a check with IF EXIST depend on whether

  • the drive is a local drive or a mapped network drive or a UNC path
  • the path contains spaces or not
  • the path is quoted or not
  • cmd runs in administrator mode or user mode

I wrote a small batch file that contains a couple of assignments of the form

set dir=c:\temp
set dir=c:\temp\with spaces
etc.

and executed these tests on each value

if exist %dir% echo exists
if exist %dir%\nul echo exists
if exist %dir%\. echo exists
if exist "%dir%" echo exists
if exist "%dir%\nul" echo exists
if exist "%dir%\." echo exists

These are the results

directory %dir% %dir%\nul %dir\. “%dir%” “%dir%\nul” “%dir%\.”
local x x x x x
local (spaces) x x
mapped (non-admin) x x x x x
mapped (non-admin, spaces) x x
UNC x x x x x x
UNC (spaces) x x x

Comments:

Testing directory path containing spaces can only be performed using the quoted notation.

Mapped network drives can only be access in non-administrator mode (see these threads).

The only reliable way to test for directory existence is therefore to use the quoted “%dir%\.” notation.

To check whether cmd runs in administrator mode or not, use an admin statement such as ‘at’:

at >nul 2>nul
if errorlevel 1 echo you are not in administrator mode

Test if Network Directory exists in Batch file (.cmd)

February 16, 2012

The default way to check whether a directory exists in a Windows batch file (.cmd) is

if not exist "%directory%\nul" (
   echo %directory% does not exist
)

However, as this MS KB explains, the check for the NUL file does not work with directories on network drives mapped to a drive letter.

A working solution I found is to process the ERRORLEVEL value a DIR command sets

dir %directory% >nul 2>nul
if errorlevel 1 (
   echo %directory does not exist
)

or

dir %directory% >nul 2>nul
if not errorlevel 1 (
    echo %directory exists
)

Also note that mapped network drives are not available in administrator mode, as is discussed in these threads.


New (empty) Window in IE9

January 25, 2012

If you press Ctrl-N in Internet Explorer (e.g. IE9), it opens a new window with the same web page. So how do you open a new empty window?

This thread lists 3 methods to open a new IE window from the taskbar:

  • Right-click an IE icon, and select “Internet Explorer”
  • Shift + Left-click on an IE icon
  • Middle-click on an IE icon

Of course, you can also select Internet Explorer from the Program Menu to get a new window.


xcopy to c:\inetpub\wwwroot fails with “Access denied”

January 25, 2012

Rather than manually copying files from \\tsclient\some\path to c:\inetpub\wwwroot\webdir I wanted to write a small batch file using xcopy and /exclude to deploy web application files.

However, even when starting the .cmd from a command line in administrator mode, I received an “Access denied” message for each file to be copied due to User Account Control prohibiting write access.

As it turns out, the task can be successfully performed using robocopy with the /zb (or /b ?) switch. (Use robocopy /? to find the huge collection of switches.)

Consequently, the feature known as xcopy deployment should therefore be renamed to robocopy deployment 😉


Extracting .msi Files without Installation

September 19, 2011

Occasionally you need to access the contents of an .msi file, but you are sitting on a PC where you do not administrative rights to run the installer.

I noticed 7-Zip adds itself to the context menu of .msi files, but it extracts the whole contents of the msi, and the output is somewhat confusing to me (admittedly, I do not use the latest version of 7-Zip).

Fortunately I came across lessmsi which opens an .msi file, allows you to select the desired files, and extracts them into the file’s sub-directory as found in the Directory column of each of the contained files.

Simple and effective!