Home All Groups Group Topic Archive Search About

CommonDialog sets Windows Default Printer - is Mike Williams out there?

Author
21 Mar 2006 8:37 PM
John Kotuby
Hi guys,
This post is a response to a thread I started back on 3/2/06 on how the VB
CommonDialog control sets the Windows Default printer to the one chosen ..
which is something nobody really wants.
Mile Wilson posted some great code but I had trouble with it on a couple of
network printers. I discover now that the devicenames of those network
printers were longer than 30 characters and somewhere the APIPrinter Dialog
code truncates the NewPrinterName variable to only 30 characters. The code
that Mike posted is at the bottom of this post.
---------------------------------------------------------------
Mike,
An update...
As you can see in the code below I added a messagebox to display the
NewPrinterName and objPrinter.DeviceName. It appears that the NewPrinterName
is being truncated at 30 characters and so the comparison does not work. My
guess that the longer device names presented a problem was correct.
Why are they being truncated? I don't know yet.

  If UCase(Printer.DeviceName) <> NewPrinterName Then
    For Each objPrinter In Printers

      MsgBox "Selected: " + NewPrinterName + vbCrLf + "Device  : " +
objPrinter.DeviceName

      If UCase$(objPrinter.DeviceName) = NewPrinterName Then
        Set Printer = objPrinter
      End If
    Next
  End If
--------------------------------------------------
Just thought you might like to know what I had discovered. Maybe it's in the
code that pulls the NewPrinterName from the DevMode.dmDeviceName.
--------------------------------------------------

NewPrinterName = UCase$(Left(DevMode.dmDeviceName, _
    InStr(DevMode.dmDeviceName, Chr$(0)) - 1))
-----------------------------------------------

Also I see Len(DevMode) used a lot.

Anyway thanks for the help.
I can probably just make the comparison of the first 30 characters of the
device names and the code would work in 99.9% of the cases.

-----------------------------------------------------------
----------------------------------------------------------
Option Explicit
Private Const OUT_DEFAULT_PRECIS = 0
Private Const CLIP_DEFAULT_PRECIS = 0
Private Const DEFAULT_QUALITY = 0
Private Const DEFAULT_PITCH = 0
Private Const FF_ROMAN = 16
Private Const CCHDEVICENAME = 32
Private Const CCHFORMNAME = 32
Private Const GMEM_MOVEABLE = &H2
Private Const GMEM_ZEROINIT = &H40
Private Const DM_DUPLEX = &H1000&
Private Const DM_ORIENTATION = &H1&
Private Const DM_PAPERSIZE = &H2&
Private Const PD_PRINTSETUP = &H40
Private Const PD_RETURNDC = &H100
Private Const PD_DISABLEPRINTTOFILE = &H80000
Private Const PHYSICALOFFSETX As Long = 112
Private Const PHYSICALOFFSETY As Long = 113
Private Const OPAQUE = 0
Private Const TRANSPARENT = 1
Private Type POINTAPI
  x As Long
  y As Long
End Type
Private Type RECT
  Left As Long
  Top As Long
  Right As Long
  Bottom As Long
End Type
Private Type PRINTDLG_TYPE
  lStructSize As Long
  hwndOwner As Long
  hDevMode As Long
  hDevNames As Long
  hdc As Long
  flags As Long
  nFromPage As Integer
  nToPage As Integer
  nMinPage As Integer
  nMaxPage As Integer
  nCopies As Integer
  hInstance As Long
  lCustData As Long
  lpfnPrintHook As Long
  lpfnSetupHook As Long
  lpPrintTemplateName As String
  lpSetupTemplateName As String
  hPrintTemplate As Long
  hSetupTemplate As Long
End Type
Private Type DEVNAMES_TYPE
  wDriverOffset As Integer
  wDeviceOffset As Integer
  wOutputOffset As Integer
  wDefault As Integer
  extra As String * 100
