This article was previously published under Q230743


Microsoft Word for Windows does not provide a method for Automation clients to set the duplex print flag before starting a print job. Although there is a parameter in the PrintOut method that indicates support for duplex printing, the parameter does not provide true duplex printing and may not be available depending on your operating system or installed language. However, developers can work around this limitation on Windows systems by changing the duplex flag for the active printer driver before calling Word's PrintOut function.

This article demonstrates how to use the Windows API to change the duplex setting of the active printer and allow a Word document to be printed in duplex.


More Information

This code uses the DocumentProperties API to change the print settings of the printer driver to enable duplex printing. For this code to work successfully, the end user will need adequate permissions to change the global print settings for the printer.

If a user does not have the proper permission to change driver settings, they will get an Access Denied error on the OpenPrinter API call.

For users of Microsoft Windows NT and Microsoft Windows 2000 who need to print to a shared network printer, this can be a problem because the print driver does not reside on the local machine but on the print server. Although it is possible for an administrator to configure the print server to give end users the proper permission to change global settings, it is NOT desirable to do so in most cases. To work around this problem, it is possible to install a local print driver for the network printer, and let each user control the settings for their local systems.


Steps to Add Local Print Driver for Network Printer on Windows NT and Windows 2000

  1. One the Start menu, select Settings, then select Printers and double-click Add Printer to bring up the Add Printer wizard.

  2. When prompted, select printer from "My Computer" and NOT from the network. Although you will connect to a network printer, you want to use a driver on My Computer. Press Next to continue.

  3. Click "Add Port".

  4. Select Local Port in the drop-down list box and then click New Port.

  5. Type the location of the printer on the network. For example: \\printserver\printername (using the exact path name to the printer).

  6. Select OK and continue with the rest of the setup. Changing the printer properties for the active printer will affect all applications that use that printer, and not just Word. If you plan on changing the setting for a particular print job, make sure you restore the setting when the job is complete.


