Adventures in Cross-Platform GUI Programming and Printing

Starting about 12 years ago, I developed and maintained an application related to one of my hobbies. This application designed and printed cards with hobby specific information on them. One of my original criteria for the application was that it run on at least Windows and Linux. Prior to printing, I wanted to display a custom print properties dialog. So my priorities for a programming language and GUI library were:

  1. Must be OS-agnostic; that is, it must run on a number of different OSes, including at least Microsoft Windows and Linux. OS/X would be a bonus, but I didn’t have access to a Mac, so I couldn’t test it anyway.
  2. Must support printing, including the generation of a custom print properties dialog box. To produce the dialog box, values of certain printer properties must be retrieved from the printer driver.

Attempt #1

At that time, as now, Java, with Swing as the GUI library, was popular so I developed the application on a computer running Windows XP. It turned out quite well if I do say so myself. I was very happy with the result.

So now it was time to make sure it worked on Linux. Oops, the program cannot retrieve printer properties. So, I posted a bug report with Java. The response was that Java worked properly and the problem was with the printer driver, so I should contact the person or company that wrote the printer driver. At that time, I owned an HP printer, and HP creates Linux drivers for their printers. So, I contacted HP. Their response was that the driver worked properly, and the problem was therefore with Java.

Stalemate! Neither Sun Microsystems nor HP were willing to budge from their positions and actually determine the cause of the problem.

Attempt #2

Finding this situation unacceptable, I looked for another language and GUI library to redo the program. C# and WPF looked promising. Mono stated that they would be porting WPF to other OSes. Great. Even though WPF was available only on Windows, there was the promise that it would be available at a later date on Linux and other OSes.

I gave away about $250 worth of Java programming books and bought books to learn C# and WPF. I rewrote the application using C# and WPF. The application worked great on my Windows XP system, so I published it to the Internet. There were the usual bugs that come with programs, but in general, all was well.

Then the killer problem occurred. For one user, the program threw an exception while attempting to print. Are you starting to see a pattern here? Debugging on his computer showed that the exception was thrown every time the program attempted to retrieve the list of available fonts from the registry. A number of developers had reported this problem to Microsoft, but there was no fix because Microsoft employees could not reproduce the problem.

Luckily for me, another person had determined that the problem occurred whenever there was an invalid character in a font’s filename as stored in the registry. That person had developed a short program that would remove the bad font entry from the registry. I modified the program and forwarded it to my user with statements that it would probably work, but that I would not be responsible for any system problems that running the program might cause. Anyway, my user ran the program, and my application now worked. Out of more than 1000 users of my program, three people eventually had the font filename problem, all of which the program fixed.

Now back to seeing if I can port to Linux. The port of WPF to other systems was shelved by Mono, so I was stuck with a Windows-only program.

Attempt #3

There are some C++ GUI libraries that are available for a number of operating systems. These include gtkmm, Qt, and wxWidgets. I will have more to say about GUI libraries in a future post.

I decided to look into each of these libraries. I finally chose gtkmm and started to redevelop my application in C++ with gtkmm.

That adventure lasted until I found I was writing code like:

   for(const std::vector<std::string>::iterator iter = vec.cbegin(); iter != vec.end(); ++iter) {}

This was before C++11 and the auto keyword. I was forever trying to determine what the return type for templated class methods were. Seemed just too hard to get anything done.

Attempt #4

Since I had become somewhat familiar with gktmm, the C++ wrapper around the C-based gtk+ library, I tried C# with gtk#. gtk# is a C# wrapper around gtk+. Since I had read a number of times that it was easier to develop an application on Linux and then port it to Windows than it was the other way around, I again rewrote my application, but this time on Linux. Everything worked great.

Then I tried it on Windows. I couldn’t retrieve printer related properties from the printer driver. What worked on Linux did not work on Windows. So, I started to debug gtk+. Buried deep within the gtk+ code is a comment that retrieving these properties was not supported on Windows. Well thanks a lot!

So on to attempt #5. In retrospect, I probably could have kludged together something that would work on Windows, but at the time, I was not into writing operating system, or more properly, GUI window manager specific code. And, it should not have been necessary for a supposedly multi-platform library.

Attempt #5

I looked back at attempt #2. By this time I had given up on having a cross-platform application, so I could continue to use WPF as needed. One of the features of WPF is that it can be used alongside WinForms, with either WPF controls inside WinForms controls, or WinForms controls inside WPF controls.

To fix the font retrieval problem, which is specific to WPF, I had to modify the program to build and display the card using WinForms, and therefore to print the cards using the WinForms printing functionality.

I finally had a program that would work, all be it on Windows only.

I continued to develop and maintain the program for about three years after I had lost interest in its related hobby.

Moral of This Story

The moral of this story is to think at least twice before attempting to develop cross-platform applications that have any special print requirements. And, in fact, be very careful with printing in general.