Help: How to set duplex printing

Just starting out? Need help? Post your questions and find answers here.
TerryHough
Enthusiast
Enthusiast
Posts: 781
Joined: Fri Apr 25, 2003 6:51 pm
Location: NC, USA
Contact:

Help: How to set duplex printing

Post by TerryHough »

I'm trying to change the printer to duplex mode in mid print job.
Testing on a Brother HL-5170DLT laser printer with duplex capability and
two input trays.

The following code is a general test of:
1) Selecting the printer and starting a print job
2) Print a page from the upper tray in portrait mode
3) Print a page from the lower tray in landscape mode
4) Print two pages in duplex mode from the upper tray in portrait mode

Everything works correctly except the duplexing.

Can anyone point out what I am doing wrong?

Code: Select all

See working code later in the thread
If you have a multi-tray printer with duplexing capability, I would greatly appreciate you giving the above PB4 code a try and give me a report of your results.

TIA,
Terry
Last edited by TerryHough on Thu Nov 09, 2006 11:03 pm, edited 2 times in total.
ABBKlaus
Addict
Addict
Posts: 1143
Joined: Sat Apr 10, 2004 1:20 pm
Location: Germany

Post by ABBKlaus »

Hi Terry,

works fine here on a Kyocera FS4000DN

Greetings

Klaus
TerryHough
Enthusiast
Enthusiast
Posts: 781
Joined: Fri Apr 25, 2003 6:51 pm
Location: NC, USA
Contact:

Post by TerryHough »

Hi Klaus

Thanks for your reply.

If it works for your printer, then I must be on the right path anyhow. Just can't get the duplexing going on my two Brother machines. Guess I will have to dig deeper.

Terry
User avatar
GeoTrail
Addict
Addict
Posts: 2794
Joined: Fri Feb 13, 2004 12:45 am
Location: Bergen, Norway
Contact:

Post by GeoTrail »

Did work correctly here. I have a Canon PIXMA MP500 that support duplex.
Got this message
Printing cannot be continued with the specified print settings.

Click Cancel Printing to stop printing.

Job: PureBasic PrintRequesterEX Test
It did print out the following:
Which way did this print? Should be Portrait from Upper Tray
I Stepped On A Cornflake!!! Now I'm A Cereal Killer!
ABBKlaus
Addict
Addict
Posts: 1143
Joined: Sat Apr 10, 2004 1:20 pm
Location: Germany

Post by ABBKlaus »

Terry, could you try saving the DEVMODE structure to file when you setup your printer to print in duplex mode :idea:

An example made with my printerlib :

Code: Select all

If Print_OpenPrinter()
  Print_SaveDEVMODE("DEVMODE.BIN")
EndIf
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Post by Shardik »