End Type
Private Type DEVMODE_TYPE
  dmDeviceName As String * CCHDEVICENAME
  dmSpecVersion As Integer
  dmDriverVersion As Integer
  dmSize As Integer
  dmDriverExtra As Integer
  dmFields As Long
  dmOrientation As Integer
  dmPaperSize As Integer
  dmPaperLength As Integer
  dmPaperWidth As Integer
  dmScale As Integer
  dmCopies As Integer
  dmDefaultSource As Integer
  dmPrintQuality As Integer
  dmColor As Integer
  dmDuplex As Integer
  dmYResolution As Integer
  dmTTOption As Integer
  dmCollate As Integer
  dmFormName As String * CCHFORMNAME
  dmUnusedPadding As Integer
  dmBitsPerPel As Integer
  dmPelsWidth As Long
  dmPelsHeight As Long
  dmDisplayFlags As Long
  dmDisplayFrequency As Long
End Type
Private Declare Function GetDeviceCaps Lib "gdi32" _
(ByVal hdc As Long, ByVal nindex As Long) As Long
Private Declare Function PrintDialog Lib "comdlg32.dll" _
  Alias "PrintDlgA" (pPrintdlg As PRINTDLG_TYPE) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias _
  "RtlMoveMemory" (hpvDest As Any, hpvSource As Any, _
  ByVal cbCopy As Long)
Private Declare Function GlobalLock Lib "kernel32" _
  (ByVal hMem As Long) As Long
Private Declare Function GlobalUnlock Lib "kernel32" _
  (ByVal hMem As Long) As Long
