[1] [2] [3] [4] [5]

Back to our regularly scheduled GUI programning:
what price Windows printing??

We’ve already discussed a little about the way the FRX stores printer information within its table. Most of the problems that people have with printing reports arise from inappropriately using the „Specific printer“ setting, or from designing reports using printers with characteristics that are significantly different from the printers in their applications’ runtime environment.  I’ve written, above, about ways to clear some of this information about the report, and how to doctor it (carefully) when necessary.

However, there are additional confusions related to the „default printer“ setting. Most importantly, there is a difference between the „Windows default“ printer and the VFP default printer.  Many people think the way to make their FoxPro application print properly, when their reports need certain printing characteristics, is to change the Windows default printer.  Not only is this unfair to other applications that may also be attempting to print, but it is also ineffective in many cases.

VFP output targets are determined by the printer you see chosen by the dialog in the File...Page Setup option of the VFP menu. This is what I refer to as „the VFP default printer“.  (You can set this VFP default printer to the actual Windows default by using the following syntax: SET PRINTER TO DEFAULT. You can also determine the current Windows default printer using SET(„PRINTER“,2), but you must bear in mind that the Windows default printer is not necessary VFP’s default, unless you have explicitly SET PRINTER TO DEFAULT.)

What do I mean by the „VFP default printer“? I mean the one that will be used by any FRX that uses „default printer“, for one thing, and also by any output such as LIST TO PRINT or SET PRINT ON. Obviously, this is a beast with whom we need to become intimately familiar!

The SYS(1037) function brings up the system printer attributes dialog, but it’s not as helpful as we’d like.

Unfortunately, the system menu bar that sets VFP’s default printer is not available under application control, and we don’t have a one-stop, simple programmatic replacement for it. We have a number of candidates, but nothing exactly the same and completely satisfactory.

We have the SET PRINTER TO NAME <Windows printer name> syntax, to set to any printer known to the system. We also have SYS(1037) to invoke the same dialog you get from the system menu bar (as you can see in the figure above). However, invoking the dialog programmatically doesn’t SET the VFP PRINTER TO the printer NAMEd in that dialog the way invoking in from the system menu bar does. What’s worse, SYS(1037) always returns a null string; you can’t find out what printer the user was actually „talking about“, and then SET PRINTER NAME TO it.

It’s far better to use SET PRINTER TO NAME (GETPRINTER()), as shown in the next figure. This dialog isn’t exactly the same as SYS(1037) but it has the very great advantage of returning a value properly and an almost-as-great advantage of not allowing you to set printer attributes that are far beyond the comprehension of the simple FRX format!

The simpler, and friendlier, GETPRINTER() dialog is a champion

You can also use the APRINTERS() function to get an array of available printers and create a custom dialog to show this information to the user.  Then you can use SET PRINTER TO NAME with the result.  Be warned, however; APRINTERS() can show you some printing systems, such as the Windows Rendering Subsystem, that shouldn’t really be available as a „printer choice“.  GETPRINTER() is somehow smart enough to ignore this type of driver.

I’ve created a container with some test sequences for you to investigate the vagaries of all this printing syntax.  You’ll see it in the next figure, dropped into a simple form named SETPRINT.SCX in your source code.  This form has no code in it, so if you want to see the code that makes it work, look at the demoSetVFPPrinter container class in DEV05.VCX).

You’ll notice that this code includes some investigation of PRTINFO( ), with which you can investigate the current „deep settings“ of the current VFP default printer or other printers. I put this in as a curiosity.  You can set most of this information from the SYS(1037) dialog; the challenge is always figuring out what to do with it after you have it.  I think the most you can do is to verify that the current settings are appropriate to some specific report requirements coming up or, if they are not, ask the user to fix them.

It is also possible to confuse PRTINFO( ), as you’ll find if you play with this dialog long enough. If you choose a laser printer and Landscape setting, for example, and afterwards choose a generic text printer (which cannot handle landscape), PRTINFO() is likely to tell you that an „unknown orientation“ has been set.  (Meanwhile, I think the printer driver will have comfortably gone into Portrait orientation without having been told.)

Play with SETPRINT.SCX, and the code in the container that drives it, to learn about the vagaries of SET PRINT and related syntax in VFP.

You’ll also notice that this container has code to give you access to the all-important current VFP default printer setting.  Since this information is arguably the most significant part of the class, I will reproduce the trick here:

*demoVFPSetPrinter.GetVFPPrinterName( )
LOCAL lcAlias, liSelect, liLine, lcLine, liMemoWidth, lcContents
liMemoWidth = SET(„MEMOWIDTH“)
liSelect = SELECT()
lcAlias = „C“+SYS(2015)
SELECT 0
SET MEMOWIDTH TO 1024
CREATE CURSOR (lcAlias) (onefield l)
CREATE REPORT (lcAlias) FROM (lcAlias)
USE IN (lcAlias)
USE (lcAlias+“.FRX“) ALIAS (lcAlias)
lcContents = Expr
USE IN (lcAlias)
ERASE (lcAlias+“.FRX“) NORECYCLE
ERASE (lcAlias+“.FRT“) NORECYCLE
liLine = ATCLINE(„DEVICE=“,lcContents)
IF EMPTY(liLine)
liLine = ATCLINE(„DEVICE =“,lcContents)
ENDIF 
lcLine = MLINE(lcContents,liLine)
SELECT (liSelect)
SET MEMOWIDTH TO liMemoWidth
RETURN ALLTR(SUBSTR(lcLine,AT(„=“,lcLine)+1))

As you can see, the trick is to CREATE a REPORT programmatically, which will store the current VFP printer default information in the FRX, where you can easily extract it and erase the temporary FRX you created.  Believe it or not, this code runs quickly.  It’s slowed down in the demo class, because it is immediately followed by all the PRTINFO() work every time it is called.  However, I’m calling it to refresh the appropriate label almost constantly in that class – much more frequently than you would ever need to –and still I don’t think you’ll see much of a slowdown.

As if all this syntax and all these choices weren’t enough, you can also use the MS Common Dialog OCX to get printer names, and use the result in a SET PRINTER command. You’ll find an example of the use of this dialog in the source code for my other session, in the RTF.SCX screen. The common dialog can be especially helpful if you are using Windows output mechanisms that require you to use the device context of the chosen printer.

We should realize that the VFP default printer is not one of the SETtings that is „private to a data session“.  This is the reason we have to work so hard to find out what its state is; in a modeless environment, we can’t ever be sure that some other component hasn’t SET this setting out from under us.

Here’s one final thought on this subject: if you really  need complete assurance of the SETting of this value, perhaps we need to create our print dialog as a DLL.  In that environment, the SETting we use can be protected from other changes that may occur in the Fox environment that is using the dialog.

[1] [2] [3] [4] [5]