Monday, 4 July 2011

Another Bad Delphi Day

Some days I just hate Delphi. Other days I love it - the speed, the power. That can even get me past the ugliness of the Pascal syntax, the horrible unit structure, the disappointing upgrades and just why the h*ll has Borland/CodeGear/Embarcadero never added split screen editing when the whole structure of the interface/implementation sections just cries out for it.

Today was a bad day.

mvPDF runs as a service application. This presents its own problems - it’s virtually impossible to debug, for a start. So I have three versions: the regular version (service), the lite version (service) and a test desktop version where I can see what is going on.

Today, I’ve been testing out some changes to the printing functionality. mvPDF, as the name suggests, was designed to produce documents, and it does this very well. But you can also use mvPDF to generate prints directly through Windows, supporting all the features - the basic API, spooler capture, merge forms, reports, markup, PCL parser etc.

The initial problem is that Delphi can only render one real print job at a time. Its printing interface is nice to use, but very limited in terms of device control. And it uses a globally defined Printer object that causes all kinds of mayhem when you have two threads trying to print to different printers at the same time. Which is of course, not good for a reporting service.

So mvPDF uses a virtual printer class to generate the print jobs, then has a separate spooler thread that sends the jobs to the designated Windows printer. By this point, all the work of building the job has been done and so each print takes a few milliseconds to spool. It’s also safer - if a printer backs up too far (e.g. if your printer runs out of paper) you can safely queue a few hundred documents but after a time the Windows spooler just stops accepting prints.

All soak tested fine with the test (desktop) version of mvPDF, so on to testing the service. And hit another Delphi problem.

When you try to print from a service application on Windows 7, you get the nice error:

There is no default printer currently selected.

Now, there is a reason why there is no default printer - whilst users should always have a default printer, a Windows 7 service runs as Local System, which won’t. It is also completely irrelevant, since the code sets the destination printer explicitly. But Delphi still complains, and so the only solution is to have Delphi set a default printer on the way in.

Except you can’t. Because to do so you need to get the device details. And to get the device details you need to access a Delphi TPrinter. And as soon as you do that it crashes with the same no default printer error. Because whenever you set the printer index, whatever you set it to, it checks the current state of the printer and that gets the current printer index and that crashes with this error. Nice one.

There are get-arounds, of course. You can run mvPDF as anything other than Local System - which is fine - or I can use the Windows API to get the device information, and bypass Delphi entirely. Or fix Delphi’s printer unit so it doesn’t have this stupid check. One of which I’ll have to do. But it’s another couple of hours and another job to do.

1 comments:

Kevin Powick said...

Being a Delphi developer, you're probably familiar with it, but SmartInspect from Gurock Software can be invaluable for tricky things like debugging services.