Private Declare Function GlobalAlloc Lib "kernel32" _
  (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function GlobalFree Lib "kernel32" _
  (ByVal hMem As Long) As Long
Private Declare Function SetBkMode Lib "gdi32" _
  (ByVal hdc As Long, ByVal nBkMode As Long) As Long

Private Sub SetPrinterOrigin(x As Single, y As Single)
With Printer
  .ScaleLeft = .ScaleX(GetDeviceCaps(.hdc, PHYSICALOFFSETX), _
    vbPixels, .ScaleMode) - x
  .ScaleTop = .ScaleY(GetDeviceCaps(.hdc, PHYSICALOFFSETY), _
    vbPixels, .ScaleMode) - y
  .CurrentX = 0
  .CurrentY = 0
End With
End Sub

Private Function PrintReport(frmOwner As Form, Optional _
  InitialPrinter As String, Optional PrintFlags As Long) _
  As Boolean
Dim PrintDlg As PRINTDLG_TYPE
Dim DevMode As DEVMODE_TYPE
Dim DevName As DEVNAMES_TYPE
Dim lpDevMode As Long, lpDevName As Long
Dim bReturn As Integer, OriginalPrinter As String
Dim objPrinter As Printer, NewPrinterName As String
PrintDlg.lStructSize = Len(PrintDlg)
PrintDlg.hwndOwner = frmOwner.hWnd
PrintDlg.flags = PrintFlags
On Error Resume Next
' save the current VB printer device name in case the user
' presses cancel and we need to restore it
OriginalPrinter = Printer.DeviceName
' Set the initial printer in the dialog
If Len(InitialPrinter) > 0 Then
  For Each objPrinter In Printers
    If InStr(1, objPrinter.DeviceName, InitialPrinter, _
        vbTextCompare) > 0 Then
      Set Printer = objPrinter
      Exit For
    End If
  Next
End If
'
DevMode.dmDeviceName = Printer.DeviceName
DevMode.dmSize = Len(DevMode)
' Must set the appropriate flag in the dmFields entry for
' any setting you wish to use (there are loads of 'em, see
' API Viewer)
DevMode.dmFields = DM_ORIENTATION Or DM_PAPERSIZE
'DevMode.dmPaperWidth = Printer.Width
DevMode.dmOrientation = Printer.Orientation 'vbPRORLandscape
DevMode.dmPaperSize = Printer.PaperSize 'vbPRPSEnv10
'DevMode.dmDuplex = Printer.Duplex
On Error GoTo 0
PrintDlg.hDevMode = GlobalAlloc(GMEM_MOVEABLE Or _
  GMEM_ZEROINIT, Len(DevMode))
lpDevMode = GlobalLock(PrintDlg.hDevMode)
If lpDevMode > 0 Then
  CopyMemory ByVal lpDevMode, DevMode, Len(DevMode)
  bReturn = GlobalUnlock(PrintDlg.hDevMode)
End If
With DevName
  .wDriverOffset = 8
  .wDeviceOffset = .wDriverOffset + 1 + Len _
    (Printer.DriverName)
  .wOutputOffset = .wDeviceOffset + 1 + Len(Printer.Port)
  .wDefault = 0
End With
With Printer
  DevName.extra = .DriverName & Chr(0) & _
    .DeviceName & Chr(0) & .Port & Chr(0)
End With
PrintDlg.hDevNames = GlobalAlloc(GMEM_MOVEABLE Or _
  GMEM_ZEROINIT, Len(DevName))
lpDevName = GlobalLock(PrintDlg.hDevNames)
If lpDevName > 0 Then
  CopyMemory ByVal lpDevName, DevName, Len(DevName)
  bReturn = GlobalUnlock(lpDevName)
End If
If PrintDialog(PrintDlg) <> 0 Then
  DoEvents ' allow the dialog to remove itself from the display
  Me.Refresh
  PrintReport = True
  lpDevName = GlobalLock(PrintDlg.hDevNames)
  CopyMemory DevName, ByVal lpDevName, 45
  bReturn = GlobalUnlock(lpDevName)
  GlobalFree PrintDlg.hDevNames
  lpDevMode = GlobalLock(PrintDlg.hDevMode)
  CopyMemory DevMode, ByVal lpDevMode, Len(DevMode)
  bReturn = GlobalUnlock(PrintDlg.hDevMode)
  GlobalFree PrintDlg.hDevMode
  NewPrinterName = UCase$(Left(DevMode.dmDeviceName, _
    InStr(DevMode.dmDeviceName, Chr$(0)) - 1))
  If UCase(Printer.DeviceName) <> NewPrinterName Then
    For Each objPrinter In Printers
      If UCase$(objPrinter.DeviceName) = NewPrinterName Then
        Set Printer = objPrinter
      End If
    Next
  End If
'
  On Error Resume Next
  Printer.Copies = DevMode.dmCopies
  Printer.Duplex = DevMode.dmDuplex
  Printer.Orientation = DevMode.dmOrientation
  Printer.PaperSize = DevMode.dmPaperSize
  Printer.PrintQuality = DevMode.dmPrintQuality
  Printer.ColorMode = DevMode.dmColor
  Printer.PaperBin = DevMode.dmDefaultSource
  '
  ' Mike's Note: One or other of the above printer settings
  ' is setting the printer font to opaque (even though the
  ' value of Printer.FontTransparent remains True). This might
  ' not happen on all printers, but it needs fixing anyway so
  ' I have now added the following line to set the font back
  ' to transparent.
  SetBkMode Printer.hdc, TRANSPARENT
  '
  On Error GoTo 0
Else
  PrintReport = False ' user cancelled
  For Each objPrinter In Printers
    If objPrinter.DeviceName = OriginalPrinter Then
      Set Printer = objPrinter
      Exit For
    End If
  Next
  GlobalFree PrintDlg.hDevNames
  GlobalFree PrintDlg.hDevMode
End If
End Function

Private Sub Command1_Click()
' Display a printer dialog which starts off with any
' initial printer we wish. In this example we use the
' first Epson printer that we find. If the specified
' printer is not found then the dialog opens showing
' the current default printer (If you want to start
' showing the default then you can either modify the
' routine so as to cause the first For Each loop is not
' executed or, more simply, use something like:
' PrintReport, Me, "start with the default printer"
If PrintReport(Me, "Epson") = False Then
' If PrintReport(Me, "Epson", PD_RETURNDC) = False Then
  Caption = "cancelled"
  Exit Sub ' user cancelled
End If
' ******** Print your document here ********
' The following line sets the origin (0, 0) to the top left
' corner of the physical page on all printers instead of to
' the top left corner of the "printable area" (the default).
' This enables you to position stuff accurately on the page
' whatever printer you are using.
Printer.ScaleMode = vbInches
SetPrinterOrigin 0, 0
'
Printer.Font.Name = "Times New Roman"
Printer.Font.Size = 12
' set the line thickness to about 3 thousandths of an inch
Printer.DrawWidth = Printer.ScaleX(0.003, vbInches, vbPixels)
' draw a 1 x 4 inch rectangle at location (1, 1)
Printer.Line (1, 1)-(1 + 4, 1 + 1), , B
' Draw some text at location (1, 3)
Printer.CurrentX = 1: Printer.CurrentY = 3
Printer.Print "Whisky and Coke®"
Printer.EndDoc
End Sub

Author
22 Mar 2006 12:24 AM
Randy Birch
This is a limitation of Windows. Bottom line, don't name printers longer
than the max.

#define CCHDEVICENAME 32

Const CCDEVICENAME As Long = 32


--

Randy Birch
MS MVP Visual Basic
http://vbnet.mvps.org/

Please reply to the newsgroups so all can participate.
Author
23 Mar 2006 3:24 PM
John Kotuby
Thanks Randy....

I didn't name the printer. This is the name supplied by the manufacturer (or
maybe Microsoft itself) when the printer and driver was installed on the
various PCs in the default manner, which is how 99.9% of the printers are
installed at our customer sites. I supplied shorter Share names for the
printers, but the printers insist on showing themselves with the full name.

