Page 1 of 2

Printer: How to get the device name

Posted: Mon Aug 07, 2006 9:16 pm
by TerryHough
How can I retrieve the "device name" of the chosen printer after a
PrintRequester()
or
PrintDlg_().

I am lost trying to do this.

Terry

Posted: Mon Aug 07, 2006 9:51 pm
by Edwin Knoppert
I don't know the pb requester but once the PrintDlg() is invoked OK then you'll have pointer to a DEVMODE and a DEVNAMES structure.

By using the correct offsets you can obtain the drivers and device name.

Posted: Mon Aug 07, 2006 9:53 pm
by Edwin Knoppert
You're in trouble, the pb calls don't provide any way acc. the help..

Posted: Mon Aug 07, 2006 10:39 pm
by Xombie
I added a couple lines to one of the functions from my xPrint library ( http://www.purebasic.fr/english/viewtopic.php?t=22409 ) that illustrates what you're looking for...

Code: Select all

Procedure.l _xp_GetDefaultPrinterSettings(*HoldJob.s_xp_PrintJob)
   ; Fills the DEVMODE and PRINTDLG structures with information about the current default printer.
   Define.DEVNAMES myDevNames
   ;
   Define.l *HoldMode
   ;
   Define.l lResult, ResultDLG
   ;
   *HoldMode = AllocateMemory(SizeOf(DEVMODE)) 
   ;
   If *HoldMode
      ;
      *HoldJob\HoldDialog\lStructSize = SizeOf(PRINTDLG)
      *HoldJob\HoldDialog\flags = #PD_RETURNDC | #PD_RETURNDEFAULT
      *HoldJob\HoldDialog\hwndOwner = #Null
      *HoldJob\HoldDialog\hDevMode = *HoldMode
      *HoldJob\HoldDialog\hDevNames = #Null
      *HoldJob\HoldDialog\hInstance = 0
      *HoldJob\HoldDialog\lpfnSetupHook = #Null 
      *HoldJob\HoldDialog\lpPrintSetupTemplateName = #Null 
      *HoldJob\HoldDialog\lpPrintTemplateName = #Null 
      ;
      lResult = CopyMemory(@*HoldJob\HoldMode, *HoldMode, SizeOf(DEVMODE)) 
      ;
      ResultDLG = PrintDlg_(*HoldJob\HoldDialog)
      ;
      lResult = CopyMemory(*HoldMode, @*HoldJob\HoldMode, SizeOf(DEVMODE)) 
      ;/ The following block illustrates retrieving the printer name
      Define.DEVMODE *HoldDev
      *HoldDev = *HoldJob\HoldDialog\hDevMode
      Debug PeekS(@*HoldDev\dmDeviceName[0])
      ;/
      FreeMemory(*HoldMode) 
      ;
   EndIf  
   ;
EndProcedure
Note that the code is original based on code by Rings. Hope that helps!

Posted: Tue Aug 08, 2006 8:30 am
by Shardik

Code: Select all

i.L
PrintDlg.PRINTDLG
PrinterName.S
Result.L

DevMode = GlobalAlloc_(#GMEM_MOVEABLE | #GMEM_ZEROINIT, SizeOf(DEVMODE))
DevModeLocked = GlobalLock_(DevMode)

PrintDlg\lStructSize = SizeOf(PRINTDLG)
PrintDlg\Flags = #PD_ALLPAGES
PrintDlg\hDevMode = DevModeLocked

Result = PrintDlg_(PrintDlg)

If Result <> #False
  *DevMode.DEVMODE = PrintDlg\hDevMode

  While i < 32 And PeekB(*DevMode + i) <> 0
    PrinterName = PrinterName + Chr(PeekB(*DevMode + i))
    i = i + 1
  Wend

  If Len(PrinterName) = 31
    PrinterName = PrinterName + "..."
  EndIf

  MessageRequester("Printer Device Name", PrinterName, #MB_ICONINFORMATION)
EndIf

GlobalUnlock_(DevMode)
GlobalFree_(DevMode)

Posted: Tue Aug 08, 2006 9:43 am
by Shardik
Xombie,

one small advice for your code example. If you are requesting a DC handle with #PD_RETURNDC you should delete it afterwards:

Code: Select all

If lResult <> 0
  DeleteDC_(*HoldJob\HoldDialog\hDC)
End If
For further information take a look at the C example "Displaying the Print Dialog Box" in the MSDN:

http://windowssdk.msdn.microsoft.com/en ... 46829.aspx

Posted: Tue Aug 08, 2006 12:16 pm
by Edwin Knoppert
Afaik the devmode devicename can be empty or truncated.
The devnames structure contains the correct name(s).

Posted: Tue Aug 08, 2006 2:09 pm
by Shardik
Edwin,

you are right that ist is probably safer to read the device name from the DEVNAMES control structure. The MSDN says:
Note that the dmDeviceName member of the DEVMODE structure also specifies a printer name. However, dmDeviceName is limited to 32 characters, and the wDeviceOffset name is not. If the wDeviceOffset and dmDeviceName names are not the same, PrintDlg initializes the dialog box using the printer specified by wDeviceOffset.
http://windowssdk.msdn.microsoft.com/en ... 46843.aspx

But because the call of the PrintDlg() function initializes the dialog box with the printer from wDeviceOffset it should be nearly as safe to use my code after the call of this function.

The possible truncation of the device name is already taken care of in my code and was successfully tested with a very long "friendly" printer name.

Nevertheless here is the "safer" solution which doesn't truncate the "friendly" printer name:

Code: Select all

PrintDlg.PRINTDLG

DevNames = GlobalAlloc_(#GMEM_MOVEABLE | #GMEM_ZEROINIT, SizeOf(DEVNAMES))
DevModeLocked = GlobalLock_(DevNames)

PrintDlg\hDevMode = 0
PrintDlg\hDevNames = DevModeLocked
PrintDlg\lStructSize = SizeOf(PRINTDLG)
PrintDlg\Flags = #PD_ALLPAGES

If PrintDlg_(PrintDlg) <> #False
  *DEVNAMES.DEVNAMES = PrintDlg\hDevNames
  MessageRequester("Printer Device Name", PeekS(*DEVNAMES + *DEVNAMES\wDeviceOffset), #MB_ICONINFORMATION)
EndIf

GlobalUnlock_(DevNames)
GlobalFree_(DevNames)

Posted: Tue Aug 08, 2006 2:40 pm
by Edwin Knoppert
1)
You should not pass a locked global mem but it's unlocked handle.
Somehow it sees it's already locked but it's not what it should expect.

2)
The first time you have succesful printdlg call(+OK) the devmode+devnames structures *are* created for you.
You better continue using those throughout the program.
No need to prepare a memory block yourself.

Posted: Tue Aug 08, 2006 2:42 pm
by Edwin Knoppert
The global mem is a type of #GHND which should be #GMEM_MOVEABLE + #GMEM_ZEROINIT
(Which you did but it's better to follow the rules)

Posted: Tue Aug 08, 2006 3:04 pm
by TerryHough
Thanks for all the responses, guys.

However, I still don't have a valid "friendly" printer name being returned.

Shardik's latest code returns a "Q" which certainly isn't the name.

So far, I haven't been able to utilize Xombie's code and get any response. Probably something I did, but I just get a memory error with it.

No wonder I was confused. This seems terribly difficult.

Posted: Tue Aug 08, 2006 3:09 pm
by Edwin Knoppert
I rewrote it and shows the printername:

Code: Select all


PrintDlg.PRINTDLG 
PrintDlg\hDevMode = 0 
PrintDlg\hDevNames = 0
PrintDlg\lStructSize = SizeOf(PRINTDLG) 
PrintDlg\Flags = #PD_ALLPAGES 

If PrintDlg_(PrintDlg) <> #FALSE 
    *DEVNAMES.DEVNAMES = GlobalLock_(PrintDlg\hDevNames) 
    MessageRequester("Printer Device Name", PeekS(*DEVNAMES + *DEVNAMES\wDeviceOffset), #MB_ICONINFORMATION) 
    GlobalUnlock_(PrintDlg\hDevNames)
EndIf 

; Only at the end of the app
GlobalFree_(PrintDlg\hDevNames)
Of course it lacks testing for valid memory and such..

Posted: Tue Aug 08, 2006 3:32 pm
by TerryHough
Edwin Knoppert wrote:I rewrote it and shows the printername:
Thanks, Edwin. That works for me here.

I got into this trying to print a page in landscape mode. Now, I can
at least try to get that working.

In fact, I have to also get the printer to select a second tray.

I will need all the help I can get.

Thanks to everyone who responded. It was all a big help.

Terry

Posted: Tue Aug 08, 2006 4:07 pm
by Edwin Knoppert
Make sure you add some testing, i left these out..

Posted: Wed Aug 09, 2006 8:22 am
by Shardik
Terry,

I am sorry that my code didn't work for you. Did you test it with Win98? I had no problems running both examples under WinNT SP6 and WinXP SP2.

How do you want to switch to landscape mode? If you want to display the print dialog and preset to landscape mode, take a look at a code example from wichtel in the German forum:

http://www.purebasic.fr/german/archive/ ... php?t=1680

If you don't want to display the print dialog and change the landscape mode only for the default printer and the current print, take a look at this code example in the German forum:

http://www.purebasic.fr/german/archive/ ... c.php?t=83

If you want to change the default of a print dialog setting without displaying the print dialog, take a look at a code example from me in the German forum, which changes the number of copies to print for the default printer:

http://www.purebasic.fr/german/viewtopic.php?t=6453

Tell me, if you want to change the default setting to landscape mode (without displaying the print dialog) and I will post an adapted example for you.


Edwin,

thank you very much for optimizing my code and pinpointing its shortcuts. You are right that it is unnecessary to allocate memory and passing it to the print dialog if you only want to read from the DEVNAMES or DEVMODE structure. But the example was adapted from a code which manipulated the contents of the print dialog...