Change printer tray from vb

Posted on 2006-04-14
Last Modified: 2012-05-05
Hi all,

I'm looking for a solution to do the following:

I have an ActiveX control, and the only thing this control does is to print a given html page (by url). To do this job, I'm using the WebBrowser control, with code like


Now the problem is that I would like (or, better to say, the clients would like;)) to select the exact printer tray to use.

What I see as a solution would be to find the default printer, and set its (imaginary) DefaultTray property to X, then to call the IE print function.

What do you think?

Question by:kerzner
    LVL 26

    Expert Comment

    LVL 1

    Author Comment

    Well, this works if you want to use the Printer object, and print something with it; however it does not affect the WebBrowser component's print function. What I'm looking for is maybe to change (and save!) the default printer's PaperBin before asking the WebBrowser to print.
    LVL 1

    Author Comment

    Well, I used the code from this page, and it worked perfectly!!!;EN-US;180645
    LVL 1

    Author Comment

    OK I will give the points to the first one who read this and submits any comment
    LVL 26

    Expert Comment

    you can delete the question by posting your comment here
    LVL 1

    Author Comment

    Thanks, I just did that.
    Was looking for a delete/cancel button or something, but there wasn't such thing and I decided it's impossible to cancel a question.
    LVL 1

    Author Comment

    If one's curious of the code, here it is:
    (It is compiled from several places, and can be used to permanently change printer settings, in my case Paper Orientation, but can be used for a wide range of other properties)


    Option Explicit

    ' Constants for DeviceCapabilities
    Private Const DC_BINS = 6
    Private Const DC_BINNAMES = 12

    ' Printer Access Levels
    Private Const PRINTER_ACCESS_ADMINISTER As Long = &H4
    Private Const PRINTER_ACCESS_USE As Long = &H8
    Private Const STANDARD_RIGHTS_REQUIRED As Long = &HF0000
    ' Constants for DEVMODE.
    Private Const CCHDEVICENAME = 32
    Private Const CCHFORMNAME = 32

    ' Constants for DocumentProperties() call.
    Private Const DM_MODIFY = 8
    Private Const DM_IN_BUFFER = DM_MODIFY
    Private Const DM_COPY = 2
    Private Const DM_OUT_BUFFER = DM_COPY

    ' Constants for Orientation.
    Private Const DMORIENT_PORTRAIT = 1
    Private Const DMORIENT_LANDSCAPE = 2

    ' Constants for printer bin.
    Private Const DMBIN_UPPER = 1
    Private Const DMBIN_LOWER = 2

    ' Constants for DMFIELDS (which fields did you change?).
    Private Const DM_ORIENTATION = &H1
    Private Const DM_DEFAULTSOURCE = &H200

    Private Type DOCINFO
       cbSize As Long
       lpszDocName As String
       lpszOutput As String
    End Type

       pDataType As String
       pDevMode As Long
       DesiredAccess As Long
    End Type

    Private Type PRINTER_INFO_2
       pServerName As Long
       pPrinterName As Long
       pShareName As Long
       pPortName As Long
       pDriverName As Long
       pComment As Long
       pLocation As Long
       pDevMode As Long
       pSepFile As Long
       pPrintProcessor As Long
       pDataType As Long
       pParameters As Long
       pSecurityDescriptor As Long
       Attributes As Long
       Priority As Long
       DefaultPriority As Long
       StartTime As Long
       UntilTime As Long
       Status As Long
       cJobs As Long
       AveragePPM As Long
    End Type

    Private Type DEVMODE
       dmDeviceName(1 To CCHDEVICENAME) As Byte
       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(1 To CCHFORMNAME) As Byte
       dmUnusedPadding As Integer
       dmBitsPerPel As Integer
       dmPelsWidth As Long
       dmPelsHeight As Long
       dmDisplayFlags As Long
       dmDisplayFrequency As Long
    End Type

    Private Declare Function OpenPrinter Lib "winspool.drv" Alias _
        "OpenPrinterA" (ByVal pPrinterName As String, phPrinter As _
        Long, pDefault As Any) As Long

    Private Declare Function ClosePrinter Lib "winspool.drv" _
        (ByVal hPrinter As Long) As Long

    Private Declare Function GetPrinter Lib "winspool.drv" Alias _
        "GetPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, _
       pPrinter As Any, ByVal cbBuf As Long, pcbNeeded As Long) As Long
    Private Declare Function SetPrinter Lib "winspool.drv" Alias _
       "SetPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, _
       pPrinter As Any, ByVal Command As Long) As Long
    Private Declare Function DocumentProperties Lib "winspool.drv" _
        Alias "DocumentPropertiesA" (ByVal hwnd As Long, _
        ByVal hPrinter As Long, ByVal pDeviceName As String, _
        pDevModeOutput As Any, pDevModeInput As Any, _
        ByVal fMode As Long) As Long

    Private Declare Function ResetDC Lib "gdi32" Alias "ResetDCA" _
        (ByVal hdc As Long, lpInitData As Any) As Long

    Private Declare Function CreateDC Lib "gdi32" Alias "CreateDCA" _
        (ByVal lpDriverName As String, ByVal lpDeviceName As String, _
        ByVal lpOutput As Long, ByVal lpInitData As Long) As Long

    Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) _
        As Long

    Private Declare Function GetLastError Lib "KERNEL32" () As Long

    Private Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" _
        (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)

    Function SetPrinterPageSource(PrinterName As String, hPrtDc As Long, PaperSource As Long) As Boolean

       Dim nSize As Long           ' Size of array (used when reading DEVMODE and PRINTER_INFO_2)
       Dim pPrinterInfo2 As PRINTER_INFO_2 ' PRINTER_INFO_2 structure
       Dim pDevMode As DEVMODE             ' DEVMODE structure
       Dim hPrinter As Long        ' Handle to the printer
       Dim LastError As Long       ' Return value for GetLastError
       Dim bPrinterInfo2() As Byte ' The PRINTER_INFO_2 structure in binary format
       Dim bDevMode() As Byte      ' The DEVMODE structure in binary format

       pd.DesiredAccess = PRINTER_ALL_ACCESS
       ' Get a handle to the printer with full access - needed if we want to save our changes permanently
       If OpenPrinter(PrinterName, hPrinter, pd) Then

          ' Read PRINTER_INFO_2 as binary array
          GetPrinter hPrinter, 2&, 0&, 0&, nSize
          ReDim bPrinterInfo2(1 To nSize) As Byte
          GetPrinter hPrinter, 2, bPrinterInfo2(1), nSize, nSize
          ' Fill in the structure
          CopyMemory pPrinterInfo2, bPrinterInfo2(1), Len(pPrinterInfo2)

          ' Get the size of the DEVMODE, passing 0's at the end
          nSize = DocumentProperties(Me.hwnd, hPrinter, PrinterName, 0&, 0&, 0)

          ' Reserve memory for the actual size of the DEVMODE
          ReDim bDevMode(1 To nSize)

          ' Fill the DEVMODE as binary array
          DocumentProperties Me.hwnd, hPrinter, PrinterName, bDevMode(1), 0&, DM_OUT_BUFFER

          ' Copy the predefined portion of the DEVMODE.
          CopyMemory pDevMode, bDevMode(1), Len(pDevMode)

          ' Change the appropriate member in the DevMode.
          'pDevMode.dmDefaultSource = PaperSource
          pDevMode.dmOrientation = DMORIENT_LANDSCAPE

          ' Set the dmFields bit flag to indicate what you're changing.
          'pDevMode.dmFields = pDevMode.dmFields Or DM_DEFAULTSOURCE
          pDevMode.dmFields = pDevMode.dmFields Or DM_ORIENTATION

          ' Copy your changes back, then update DEVMODE.
          CopyMemory bDevMode(1), pDevMode, Len(pDevMode)
          DocumentProperties Me.hwnd, hPrinter, PrinterName, bDevMode(1), bDevMode(1), DM_IN_BUFFER Or DM_OUT_BUFFER

          ' Point PRINTER_INFO_2 at our modified DEVMODE
          pPrinterInfo2.pDevMode = VarPtr(bDevMode(1))
          ' Finally make our changes permanent
          SetPrinter hPrinter, 2, pPrinterInfo2, 0&
          ResetDC hPrtDc, bDevMode(1)   ' Reset the DEVMODE
          ' Close the handle when you're done with it.
          ClosePrinter hPrinter
          SetPrinterPageSource = True

          SetPrinterPageSource = False  ' Reset failed!
          LastError = GetLastError()
          MsgBox "Error setting printer bin. Error Code: " & LastError, vbExclamation, "Print Error"
       End If
    End Function
    LVL 1

    Author Comment

    Oh, and to use the above routine, try for example

       Dim hPrintDC As Long: hPrintDC = CreateDC(Printer.DriverName, Printer.DeviceName, 0, 0)
       SetPrinterPageSource Printer.DeviceName, hPrintDC, SomeTrayNumber

    you can get the tray/bin numbers for a printer by the following routine:

    ' Pass this to a multiline textbox...
    Private Function GetPrinterBins(Prn As Printer) As String
       Dim PrinterBins As String
       Dim BinsCount As Long: BinsCount = DeviceCapabilities(Prn.DeviceName, Prn.Port, DC_BINS, ByVal vbNullString, 0)
       Dim BinNumbers() As Integer: ReDim BinNumbers(1 To BinsCount)
       Dim BinNames As String: BinNames = String(24 * BinsCount, 0)
       BinsCount = DeviceCapabilities(Prn.DeviceName, Prn.Port, DC_BINS, BinNumbers(1), 0)
       BinsCount = DeviceCapabilities(Prn.DeviceName, Prn.Port, DC_BINNAMES, ByVal BinNames, 0)
       PrinterBins = PrinterBins & Prn.DeviceName
       Dim BinString As String
       Dim nBin As Integer
       For nBin = 1 To BinsCount
          BinString = Mid(BinNames, 24 * (nBin - 1) + 1, 24)
          BinString = Left(BinString, InStr(1, BinString, Chr(0)) - 1)
          BinString = String(6 - Len(CStr(BinNumbers(nBin))), " ") & BinNumbers(nBin) & "  " & BinString
          PrinterBins = PrinterBins & vbCrLf & BinString
       GetPrinterBins = PrinterBins
    End Function

    where DeviceCapabilities is declared with:

    Private Declare Function DeviceCapabilities Lib "winspool.drv" _
       Alias "DeviceCapabilitiesA" (ByVal lpDeviceName As String, _
       ByVal lpPort As String, ByVal iIndex As Long, lpOutput As Any, _
       ByVal dev As Long) As Long




    Accepted Solution

    Closed, 500 points refunded.
    The Experts Exchange
    Community Support Moderator of all Ages

    Featured Post

    How to improve team productivity

    Quip adds documents, spreadsheets, and tasklists to your Slack experience
    - Elevate ideas to Quip docs
    - Share Quip docs in Slack
    - Get notified of changes to your docs
    - Available on iOS/Android/Desktop/Web
    - Online/Offline

    Join & Write a Comment

    Introduction I needed to skip over some file processing within a For...Next loop in some old production code and wished that VB (classic) had a statement that would drop down to the end of the current iteration, bypassing the statements that were c…
    If you need to start windows update installation remotely or as a scheduled task you will find this very helpful.
    As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
    Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…

    729 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    19 Experts available now in Live!

    Get 1:1 Help Now