Do you have a suggestion around this... especially in a network environment
where the DeviceName is prefixed by the Network path?

Also the printer DeviceNames that have a problem actually are 32 characters
long. That's why I was wondering why they are truncated to 30 by the API
Dialog.

Thanks mucho

Show quoteHide quote
"Randy Birch" <rgb_removet***@mvps.org> wrote in message
news:OKsUEcUTGHA.4920@tk2msftngp13.phx.gbl...
> This is a limitation of Windows. Bottom line, don't name printers longer
> than the max.
>
> #define CCHDEVICENAME 32
>
> Const CCDEVICENAME As Long = 32
>
>
> --
>
> Randy Birch
> MS MVP Visual Basic
> http://vbnet.mvps.org/
>
> Please reply to the newsgroups so all can participate.
>
>
>
Author
24 Mar 2006 3:50 AM
Randy Birch
I know of no workaround. I suspect the truncation is to allow for trailing
nulls at the end of the C string.

--

Randy Birch
MS MVP Visual Basic
http://vbnet.mvps.org/

Please reply to the newsgroups so all can participate.




Show quoteHide quote
"John Kotuby" <jo***@powerlist.com> wrote in message
news:exsBJ2oTGHA.1688@TK2MSFTNGP11.phx.gbl...
: Thanks Randy....
:
: I didn't name the printer. This is the name supplied by the manufacturer
(or
: maybe Microsoft itself) when the printer and driver was installed on the
: various PCs in the default manner, which is how 99.9% of the printers are
: installed at our customer sites. I supplied shorter Share names for the
: printers, but the printers insist on showing themselves with the full
name.
:
: Do you have a suggestion around this... especially in a network
environment
: where the DeviceName is prefixed by the Network path?
:
: Also the printer DeviceNames that have a problem actually are 32
characters
: long. That's why I was wondering why they are truncated to 30 by the API
: Dialog.
:
: Thanks mucho
:
: "Randy Birch" <rgb_removet***@mvps.org> wrote in message
: news:OKsUEcUTGHA.4920@tk2msftngp13.phx.gbl...
: > This is a limitation of Windows. Bottom line, don't name printers longer
: > than the max.
: >
: > #define CCHDEVICENAME 32
: >
: > Const CCDEVICENAME As Long = 32
: >
: >
: > --
: >
: > Randy Birch
: > MS MVP Visual Basic
: > http://vbnet.mvps.org/
: >
: > Please reply to the newsgroups so all can participate.
: >
: >
: >
:
: