Foxhound is the better* Database Monitor for SQL Anywhere.
*better: More thorough, more relevant, more effective.
...more Alerts, more All Clears, more details, more control in your hands.


Breck Carter
Last modified: September 6, 1996
mail to: bcarter@bcarter.com



Switching NT Printers

How can I switch between several printers from within a PowerBuilder program running on Windows NT 3.51? This program runs in "batch mode" so the PrintSetup() dialog box can't be used.

If there is a way to "send this print job to a particular printer" I wish someone would tell me about it. The technique of choice seems to be "set the default Windows printer to the one you want and then quickly start your print job before some other task changes the default again."

Veterans of the mainframe era laugh at this kind of logic. That's because in their world it is no big deal to define which output goes to which printer, ahead of time, no questions asked. Sometimes it seems that vendors of PC software don't actually own any printers let alone produce reports.

Nevertheless, here's a technique that seems to work:

Once you've successfully called PrintOpen() you can call PrintDataWindow() and then PrintClose(). And you can repeat the logic above to open several different print jobs, each using a different printer, and then switch PrintDataWindow() calls back and forth among these jobs. In this way you can write an industrial strength batch printing program that keeps all your printers busy chewing up those trees.

So exactly how do you get and set the default printer from within a program? According to Microsoft, "In all versions of Windows, the appropriate way to get the default printer is to use GetProfileString, and the appropriate way to set the default printer is to use WriteProfileString. This works whether the default printer information is stored in the WIN.INI file or in the registry."

Here's what the external function declarations look like for Windows NT 3.51:

   function long GetProfileStringA &
      ( string as_section, string as_key, string as_default, &
        ref string as_value, long al_size ) library "KERNEL32"

   function long WriteProfileStringA &
      ( string as_section, string as_key, &
        ref string as_value ) library "KERNEL32"

Here's a sample of code that retrieves the current default printer definition:

   string ls_actual_definition
   ls_actual_definition = space ( 255 )
   GetProfileStringA &
      ( "windows", &
        "device", &
        ",,,", &
        ls_actual_definition, &
        len ( ls_actual_definition ) )

The code above has two uses: To make sure the default printer hasn't changed, and to find out what the printer definitions look like. To do the latter simply use Print Manager to pick different printers as the default and run a little PowerBuilder program that displays the value returned by GetProfileStringA().

Here's a sample of code that sets the current default printer:

   string ls_printer_definition
   ls_printer_definition = "\\OPS\HP_LJ4OPS,winspool,Ne01:"
   WriteProfileStringA &
      ( "windows", &
        "device", &
        ls_printer_definition )

Microsoft documentation suggests that the call to WriteProfileStringA() call should be followed with a broadcast "Windows INI file has changed" message to notify other applications that the printer has changed. Experimentation has shown this may not be necessary with Windows NT since Print Manager immediately displays the new default printer after another program calls WriteProfileStringA().

Nevertheless, here's what the PowerBuilder code would look like:

   send ( 65535, 26, 0, 0 ) // HWND_BROADCAST - WM_WININICHANGE

Another problem is that the window containing the PrintOpen() call doesn't give control back to the user until the print job is closed, if the printer is on a network. This can be dealt with by using a separate response window to call PrintOpen() and then executing this code wherever a print job is to be opened:

   long ll_print_job_number

   OpenWithParm ( w_hidden_print_open, "Print 1" )
   ll_print_job_number = message.DoubleParm

This response window never actually appears. All it does is call PrintOpen() from its open script and then closes itself:

   long   ll_print_job_number
   string ls_print_job_name

   ls_print_job_name = message.StringParm
   ll_print_job_number = PrintOpen ( ls_print_job_name )
   CloseWithReturn ( this, ll_print_job_number )


Breck Carter can be reached by phone at (416) 763-5200 or via email at bcarter@bcarter.com.