Steps to Build the Sample

  1. Open Visual Basic and create a new project. Form1 is created by default.

  2. Add a standard BAS module to the project and add the following code to the module's code window:

    Option Explicit
     pDatatype As Long
     pDevmode As Long
     DesiredAccess As Long
    End Type
    Public 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       ' Pointer to DEVMODE
     pSepFile As Long
     pPrintProcessor As Long
     pDatatype As Long
     pParameters As Long
     pSecurityDescriptor As Long  ' Pointer to SECURITY_DESCRIPTOR
     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
    Public Type DEVMODE
     dmDeviceName As String * 32
     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 * 32
     dmUnusedPadding As Integer
     dmBitsPerPel As Integer
     dmPelsWidth As Long
     dmPelsHeight As Long
     dmDisplayFlags As Long
     dmDisplayFrequency As Long
     dmICMMethod As Long
     dmICMIntent As Long
     dmMediaType As Long
     dmDitherType As Long
     dmReserved1 As Long
     dmReserved2 As Long
    End Type
    Public Const DM_DUPLEX = &H1000&
    Public Const DM_IN_BUFFER = 8
    Public Const DM_OUT_BUFFER = 2
    Public Const PRINTER_ACCESS_USE = &H8
    Public Declare Function ClosePrinter Lib "winspool.drv" _
    (ByVal hPrinter As Long) As Long
    Public Declare Function DocumentProperties Lib "winspool.drv" _
     Alias "DocumentPropertiesA" (ByVal hwnd As Long, _
     ByVal hPrinter As Long, ByVal pDeviceName As String, _
     ByVal pDevModeOutput As Long, ByVal pDevModeInput As Long, _
     ByVal fMode As Long) As Long
    Public Declare Function GetPrinter Lib "winspool.drv" Alias _
     "GetPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, _
     pPrinter As Byte, ByVal cbBuf As Long, pcbNeeded As Long) As Long
    Public Declare Function OpenPrinter Lib "winspool.drv" Alias _
     "OpenPrinterA" (ByVal pPrinterName As String, phPrinter As Long, _
     pDefault As PRINTER_DEFAULTS) As Long
    Public Declare Function SetPrinter Lib "winspool.drv" Alias _
     "SetPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, _
     pPrinter As Byte, ByVal Command As Long) As Long
    Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
    (pDest As Any, pSource As Any, ByVal cbLength As Long)
    ' ==============================================================
    ' SetPrinterDuplex 
    '  Programmatically set the Duplex flag for the specified printer
    '  driver's default properties.
    '  Returns: True on success, False on error. (An error will also
    '  display a message box. This is done for informational value
    '  only. You should modify the code to support better error
    '  handling in your production application.)
    '  Parameters:
    '    sPrinterName - The name of the printer to be used.
    '    nDuplexSetting - One of the following standard settings:
    '       1 = None
    '       2 = Duplex on long edge (book)
    '       3 = Duplex on short edge (legal)
    ' ==============================================================
    Public Function SetPrinterDuplex(ByVal sPrinterName As String, _
     ByVal nDuplexSetting As Long) As Boolean
    Dim hPrinter As Long
    Dim pinfo As PRINTER_INFO_2
    Dim dm As DEVMODE
    Dim yDevModeData() As Byte
    Dim yPInfoMemory() As Byte
    Dim nBytesNeeded As Long
    Dim nRet As Long, nJunk As Long
    On Error GoTo cleanup
    If (nDuplexSetting < 1) Or (nDuplexSetting > 3) Then
       MsgBox "Error: dwDuplexSetting is incorrect."
       Exit Function
    End If
    pd.DesiredAccess = PRINTER_ALL_ACCESS
    nRet = OpenPrinter(sPrinterName, hPrinter, pd)
    If (nRet = 0) Or (hPrinter = 0) Then
       If Err.LastDllError = 5 Then
          MsgBox "Access denied -- See the article for more info."
          MsgBox "Cannot open the printer specified " & _
            "(make sure the printer name is correct)."
       End If
       Exit Function
    End If
    nRet = DocumentProperties(0, hPrinter, sPrinterName, 0, 0, 0)
    If (nRet < 0) Then
       MsgBox "Cannot get the size of the DEVMODE structure."
       GoTo cleanup
    End If
    ReDim yDevModeData(nRet + 100) As Byte
    nRet = DocumentProperties(0, hPrinter, sPrinterName, _
                VarPtr(yDevModeData(0)), 0, DM_OUT_BUFFER)
    If (nRet < 0) Then
       MsgBox "Cannot get the DEVMODE structure."
       GoTo cleanup
    End If
    Call CopyMemory(dm, yDevModeData(0), Len(dm))
    If Not CBool(dm.dmFields And DM_DUPLEX) Then
      MsgBox "You cannot modify the duplex flag for this printer " & _
             "because it does not support duplex or the driver " & _
             "does not support setting it from the Windows API."
       GoTo cleanup
    End If
    dm.dmDuplex = nDuplexSetting
    Call CopyMemory(yDevModeData(0), dm, Len(dm))
    nRet = DocumentProperties(0, hPrinter, sPrinterName, _
      VarPtr(yDevModeData(0)), VarPtr(yDevModeData(0)), _
    If (nRet < 0) Then
      MsgBox "Unable to set duplex setting to this printer."
      GoTo cleanup
    End If
    Call GetPrinter(hPrinter, 2, 0, 0, nBytesNeeded)
    If (nBytesNeeded = 0) Then GoTo cleanup
    ReDim yPInfoMemory(nBytesNeeded + 100) As Byte
    nRet = GetPrinter(hPrinter, 2, yPInfoMemory(0), nBytesNeeded, nJunk)
    If (nRet = 0) Then
       MsgBox "Unable to get shared printer settings."
       GoTo cleanup
    End If
    Call CopyMemory(pinfo, yPInfoMemory(0), Len(pinfo))
    pinfo.pDevmode = VarPtr(yDevModeData(0))
    pinfo.pSecurityDescriptor = 0
    Call CopyMemory(yPInfoMemory(0), pinfo, Len(pinfo))
    nRet = SetPrinter(hPrinter, 2, yPInfoMemory(0), 0)
    If (nRet = 0) Then
       MsgBox "Unable to set shared printer settings."
    End If
    SetPrinterDuplex = CBool(nRet)
    If (hPrinter <> 0) Then Call ClosePrinter(hPrinter)
    End Function
  3. On Form1, add a standard Command Button.

  4. Add the following code to the code window for Form1:

    Option Explicit
    Private Sub Command1_Click()
    Dim oWord As Object
    Dim oDoc As Object
    Set oWord = CreateObject("Word.application")
    oWord.Visible = True
    Set oDoc = oWord.Documents.Add
    oWord.Selection.TypeText "This is on page 1" & vbCr
    oWord.Selection.InsertBreak 1
    oWord.Selection.TypeText "This is page 2"
    SetPrinterDuplex Printer.DeviceName, 2
    oDoc.PrintOut Background:=False
    SetPrinterDuplex Printer.DeviceName, 1
    MsgBox "Print Done", vbMsgBoxSetForeground
    oDoc.Saved = True
    Set oDoc = Nothing
    Set oWord = Nothing
    End Sub
  5. Run the sample. If you have a printer that supports duplex printing, the test document should print on both sides of the page.


Still have questions? We can help. Submit a case to Technical Support.

Last Modified On: December 10, 2016