Page 1 of 2

Posted: Thu Nov 09, 2006 11:02 pm
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

Posted: Fri Nov 10, 2006 12:27 am
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.

Posted: Fri Nov 10, 2006 3:49 am
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

Posted: Fri Nov 10, 2006 12:21 pm
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

Posted: Fri Nov 10, 2006 12:54 pm
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

Posted: Fri Nov 10, 2006 3:21 pm
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

Posted: Fri Nov 10, 2006 3:38 pm
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

Posted: Mon Jan 19, 2009 11:30 pm
by TerryHough
TerryHough wrote: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:
Needless to say, my prediction comes back to bite me.

Now I really need to be able to switch duplex modes mid print job and I still haven't found a way to do it.

Anybody have a suggestion?