Terry, on a Lexmark C920 the change between landscape and portrait mode is successful but the duplex print fails. For a quick solution I have adapted the very short and simple code from Andreas from the German forum (example for printing on the default printer in landscape mode, http://www.purebasic.fr/german/archive/ ... c.php?t=83) to produce a duplex print:

Code: Select all

DefaultPrinterName.S = Space(260)
PrinterDC.L
PrinterHandle.L

GetPrivateProfileString_("WINDOWS","DEVICE","", @DefaultPrinterName, 260, "Win.Ini")
DefaultPrinterName = StringField(DefaultPrinterName, 1, ",")

OpenPrinter_(@DefaultPrinterName, PrinterHandle, 0)

Dim DevIn.DEVMODE(0)
Dim DevOut.DEVMODE(0)

DocumentProperties_(0, Printerhandle, @DefaultPrinterName, DevIn(0), DevOut(0), #DM_OUT_BUFFER | #DM_IN_BUFFER)
ClosePrinter_(PrinterHandle)

DevIn(0)\dmDuplex = #DMDUP_VERTICAL
PrinterDC = CreateDC_("WINSPOOL", @DefaultPrinterName, 0, DevIn(0))

DocInfo.DOCINFO
DocInfo\cbSize = SizeOf(DOCINFO)
DocInfo\lpszDocName = @"Duplex Print"
DocInfo\lpszOutput = #Null

If StartDoc_(PrinterDC,@DocInfo) > 0
  If StartPage_(PrinterDC) > 0
    TextOut_(PrinterDC, 60, 70, "Front page", 10)
    EndPage_(PrinterDC)
    TextOut_(PrinterDC, 60, 70, "Back page", 10)
    EndPage_(PrinterDC)
    EndDoc_(PrinterDC)
  EndIf
EndIf
ABBKlaus
Addict
Addict
Posts: 1143
Joined: Sat Apr 10, 2004 1:20 pm
Location: Germany

Post by ABBKlaus »

@Shardik :

in general you are right but :
http://msdn.microsoft.com/library/en-us ... l_7k1f.asp
Note The DEVMODE structure actually used by a printer driver contains the device-independent part (as defined above) followed by a driver-specific part that varies in size and content with each driver and driver version. Because of this driver dependence, it is very important for applications to query the driver for the correct size of the DEVMODE structure before allocating a buffer for it.
and you forgot to set the dmfields member :wink:

Code: Select all

DefaultPrinterName.S = Space(260) 
PrinterDC.L 
PrinterHandle.L 

GetPrivateProfileString_("WINDOWS","DEVICE","", @DefaultPrinterName, 260, "Win.Ini") 
DefaultPrinterName = StringField(DefaultPrinterName, 1, ",") 

OpenPrinter_(@DefaultPrinterName, PrinterHandle, 0) 

Buffersize=DocumentProperties_(0, Printerhandle, @DefaultPrinterName, 0, 0, 0) 
*Ptr_DEVMODE=AllocateMemory(Buffersize) 
DocumentProperties_(0, Printerhandle, @DefaultPrinterName, *Ptr_DEVMODE , 0, #DM_OUT_BUFFER) 

ClosePrinter_(PrinterHandle) 

*New_DEVMODE.DEVMODE=*Ptr_DEVMODE
*New_DEVMODE\dmFields|#DM_DUPLEX
*New_DEVMODE\dmDuplex = #DMDUP_VERTICAL

PrinterDC = CreateDC_("WINSPOOL", @DefaultPrinterName, 0, *Ptr_DEVMODE) 

DocInfo.DOCINFO 
DocInfo\cbSize = SizeOf(DOCINFO) 
DocInfo\lpszDocName = @"Duplex Print" 
DocInfo\lpszOutput = #Null 

If StartDoc_(PrinterDC,DocInfo) > 0 
  If StartPage_(PrinterDC) > 0 
    TextOut_(PrinterDC, 60, 70, "Front page", 10) 
    EndPage_(PrinterDC)
    TextOut_(PrinterDC, 60, 70, "Back page", 9) 
    EndPage_(PrinterDC) 
    EndDoc_(PrinterDC) 
  EndIf 
EndIf 
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Post by Shardik »

Klaus, you are right. Of course I know this Microsoft quote too. But I only wanted to deliver a quick and easy solution. You are also right regarding my omission of dmField. Therefore I was surprised myself that my "minimalistic" example was running flawless even without dmField :wink:

But here follows a more "proper" solution which works on my Lexmark C920 and should work on other duplex capable printers too:

Code: Select all

Procedure.L ChangePrinterSettings(PrinterName.S, DuplexMode.L = #DMDUP_SIMPLEX, PageOrientation.L = #DMORIENT_PORTRAIT, Tray.L = #DMBIN_AUTO)
  BufferSize.L
  BytesRetrieved.L
  *DEVMODEBuffer.DEVMODE
  *DEVMODEBufferCopy.DEVMODE
  ErrorMsg.S
  ModeFlag.L
  PRINTER_DEFAULTSStructure.PRINTER_DEFAULTS
  PrinterHandle.L
  Result.L

  ; ----- Get printer handle
  
  PRINTER_DEFAULTSStructure\DesiredAccess = #STANDARD_RIGHTS_REQUIRED | #PRINTER_ACCESS_ADMINISTER | #PRINTER_ACCESS_USE

  Result = OpenPrinter_(@PrinterName, @PrinterHandle, @PRINTER_DEFAULTSStructure)
  
  If Result = #False Or PrinterHandle = 0
    ErrorMsg = "Could not obtain printer handle!"
    Result = #False
    Goto CleanUp
  EndIf

  ; ----- Get buffer size for DEVMODE structure

  ModeFlag = 0

  BufferSize = DocumentProperties_(0, PrinterHandle, @PrinterName, 0, 0, ModeFlag)
  
  If BufferSize < 0 Or ModeFlag <> 0
    ErrorMsg = "The size of the DEVMODE structure couldn't be obtained!"
    Goto CleanUp
    End
  EndIf

  ; ----- Get memory buffer for DEVMODE structure
  
  *DEVMODEBuffer = AllocateMemory(BufferSize)
  
  If *DEVMODEBuffer = 0
    ErrorMsg = "A memory request for the DEVMODE structure failed!"
    Result = #False
    Goto CleanUp
  EndIf
  
  ; ----- Get memory buffer for copy of DEVMODE structure
  
  *DEVMODEBufferCopy = AllocateMemory(BufferSize)
  
  If *DEVMODEBufferCopy = 0
    ErrorMsg = "A memory request for a copy of the DEVMODE structure failed!"
    Result = #False
    Goto CleanUp
  EndIf

  ; ----- Generate DEVMODE structure
  
  Result = DocumentProperties_(0, PrinterHandle, @PrinterName, *DEVMODEBuffer, 0, #DM_OUT_BUFFER)
  
  If Result < 0
    ErrorMsg = "The request for the DEVMODE structure failed!"
    Result = #False
    Goto CleanUp
  EndIf

  ; ----- Copy DEVMODE structure

  CopyMemory(*DEVMODEBuffer, *DEVMODEBufferCopy, BufferSize)

  ; ----- Does driver allow to activate duplex setting?

  If *DEVMODEBufferCopy\dmFields & #DM_DUPLEX <> #DM_DUPLEX
    ErrorMsg = "The duplex setting cannot be activated for this printer!"
    Result = #False
    Goto CleanUp
  EndIf

  ; ----- Change tray in copy of DEVMODE structure

  *DEVMODEBufferCopy\dmOrientation = Tray

  ; ----- Change page orientation in copy of DEVMODE structure

  *DEVMODEBufferCopy\dmOrientation = PageOrientation

  ; ----- Change duplex setting in copy of DEVMODE structure

  *DEVMODEBufferCopy\dmDuplex = DuplexMode

  ; ----- Specify which parameters are to be changed

  *DEVMODEBufferCopy\dmFields = #DM_ORIENTATION | #DM_DUPLEX | #DM_DEFAULTSOURCE

  ; ----- Overwrite DEVMODE structure with modified copy

  CopyMemory(*DEVMODEBufferCopy, *DEVMODEBuffer, BufferSize)

  ; ----- Hand over the modified DEVMODE structure

  Result = DocumentProperties_(0, PrinterHandle, @PrinterName, *DEVMODEBuffer, *DEVMODEBuffer, #DM_IN_BUFFER | #DM_OUT_BUFFER)

  If Result <> #IDOK
    ErrorMsg = "The hand over of the DEVMODE structure failed!"
    Result = #False
  EndIf

; ----- Free printer handle and memory buffers

CleanUp:
  If PrinterHandle <> 0
    ClosePrinter_(PrinterHandle)
  EndIf

  If *DEVMODEBufferCopy <> 0
    FreeMemory(*DEVMODEBufferCopy)
  EndIf

  If Result = #False
    If *DEVMODEBuffer <> 0
      FreeMemory(*DEVMODEBuffer)
    EndIf
  
    MessageRequester("Error", ErrorMsg, #MB_ICONERROR)
    ProcedureReturn #False
  EndIf

  ProcedureReturn *DEVMODEBuffer
EndProcedure


DefaultPrinterName.S = Space(260)
DocInfo.DOCINFO
Driver.S
PrinterDC.L
Text.S

Select OSVersion() 
  Case #PB_OS_Windows_98, #PB_OS_Windows_ME 
    Driver = "" 
  Case #PB_OS_Windows_2000, #PB_OS_Windows_XP 
    Driver = "WINSPOOL" 
  Default 
    Driver = "WINSPOOL" 
EndSelect 

; Get name of default printer

GetPrivateProfileString_("WINDOWS","DEVICE","", @DefaultPrinterName, 260, "Win.Ini")
DefaultPrinterName = StringField(DefaultPrinterName, 1, ",")

*DEVMODEBuffer = ChangePrinterSettings(DefaultPrinterName, #DMDUP_SIMPLEX, #DMORIENT_PORTRAIT, #DMBIN_UPPER)

If *DEVMODEBuffer = 0
  End
EndIf

PrinterDC = CreateDC_(@Driver, @DefaultPrinterName, 0, *DEVMODEBuffer)

; Store the document title, optional file to store in 

DocInfo\cbSize = SizeOf(DOCINFO) 
DocInfo\lpszDocName = @"PureBASIC test print page 1" 
DocInfo\lpszOutput  = #Null  ; Or use a pointer to a null-terminated Filename.ext

Text = "Which way did this print?  Should be Portrait from Upper Tray." 

If StartDoc_(PrinterDC,@DocInfo) > 0 
  If StartPage_(PrinterDC) > 0 
    TextOut_(PrinterDC, 300, 100, Text, Len(Text)) 
    EndPage_(PrinterDC) 
  EndIf

  EndDoc_(PrinterDC) 
EndIf

FreeMemory(*DEVMODEBuffer)

If PrinterDC <> 0
  DeleteDC_(PrinterDC)
EndIf

; Set document properties To Landscape And Lower Tray 

*DEVMODEBuffer = ChangePrinterSettings(DefaultPrinterName, #DMDUP_VERTICAL, #DMORIENT_LANDSCAPE, #DMBIN_LOWER)

If *DEVMODEBuffer = 0
  End
EndIf

PrinterDC = CreateDC_(@Driver, @DefaultPrinterName, 0, *DEVMODEBuffer)

; Store the document title, optional file to store in 

DocInfo\cbSize = SizeOf(DOCINFO) 
DocInfo\lpszDocName = @"PureBASIC test print page 2" 
DocInfo\lpszOutput  = #Null  ; Or use a pointer to a null-terminated Filename.ext

Text = "And where did this one print?  Should be Landscape from Lower Tray"

If StartDoc_(PrinterDC,@DocInfo) > 0 
  If StartPage_(PrinterDC) > 0 
    TextOut_(PrinterDC, 300, 100, Text, Len(Text)) 
    EndPage_(PrinterDC) 
  EndIf

  Text = "And this should be on the back of the previous page (duplex)." 

  If StartPage_(PrinterDC) > 0 
    TextOut_(PrinterDC, 300, 100, Text, Len(Text)) 
    EndPage_(PrinterDC) 
  EndIf

  EndDoc_(PrinterDC) 
EndIf

FreeMemory(*DEVMODEBuffer)

If PrinterDC <> 0
  DeleteDC_(PrinterDC)
EndIf
Terry, in the Microsoft MSDN description for the DocumentProperties() function you find the following important guide lines you should consider when changing printer settings for a single printout:
http://msdn2.microsoft.com/en-us/library/ms535735.aspx
To make changes to print settings that are local to an application, an application should follow these steps:

1. Get the number of bytes required for the full DEVMODE structure by calling DocumentProperties and specifying zero in the fMode parameter.
2. Allocate memory for the full DEVMODE structure.
3. Get the current printer settings by calling DocumentProperties. Pass a pointer to the DEVMODE structure allocated in Step 2 as the pDevModeOutput parameter and specify the DM_OUT_BUFFER value.
4. Modify the appropriate members of the returned DEVMODE structure and indicate which members were changed by setting the corresponding bits in the dmFields member of the DEVMODE.
5. Call DocumentProperties and pass the modified DEVMODE structure back as both the pDevModeInput and pDevModeOutput parameters and specify both the DM_IN_BUFFER and DM_OUT_BUFFER values (which are combined using the OR operator).The DEVMODE structure returned by the third call to DocumentProperties can be used as an argument in a call to the CreateDC function.
Edit 1:
Tested successfully with WinNT 4 SP6 and Lexmark C920 (network printer) and with Win2K and Hitachi DDP70 (network printer), but duplex print fails on my Toshiba Satellite Pro Laptop, Win XP Pro SP2 and that same Lexmark C920 with which my PC under WinNT has no problems. Strange... Still testing this failure...

Edit 2:
I have found out that the Lexmark C920 driver on my Laptop in WinXP is not configured correctly so that the duplex unit in the driver is not activated. Should work if I succeed in telling the driver that the duplex unit is installed :wink:

Edit 3:
Duplex print works now with the activated duplex option in the driver like a charm even with WinXP :lol:
TerryHough
Enthusiast
Enthusiast
Posts: 781
Joined: Fri Apr 25, 2003 6:51 pm
Location: NC, USA
Contact:

Post by TerryHough »

First, thanks to both ABBKlaus and Shardik for help, code and suggestions.

@Shardik
Your last code fails to duplex here on WinXPPro SP2 and my Brother
HL-5170DNLT printer. Otherwise it prints properly. Isn't Windows
frustrating? And, before you ask, the driver is configured correctly. :)

By using Shardik's code as modified by ABBKlaus I was able to find my
solution. Since that code only used the default printer, I then made the
changes ABBKlaus and Shardik discussed in my own code and it now
works for the default or a selected printer.

Here is the kicker, and now that I have found the problem it probably
seems obvious. (At least on my printers) You can't change the duplexing
mode mid print job. You can change the orientation and paper source
inside the print job, but not the duplexing mode.

Thanks to both of you for your help and suggestions.

Here is my code modifed and working:

Code: Select all

; PrintRequesterEX - modified Nov 09, 2006
; Modified from code example by Siegfried Rings with help
; from Edwin Knoppert, Shardik, Xombie, Sparkie, and ABBKlaus

; The following code is a test of: 
;  1) Selecting the printer (or using the default printer) and starting a print job 
;  2) Print a page from the upper tray in portrait mode 
;  3) Print a page from the lower tray in landscape mode 
;  4) Print two pages in duplex mode from the upper tray in portrait mode 

#Window = 0
Pgm$="Printer mode testing" ; Title of this program's window

; Globals needed
Global *Ptr_DEVMODE.l
Global DocInf.DOCINFO
Global lpszDriver.s
Global PrintDlg.PRINTDLG
Global PrinterName$ = Space(260)
Global Msg.s

Procedure PrintRequesterEX(UseDefaultPrinter.l = 1)
  ; Determine the spooler name
  Select OSVersion()
    Case #PB_OS_Windows_98, #PB_OS_Windows_ME
    	lpszDriver.s = ""
    Case #PB_OS_Windows_2000, #PB_OS_Windows_XP
    	lpszDriver.s = "WINSPOOL"
    Default
    	lpszDriver = "WINSPOOL"
  EndSelect
  ; --------------------------
  ; Create the print dialog the way I want it to look, or create it
  ; and bypass the display when set to use the default printer.
	;
  ; Set the PRINTDLG structure to desired settings to modify the appearance of the Printer Dialog
  PrintDlg\lStructSize = SizeOf(PRINTDLG)
  PrintDlg\hwndOwner   = WindowID(#Window)
 	PrintDlg\hDevMode 	 = #Null
  PrintDlg\hDevNames   = #Null
 	PrintDlg\Flags   		 = #PD_RETURNDC | #PD_ALLPAGES | #PD_HIDEPRINTTOFILE |#PD_NOSELECTION
  PrintDlg\nFromPage   = 0
  PrintDlg\nToPage     = 0
  PrintDlg\nMinPage    = 1
  PrintDlg\nMaxPage    = 1
  PrintDlg\nCopies     = 1
  If UseDefaultPrinter
    PrintDlg\Flags     = PrintDlg\Flags | #PD_RETURNDEFAULT
  EndIf
  ; --------------------------
  
  ; Call the modified Printer Dialog --
  If PrintDlg_(PrintDlg)
    *DEVNAMES.DEVNAMES = GlobalLock_(PrintDlg\hDevNames)
    PrinterName$ = PeekS(*DEVNAMES + *DEVNAMES\wDeviceOffset)
    GlobalUnlock_(PrintDlg\hDevNames)
  	Static PrinterHandle.l = 0
  	OpenPrinter_(Printername$,@PrinterHandle.l,0)
		Buffersize.l = DocumentProperties_(0, Printerhandle, @PrinterName$, 0, 0, 0)
		*Ptr_DEVMODE = AllocateMemory(Buffersize)
		DocumentProperties_(0, Printerhandle, @PrinterName$, *Ptr_DEVMODE , 0, #DM_OUT_BUFFER)
  	ClosePrinter_(PrinterHandle)
    ProcedureReturn #True
  Else
   	Msg.s = "Printer selection result: "
  	If CommDlgExtendedError_() <> 0
    	Msg + "Error code: " + Str(CommDlgExtendedError_())
  	Else
    	Msg + "User cancelled"
  	EndIf 	
  	ProcedureReturn #False
  EndIf
  ; --------------------------
EndProcedure

hWnd = OpenWindow(#Window, 0, 0, 490, 420,Pgm$, #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
If hWnd
  CreateGadgetList(WindowID(0))
  
  result = PrintRequesterEX(#True)
  				 ; The default to use the Default Printer 
           ;   UseDefaultPrinter = absent or #TRUE = automatically select the default printer,
           ;                       #FALSE = modified print requester dialog is used.
  If result
  	; The printer was chosen, now make any modifications to the printer's defaults desired.
  	; Create a DEVMODE structure at the memory address returned by the printer selection.
		*New_DEVMODE.DEVMODE=*Ptr_DEVMODE
		; The structure dmFields member must have bit flags set for the fields to be modified, so set them.
		*New_DEVMODE\dmFields|#DM_ORIENTATION | #DM_DEFAULTSOURCE | #DM_DUPLEX 
		; Put the desired setup in the appropriate members as defined in dmFields
		*New_DevMode\dmOrientation   	= #DMORIENT_PORTRAIT	; Set to Portrait
		*New_DevMode\dmDefaultsource 	= #DMBIN_UPPER      	; Set to Upper Tray
		*New_DEVMODE\dmDuplex 				= #DMDUP_SIMPLEX			; Set to Single Sided

;     ;  Show some printer setup information -------------------------------------
;     		; Show printer information - useful while debugging
;         Info.s = "Printername =" + #TAB$ +PrinterName$							 				+ #LF$	
;         Info.s + "Copies      =" + #TAB$ +Str(*New_DEVMODE\dmCopies)  			+ #LF$
;         Info.s + "FromPage    =" + #TAB$ +Str(PrintDlg\nFromPage)    				+ #LF$
;         Info.s + "ToPage      =" + #TAB$ +Str(PrintDlg\nToPage)      				+ #LF$
;         Info.s + "Quality     =" + #TAB$ +Str(*New_DEVMODE\dmPrintQuality)	+ #LF$
;         Info.s + "Color       =" + #TAB$ +Str(*New_DEVMODE\dmColor)       	+ #LF$
;         Select *New_DEVMODE\dmOrientation
;           Case #DMORIENT_PORTRAIT
;           	Orientation$ = "Portrait"
;           Case #DMORIENT_LANDSCAPE
;           	Orientation$ = "Landscape"
;         EndSelect  	
;         Info.s + "Orientation =" + #TAB$ + Orientation$							 		+ #LF$
;         Select *New_DEVMODE\dmDefaultsource
;           Case #DMBIN_UPPER
;           	Source$ = "Upper Tray"
;           Case #DMBIN_LOWER
;           	Source$ = "Lower Tray"
;     		EndSelect      	
;         Info.s + "Papersource =" + #TAB$ + Source$											+ #LF$
;     		Select *New_DEVMODE\dmDuplex
;     			Case #DMDUP_SIMPLEX
;     				Duplex$ = "Single sided"
;     			Case #DMDUP_VERTICAL
;     				Duplex$ = "Front and back, long side"
;     			Case #DMDUP_HORIZONTAL
;     				Duplex$ = "Front and back, short side"
;     			Default
;     				Duplex$ = "Not available"
;     		EndSelect
;         Info.s + "Duplex mode =" + #TAB$ + Duplex$											+ #LF$
;         MessageRequester("Info",Info,#MB_ICONINFORMATION)
;         End
;     ;---------------------------------------------------------------------------
    
    If PrinterName$ > ""	; There is a valid printer selected.
      If PrinterDC.l  		; If the printer already opened, close it.  
        ClosePrinter_(PrinterDC)
        PrinterDC = 0
      EndIf

			; Create a printer device context with the desired DEVMODE settings
      PrinterDC = CreateDC_(@lpszDriver, PrinterName$, #Null, *Ptr_DEVMODE)  ; Returns 0 if it fails
      If PrinterDC	; The printer device context was created successfully.
        ; Create a DOCINFO struction to contain information about the document to be printed
        DocInf\cbSize = SizeOf(DOCINFO)
        DocInf\lpszDocName = @"Purebasic PrintRequesterEX Test"	; Store the document title
        DocInf\lpszOutput  = #Null  ; Optionally use a pointer to a null-terminated Filename.ext string
        														; to send the printout to a file
        														; Eg. DocInf\lpszOutput = @"C:\TEST.TXT"
        
        If StartDoc_(PrinterDC,@DocInf)	; The document was started successfully.
          If StartPage_(PrinterDC)  		; The page was created successfully.
            Text$ = "Which way did this print?  Should be Portrait from Upper Tray."
            ; Pass Col & Row (integers in DPI offset) to the TextOut API
            TextOut_(PrinterDC, 300, 100, Text$, Len(Text$))	; Returns 0 if fails
            EndPage_(PrinterDC)																; Returns 0 if fails
          EndIf   						

					; ResetDC_ cannot be done inside a StartPage_.  It will be ignored.  You can 
					; ResetDC_ following an EndPage_ and before the next StartPage_.
					; It should be handled this way for backwards compatibility with Win95, 98, ME.
					
          ; Set DEVMODE properties to Landscape and Lower Tray
					*New_DevMode\dmOrientation   	= #DMORIENT_LANDSCAPE	; Set to Landscape
					*New_DevMode\dmDefaultsource 	= #DMBIN_LOWER      	; Set to Lower Tray
          If ResetDC_(PrinterDC,*Ptr_DEVMODE) = PrinterDC			; Reset the printer's dc to new DEVMODE
 	          If StartPage_(PrinterDC)
   	          Text$ = "And where did this one print?  Should be Landscape from Lower Tray"
	            TextOut_(PrinterDC, 60, 300,Text$,Len(Text$))
  	          EndPage_(PrinterDC)
  	        EndIf  
	        Else
	   				MessageRequester(Pgm$ + "Printer Reset","ResetDC_ failed.",#MB_ICONERROR)
  	      EndIf
        	EndDoc_(PrinterDC)																	; Returns 0 if fails
      	EndIf
        
        ; The dmDuplex setting cannot be changed mid document.  So, lets start a new document
        ; after changing that setting.
        
				;	Set DEVMODE properties to Portrait from Upper Tray in Duplex mode.
				*New_DEVMODE.DEVMODE=*Ptr_DEVMODE
				*New_DEVMODE\dmFields|#DM_ORIENTATION | #DM_DEFAULTSOURCE | #DM_DUPLEX 
				*New_DevMode\dmOrientation   	= #DMORIENT_PORTRAIT	; Set to Portrait
				*New_DevMode\dmDefaultsource 	= #DMBIN_UPPER      	; Set to Upper Tray
				*New_DEVMODE\dmDuplex 				= #DMDUP_VERTICAL			; Set to Duplex, long edge
			  If ResetDC_(PrinterDC,*Ptr_DEVMODE) = PrinterDC			; Reset the printer's dc to new DEVMODE
	        If StartDoc_(PrinterDC,@DocInf)
  	        If StartPage_(PrinterDC)
 	  	        Text$ = "And where did this one print?  Should be Portrait from Upper Tray"
  	  	      TextOut_(PrinterDC, 60, 300,Text$,Len(Text$))
 	  	  	    EndPage_(PrinterDC)
 	  	    	EndIf  
          	If StartPage_(PrinterDC)
 	          	Text$ = "And this should be on the back of the previous page (duplex)."
  	        	TextOut_(PrinterDC, 60, 300,Text$,Len(Text$))
 	  	      	EndPage_(PrinterDC)
 	  	    	EndIf  
  	      	EndDoc_(PrinterDC)
 	  	  	EndIf
				Else
   				MessageRequester(Pgm$ + "Printer Reset","ResetDC_ failed.",#MB_ICONERROR)
				EndIf
        					
    	EndIf
    	DeleteDC_(PrinterDC)	; Delete the printer's dc
  	Else
  		; The printer's device context could not be created.  Windows may have already displayed a 
  		; message describing the reason, or the DEVMODE is incorrectly configured.  Windows does
  		; not directly return a failure code.
	    MessageRequester(Pgm$ + " Printer Open","The selected printer could not be opened for use.",#MB_ICONSTOP)
	  EndIf
 	Else
 		; The printer selection failed or was cancelled by the user.  The reason was passed back from
 		; the PrintRequesterEX procedure in the global Msg.s  
    MessageRequester(Pgm$ + " Printer Selection",Msg,#MB_ICONSTOP)
  EndIf
EndIf 	
End
ABBKlaus
Addict
Addict
Posts: 1143
Joined: Sat Apr 10, 2004 1:20 pm
Location: Germany

Post by ABBKlaus »

Sorry Terry,

i was aware of that limitation, but did not investigatet any further because your code worked on our printer :oops:

http://msdn.microsoft.com/library/en-us ... l_2isl.asp
Remarks
The system disables the ResetDC function between calls to the StartPage and EndPage functions. This means that you cannot change the device mode except at page boundaries. After calling EndPage, you can call ResetDC to change the device mode, if necessary. Note that a call to ResetDC resets all device context attributes back to default values.
TerryHough
Enthusiast
Enthusiast
Posts: 781
Joined: Fri Apr 25, 2003 6:51 pm
Location: NC, USA
Contact:

Post by TerryHough »

I had read that too. In fact, I had included a comment in my code
regarding not calling ResetDC_ between StartPage_ and EndPage_.

But I didn't, and still don't, read that to mean you can't reset the duplex
mode between StartDoc_ and EndDoc_. I haven't found that specifically
stated anywhere. Yet it seems to be true. Maybe somebody will
disprove that to me.

Anyhow, that really isn't a problem for me. I must be able to switch
print orientation and tray selection mid print job. But, I can't think of a
reason I would switch duplex modes, although if it were available I
would probably find a use for it. :lol:

I want to prevent the attempt to change the duplex mode if the printer
doesn't have the capability. Shardik's latest example has some code to
do that. When I ran it on a printer without duplex capability today, it
didn't catch it. I will take a better look at it tomorrow.

Thanks again.
Terry
ABBKlaus
Addict
Addict
Posts: 1143
Joined: Sat Apr 10, 2004 1:20 pm
Location: Germany

Post by ABBKlaus »

I want to prevent the attempt to change the duplex mode if the printer
doesn't have the capability. Shardik's latest example has some code to
do that. When I ran it on a printer without duplex capability today, it
didn't catch it. I will take a better look at it tomorrow.
I have a better solution for this :

Code: Select all

; Globals needed 
Global *Ptr_DEVMODE.l 
Global DocInf.DOCINFO 
Global lpszDriver.s 
Global PrintDlg.PRINTDLG 
Global PrinterName$ = Space(260) 
Global Msg.s 

Procedure PrintRequesterEX(UseDefaultPrinter.l = 1) 
  ; Determine the spooler name 
  Select OSVersion() 
    Case #PB_OS_Windows_98, #PB_OS_Windows_ME 
       lpszDriver.s = "" 
    Case #PB_OS_Windows_2000, #PB_OS_Windows_XP 
       lpszDriver.s = "WINSPOOL" 
    Default 
       lpszDriver = "WINSPOOL" 
  EndSelect 
  ; -------------------------- 
  ; Create the print dialog the way I want it to look, or create it 
  ; and bypass the display when set to use the default printer. 
   ; 
  ; Set the PRINTDLG structure to desired settings to modify the appearance of the Printer Dialog 
  PrintDlg\lStructSize = SizeOf(PRINTDLG) 
  PrintDlg\hwndOwner   = 0 ; WindowID(#Window) 
    PrintDlg\hDevMode     = #Null 
  PrintDlg\hDevNames   = #Null 
    PrintDlg\Flags          = #PD_RETURNDC | #PD_ALLPAGES | #PD_HIDEPRINTTOFILE |#PD_NOSELECTION 
  PrintDlg\nFromPage   = 0 
  PrintDlg\nToPage     = 0 
  PrintDlg\nMinPage    = 1 
  PrintDlg\nMaxPage    = 1 
  PrintDlg\nCopies     = 1 
  If UseDefaultPrinter 
    PrintDlg\Flags     = PrintDlg\Flags | #PD_RETURNDEFAULT 
  EndIf 
  ; -------------------------- 
  
  ; Call the modified Printer Dialog -- 
  If PrintDlg_(PrintDlg) 
    *DEVNAMES.DEVNAMES = GlobalLock_(PrintDlg\hDevNames) 
    PrinterName$ = PeekS(*DEVNAMES + *DEVNAMES\wDeviceOffset) 
    GlobalUnlock_(PrintDlg\hDevNames) 
     Static PrinterHandle.l = 0 
     OpenPrinter_(Printername$,@PrinterHandle.l,0) 
      Buffersize.l = DocumentProperties_(0, Printerhandle, @PrinterName$, 0, 0, 0) 
      *Ptr_DEVMODE = AllocateMemory(Buffersize) 
      DocumentProperties_(0, Printerhandle, @PrinterName$, *Ptr_DEVMODE , 0, #DM_OUT_BUFFER) 
     ClosePrinter_(PrinterHandle) 
    ProcedureReturn #True 
  Else 
      Msg.s = "Printer selection result: " 
     If CommDlgExtendedError_() <> 0 
       Msg + "Error code: " + Str(CommDlgExtendedError_()) 
     Else 
       Msg + "User cancelled" 
     EndIf     
     ProcedureReturn #False 
  EndIf 
  ; -------------------------- 
EndProcedure 

; Peeking for #DC_DUPLEX
; ABBKlaus on 10.11.2006 08:57
;
; Don´t trust its result :
; Kyocera FS4000DN (Duplex by design): #DC_DUPLEX = 1
; Kyocera FS1020D (Duplex by design): #DC_DUPLEX = 1
; Kyocera FS3800 (Duplex installed) : #DC_DUPLEX = 1
; Kyocera FS720KX (no Duplex) : #DC_DUPLEX = 0
; KONICA MINOLTA magicolor 2430DL (no Duplex) : #DC_DUPLEX = 1
; FreePDF XP (no Duplex) : #DC_DUPLEX = 0

If PrintRequesterEX(#False)
  
  Duplex=DeviceCapabilities_(@PrinterName$,0,#DC_DUPLEX,#Null,0)
  
  If Duplex=1
    MessageRequester("DeviceCapabilities_()","Printer is capable of printing in Duplex mode",0)
  Else
    MessageRequester("DeviceCapabilities_()","Printer is not capable of printing in Duplex mode",0)
  EndIf
EndIf
Regards Klaus
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Post by Shardik »

A short and straightforward bare bones solution for those who want to test the duplex capability of their default printer:

Code: Select all

Buffer.S = Space(260)
DefaultPrinterName.S
DefaultPortName.S

GetPrivateProfileString_("WINDOWS", "DEVICE", "", @Buffer, 260, "Win.Ini")

DefaultPrinterName = StringField(Buffer, 1, ",")
DefaultPortName = StringField(Buffer, 3, ",")

Select DeviceCapabilities_(@DefaultPrinterName, @DefaultPortName, #DC_DUPLEX, 0, 0)
  Case 1
    Debug "Printer supports duplex print"
  Case -1
    Debug "DeviceCapabilities() function failed!"
  Case 0
    Debug "Sorry! Printer doesn't support duplex print!"
EndSelect
TerryHough
Enthusiast
Enthusiast
Posts: 781
Joined: Fri Apr 25, 2003 6:51 pm
Location: NC, USA
Contact:

Post by TerryHough »

Hello ABBKlaus and Shardik.

Both of you beat me to it this morning. Been up a few hours earlier than
me here on the east coast of the US, right?

Klaus, I read the comment in your code about not trusting the result. In
fact, the first printer I ran it on said it had duplex capablilities and I
thought that was incorrect. (Same printer I ran Shardik's code on
yesterday and thought it failed.) When I dug a bit deeper, I learned
that the printer has a "manual" duplex mode; no duplex tray, but a
manual method where the user has to flip the paper over. So, the
answer was correct after all, and I learned something I didn't know
about the printer.

I've tested with six installed printers (local and network) here at the
office, and your code's response was correct in all cases. :)

Thanks again to both of you for your time and assistance,
Terry
Last edited by TerryHough on Fri Nov 10, 2006 5:14 pm, edited 1 time in total.
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Post by Shardik »

Both of you beat me to it this morning. Been up a few hours earlier than
me here on the east cost of the US, right?
Terry,
as one of my daughters currently visits a highschool in New Jersey I know that the New Jersey and New York time is six hours behind the current German time, so when I leave my work at 4 pm the time in New Jersey is at 10 am :wink:

I am very satisfied that my solution didn't fail on your printer because of your "manual duplex mode". Otherwise I wouldn't have had any explanation for it... :D
Post Reply