Option Explicit
' Macro écrite pour Excel
'Change le bac de l'imprimante affectée à Excel à cet instant
' le nom du bac est indiqué en constante texte.
' il existe des valeurs standard pour les bacs, mais toutes les imprimantes
' ne suivent pas ces valeurs.
' D'autres macros sont inclues dans le module
' elles peuvent éventuellment être utilisées pour ce genre d'oprétaion.
' ATTENTION : le bac est changé mais pas toujours) pour l'application Excel en cours.
' Il semblerait que la prise en compte du changement de bac pour l'application
' (et donc le classeur) en cours dépende du fait que l'imprimante
' soit en réseau et encore plus si l'imprimante est partagée.

' une solution de contournement est de :
' 1 changer le bac
' 2 changer d'imprimante active (ça marche mieux avec une imprimante "sur nul:")
' 3 revenir à l'imprimante d'origine qui est donc sur le bac souhaité.
' Ça marche sur une installation, mais pas sur une autre (imprimante partagée avec W8 ?)
' idem Excel n'accepte
Private Const BacPourImprimer As String = "Réceptacle arrière"

' D'après : http://www.vbfrance.com/codes/CHANGER-PROPRIETES-IMPRIMANTE-COURS_7485.aspx
Private NomImpr As String
Private ModèleImpr As String
Private Port As String
Private TableauDesImprimantes() As String
Private NumImprimanteActive As Integer
Private NumImprimanteSurNul As Integer
Private NomsBacs() As String
Private NumerosBacs() As Integer
Private NumBacOrigine As Integer
Private appExcel As Excel.Application
Private Type PRINTER_DEFAULTS
    pDatatype As Long
    pDevMode As Long
    DesiredAccess As Long
End Type

Private Const HWND_BROADCAST = &HFFFF
Private Const WM_WININICHANGE = &H1A
Private Const CCHDEVICENAME = 32
Private Const CCHFORMNAME = 32
Private Const STANDARD_RIGHTS_REQUIRED = &HF0000
Private Const PRINTER_ACCESS_ADMINISTER = &H4
Private Const PRINTER_ACCESS_USE = &H8
Private Const PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or PRINTER_ACCESS_ADMINISTER Or PRINTER_ACCESS_USE)
Private Const PRINTER_ATTRIBUTE_DEFAULT = 4
Private Const DM_MODIFY = 8
Private Const DM_IN_BUFFER = DM_MODIFY
Private Const DM_COPY = 2
Private Const DM_OUT_BUFFER = DM_COPY
Private Const DMDUP_SIMPLEX = 1
Private Const DMDUP_VERTICAL = 2
Private Const DMDUP_HORIZONTAL = 3
Private Const DM_DUPLEX = &H1000&
Private Const DM_DEFAULTSOURCE = &H200
Private Const DM_ORIENTATION = &H1&
Private Const DM_PAPERLENGTH = &H4&
Private Const DM_PAPERSIZE = &H2&
Private Const DM_PAPERWIDTH = &H8&
Private Const DM_YRESOLUTION = &H2000&
Private Const DMORIENT_PORTRAIT = &H1&
Private Const DMORIENT_LANDSCAPE = &H2&
Private Const DMPAPER_A4 = 9                              '  A4 210 x 297 mm
Private Const DMPAPER_A5 = 11                            '  A5 148 x 210 mm
Private Const DMPAPER_ENV_C5 = 28                '  Envelope C5 162 x 229 mm
Private Const DMPAPER_ENV_DL = 27                '  Envelope DL 110 x 220mm

' Codes standards pour les bacs , pas toujours suivis.
' On ne s'en sert pas ici
Private Const DMBIN_ENVELOPE = 5
Private Const DMBIN_AUTO = 7
Private Const DMBIN_CASSETTE = 14
Private Const DMBIN_ENVMANUAL = 6
Private Const DMBIN_FORMSOURCE = 15
Private Const DMBIN_LARGECAPACITY = 11
Private Const DMBIN_LARGEFMT = 10  ' Grand format
Private Const DMBIN_LOWER = 2
Private Const DMBIN_MANUAL = 4
Private Const DMBIN_MIDDLE = 3
Private Const DMBIN_ONLYONE = 1
Private Const DMBIN_SMALLFMT = 9 ' petit fotmat
Private Const DMBIN_TRACTOR = 8
Private Const DMBIN_UPPER = 1
Private Const DMBIN_USER = 256

'http://msdn.microsoft.com/en-us/library/windows/desktop/dd183552(v=vs.85).aspx

'DC_BINNAMES Retrieves the names of the printer's paper bins.
' The pOutput buffer receives an array of string buffers.
' Each string buffer is 24 characters long and contains the name of a paper bin.
' The return value indicates the number of entries in the array.
' The name strings are null-terminated unless the name is 24 characters long.
' If pOutput is NULL, the return value is the number of bin entries required

Private Const DC_BINNAMES = 12
'DC_BINS Retrieves a list of available paper bins.
' The pOutput buffer receives an array of WORD values that indicate
' the available paper sources for the printer.
' The return value indicates the number of entries in the array.
' For a list of the possible array values, see the description of the
' dmDefaultSource member of the DEVMODE structure. If pOutput is NULL,
' the return value indicates the required number of entries in the array.

Private Const DC_BINS = 6  ' pour les bacs
'DC_PAPERNAMES Retrieves a list of supported paper names (for example, Letter or Legal).
' The pOutput buffer receives an array of string buffers.
' Each string buffer is 64 characters long and contains the name of a paper form.
' The return value indicates the number of entries in the array.
' The name strings are null-terminated unless the name is 64 characters long.
' If pOutput is NULL, the return value is the number of paper forms.

Private Const DC_PAPERNAMES = 16
'DC_PAPERS Retrieves a list of supported paper sizes.
' The pOutput buffer receives an array of WORD values that indicate
' the available paper sizes for the printer.
' The return value indicates the number of entries in the array.
' For a list of the possible array values, see the description
' of the dmPaperSize member of the DEVMODE structure. If pOutput is NULL,
' the return value indicates the required number of entries in the array

Private Const DC_PAPERS = 2
'DC_PAPERSIZE Retrieves the dimensions, in tenths of a millimeter,
' of each supported paper size.
' The pOutput buffer receives an array of POINT structures.
' Each structure contains the width (x-dimension) and length (y-dimension) of a paper size
' as if the paper were in the DMORIENT_PORTRAIT orientation.
' The return value indicates the number of entries in the array.

Private Const DC_PAPERSIZE = 3
Private Const PRINTER_ENUM_CONNECTIONS = &H4
Private Const PRINTER_ENUM_LOCAL = &H2

Private Type DEVMODE  ' Structure des paramètres de l'imprimante
    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
    dmLogPixels As Integer
    dmBitsPerPel As Long
    dmPelsWidth As Long
    dmPelsHeight As Long
    dmDisplayFlags As Long
    dmDisplayFrequency As Long
    dmICMMethod As Long        ' // Windows 9x seulement
    dmICMIntent As Long        ' // Windows 9x seulement
    dmMediaType As Long        ' // Windows 9x seulement
    dmDitherType As Long       ' // Windows 9x seulement
    dmReserved1 As Long        ' // Windows 9x seulement
    dmReserved2 As Long        ' // Windows 9x only
End Type

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)
Private Declare Function GetLastError Lib "kernel32" () As Long
Private Declare Function DeviceCapabilities Lib "winspool.drv" _
    Alias "DeviceCapabilitiesA" (ByVal lpsDeviceName As String, _
    ByVal lpPort As String, ByVal iIndex As Long, lpOutput As Any, _
    ByVal dev As Long) As Long

Private Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA" (ByVal pPrinterName As String, phPrinter As Long, pDefault As PRINTER_DEFAULTS) 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 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 ClosePrinter Lib "winspool.drv" (ByVal hPrinter 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 GetProfileStringA Lib "kernel32" (ByVal lpAppName As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long) As Long


Sub ChangeTrayAndPrint()
' selects the manual page tray and prints the selected sheet(s)
'RET.ARR {BACKSPACE} ou {BS}
'PAUSE {BREAK}
'VERR. MAJ {CAPSLOCK}
'EFFACEMENT {CLEAR}
'SUPPRIMER ou SUPPR. {DELETE} ou {DEL}
'BAS {DOWN}
'FIN {END}
'ENTRÉE (pavé numérique) {ENTER}
'ENTRÉE ~ (tilde)
'ÉCHAP {ESCAPE} or {ESC}
'AIDE {HELP}
'DÉBUT {HOME}
'INS {INSERT}
'GAUCHE {LEFT}
'VERR. NUM {NUMLOCK}
'PG.SUIV {PGDN}
'PG.PRÉC {PGUP}
'RETOUR {RETURN}
'DROITE {RIGHT}
'ARRÊT DÉFIL. {SCROLLLOCK}
'TABULATION {TAB}
'HAUT {UP}
'De F1 à F15 {F1} à {F15}



'Vous pouvez aussi spécifier des touches combinées avec MAJ et/ou CTRL et/ou ALT. Pour spécifier une combinaison de touches, utilisez le tableau suivant.

'Pour combiner une touche avec : Précédez le code de la touche de :
'ALT + (signe plus)
'CTRL ^ (signe d'insertion)
'MAJ % (signe de pourcentage)

    Application.Sendkeys "%fu%e{TAB}{DOWN}{DOWN}{TAB}m~~", True
End Sub



Private Function ChangePrinterSettings(ByRef pOldBin As Integer, ByVal pNewBin As Integer) As Boolean
'****************************************************************
'* Gestion des imprimantes                                                     *
'*---------------------------------------------------------------------------- *
'* Modification :                                                              *
'*        - du format du papier                                                *
'*  et/ou - de l'orientation                                                   *
'*  et/ou - du bac d'entrée                                                    *
'*******************************************************************************

    Dim hPrinter As Long           ' Handle de printer
    Dim pd As PRINTER_DEFAULTS
    Dim ret As Long, i As Long
    Dim TabInfos() As Long
    Dim TabInfosSizeNeed As Long   ' Taille du tableau nécessaire
    Dim NewDevMode As DEVMODE
    Dim pFullDevMode As Long
    Dim LastError As Long
    Dim rep As Long
    On Error GoTo ChangePrinterSettingsError
    
    ' Affecte les membres de PRINTER_DEFAULTS
    With pd
        .pDatatype = 0&
        .pDevMode = 0&
        .DesiredAccess = PRINTER_ALL_ACCESS
    End With
    'The OpenPrinter function retrieves a handle to the specified printer
    'or print server or other types of handles in the print subsystem.
    'Parameters
    'pPrinterName [in]
    '  A pointer to a null-terminated string that specifies the name
    '  of the printer or print server, the printer object, the XcvMonitor, or the XcvPort.
    '  For a printer object use: PrinterName, Job xxxx. For an XcvMonitor, use: ServerName, XcvMonitor MonitorName.
    '  For an XcvPort, use: ServerName, XcvPort PortName.
    '  If NULL, it indicates the local printer server.
    'phPrinter [out]
    '  A pointer to a variable that receives a handle (not thread safe)
    '  to the open printer or print server object.
    '  The phPrinter parameter can return an Xcv handle for use with the XcvData function.
    ' For more information about XcvData, see the DDK.
    'pDefault [in]
    '   A pointer to a PRINTER_DEFAULTS structure. This value can be NULL.
    'The PRINTER_DEFAULTS structure specifies the default data type, environment,
    ' initialization data, and access rights for a printer.

'Members
'pDatatype
'  Pointer to a null-terminated string that specifies the default data type for a printer.

'pDevMode
' Pointer to a DEVMODE structure that identifies the default environment and initialization data for a printer.

'DesiredAccess
'  Specifies desired access rights for a printer. The OpenPrinter function uses this member to set access rights to the printer. These rights can affect the operation of the SetPrinter and DeletePrinter functions. The access rights can be one of the following.

' Value Meaning
'  PRINTER_ACCESS_ADMINISTER To perform administrative tasks, such as those provided by SetPrinter.
'  PRINTER_ACCESS_USE To perform basic printing operations.
'  PRINTER_ALL_ACCESS To perform all administrative tasks and basic printing operations
'  except for SYNCHRONIZE (see Standard Access Rights ).
'  generic security values, such as WRITE_DAC To allow specific control access rights. See Standard Access Rights.

    'Return value
    ' If the function succeeds, the return value is a nonzero value.
    ' If the function fails, the return value is zero.
    
    ' Fournit un handle vers l'imprimante ModèleImpr
    ret = OpenPrinter(ModèleImpr, hPrinter, pd)
    
    ' Echec de l'ouverture de l'imprimante
    If ret = False Then
        'pb de droits sur l'imprimante : on rééssaye avec des droits de simple utilisateur (Win NT/2000/XP seulement)
        pd.DesiredAccess = (STANDARD_RIGHTS_REQUIRED Or PRINTER_ACCESS_USE)
        ret = OpenPrinter(ModèleImpr, hPrinter, pd)
        If ret = False Then
          LastError = GetLastError()
            Select Case LastError
                Case 0 To 6
                Case 1722
                    MsgBox "Votre imprimante par défaut est hors connexion. Si c'est une imprimante réseau, vérifier que le poste qui y est rattaché est bien allumé", vbExclamation
                    Exit Function
                Case Else
                    MsgBox "Erreur de l'API OpenPrinter Code: " & LastError
                    Exit Function
            End Select
        End If
    End If
    'Paramètres de GetPrinter
    '· hPrinter
    '[in] Handle to the printer for which the function retrieves information.
    'Use the OpenPrinter or AddPrinter function to retrieve a printer handle.
    '
    '· Level
    '[in] Specifies the level or type of structure that the function stores into the buffer pointed to by pPrinter.
    'Windows 95/98/Me: This value can be 1, 2, or 5.
    'Windows NT/2000/XP: This value can be 1, 2, 3, 4, 5, 7, 8 or 9.
    '
    '· pPrinter
    '[out] Pointer to a buffer that receives a structure containing information about the specified printer.
    ' The buffer must be large enough to receive the structure and any strings
    ' or other data to which the structure members point.
    ' If the buffer is too small, the pcbNeeded parameter returns the required buffer size.
    'The type of structure is determined by the value of Level.
    '1
    'A PRINTER_INFO_1 structure containing general printer information.
    '2
    'A PRINTER_INFO_2 structure containing detailed information about the printer.
    '3
    'Windows NT/2000/XP: A PRINTER_INFO_3 structure containing the printer's security information.
    '4
    'Windows NT/2000/XP: A PRINTER_INFO_4 structure containing minimal printer information,
    ' including the name of the printer, the name of the server, and whether the printer is remote or local.
    '5
    'A PRINTER_INFO_5 structure containing printer information such as printer attributes and time-out settings.
    '7
    'Windows 2000/XP: A PRINTER_INFO_7 structure that indicates whether
    ' the printer is published in the directory service.
    '8
    'Windows 2000/XP: A PRINTER_INFO_8 structure specifying the global default printer settings.
    '9
    'Windows 2000/XP: A PRINTER_INFO_9 structure specifying the per-user default printer settings.
    '
    '· cbBuf
    '[in] Specifies the size, in bytes, of the buffer pointed to by pPrinter.
    '
    '· pcbNeeded
    '[out] Pointer to a variable that the function sets to the size, in bytes,
    ' of the printer information.
    ' If cbBuf is smaller than this value, GetPrinter fails, and the value represents the required buffer size.
    ' If cbBuf is equal to or greater than this value, GetPrinter succeeds,
    ' and the value represents the number of bytes stored in the buffer.
    '
    'Return Values
    'If the function succeeds, the return value is nonzero.
    'If the function fails, the return value is zero.
    '  To get extended error information, call GetLastError.
    
    ret = GetPrinter(hPrinter, 2, ByVal 0&, 0, TabInfosSizeNeed)
    ' Pas de vérification de GetLastError ici (normalement -> échec avec une erreur 122 - ERROR_INSUFFICIENT_BUFFER)
    LastError = GetLastError()
    ' Redimensionnement de TabInfos selon les besoins
    ReDim TabInfos((TabInfosSizeNeed \ 4))
    ' ... appel à GetPrinter() pour la récupération des infos
    ret = GetPrinter(hPrinter, 2, TabInfos(0), TabInfosSizeNeed, TabInfosSizeNeed)
    'Erreur de GetPrinter : erreurs 0, 6, 1722 ignorées
    ' (6 résultant d'un pb de droits utilisateur, 1722, d'une imprimante hors connexion)
     LastError = GetLastError()
    If ret = False Then  ' Ret = 0
      'LastError = GetLastError()
        Select Case LastError
            Case 0 To 6
            Case 1722
              MsgBox "Votre imprimante par défaut est hors connexion. Si c'est une imprimante réseau, vérifier que le poste qui y est rattaché est bien allumé", vbExclamation
                Exit Function
            Case Else
                MsgBox "Erreur de l'API GetPrinter Code: " & LastError
                Exit Function
        End Select
    End If
    'Extrait de la structure PRINTER_INFO2 la portion DEVMODE
    pFullDevMode = TabInfos(7)
    'Copie la portion pointée dans la sous-structure NewDevMode créée à cet effet
    Call CopyMemory(NewDevMode, ByVal pFullDevMode, Len(NewDevMode))
    'Changement de bac
    With NewDevMode
      'Bac
      If pNewBin > 0 Then  'And pNewBin < 5 Then
          pOldBin = .dmDefaultSource
          .dmDefaultSource = pNewBin
      End If
    End With
    ' Mise à jour des infos dans la structure propre à l'imprimante
    ' Recopie des valeurs changées dans les anciennes
    Call CopyMemory(ByVal pFullDevMode, NewDevMode, Len(NewDevMode))
    'http://msdn.microsoft.com/en-us/library/windows/desktop/dd183576(v=vs.85).aspx
    ' The DocumentProperties function retrieves or modifies printer initialization information
    ' or displays a printer-configuration property sheet for the specified printer.

'Syntax
'Parameters
'hwnd [in]
'A handle to the parent window of the printer-configuration property sheet.

'hPrinter [in]
'A handle to a printer object. Use the OpenPrinter or AddPrinter function to retrieve a printer handle.

'pDeviceName [in]
'A pointer to a null-terminated string that specifies the name of the device for which the printer-configuration property sheet is displayed.

'pDevModeOutput [out]
'A pointer to a DEVMODE structure that receives the printer configuration data specified by the user.

'pDevModeInput [in]
'A pointer to a DEVMODE structure that the operating system uses to initialize the property sheet controls.

'This parameter is only used if the DM_IN_BUFFER flag is set in the fMode parameter.
' If DM_IN_BUFFER is not set, the operating system uses the printer's default DEVMODE.

'fMode [in]
'The operations the function performs. If this parameter is zero,
'the DocumentProperties function returns the number of bytes required
'by the printer driver's DEVMODE data structure.
'Otherwise, use one or more of the following constants to construct a value
'for this parameter; note, however, that in order to change the print settings,
'an application must specify at least one input value and one output value.

'Value Meaning
'DM_IN_BUFFER Input value. Before prompting, copying,
'or updating, the function merges the printer driver's current print settings
'with the settings in the DEVMODE structure specified by the pDevModeInput parameter.
'The function updates the structure only for those members specified by the DEVMODE structure's dmFields member.
'This value is also defined as DM_MODIFY. In cases of conflict during the merge,
'the settings in the DEVMODE structure specified by pDevModeInput
'override the printer driver's current print settings.
 
'DM_IN_PROMPT Input value. The function presents the printer driver's
'Print Setup property sheet and then changes the settings in the printer's
'DEVMODE data structure to those values specified by the user. This value is also defined as DM_PROMPT.
 
'DM_OUT_BUFFER Output value.
'The function writes the printer driver's current print settings, including private data,
'to the DEVMODE data structure specified by the pDevModeOutput parameter.
'The caller must allocate a buffer sufficiently large to contain the information.
'If the bit DM_OUT_BUFFER sets is clear, the pDevModeOutput parameter can be NULL.
'This value is also defined as DM_COPY.
 
'Return value
'If the fMode parameter is zero, the return value is the size of the buffer
' required to contain the printer driver initialization data.
' Note that this buffer can be larger than a DEVMODE structure if the printer
' driver appends private data to the structure.

'If the function displays the property sheet,
' the return value is either IDOK or IDCANCEL, depending on which button the user selects.

'If the function does not display the property sheet and is successful, the return value is IDOK.

'If the function fails, the return value is less than zero.

' Remarks
'Note  This is a blocking or synchronous function and might not return immediately.
 ' How quickly this function returns depends on run-time factors such as network status,
 ' print server configuration, and printer driver implementation—factors
 ' that are difficult to predict when writing an application.
 ' Calling this function from a thread that manages interaction with the user interface
 ' could make the application appear to be unresponsive.

'The string pointed to by the pDeviceName parameter can be obtained
' by calling the GetPrinter function.

'The DEVMODE structure actually used by a printer driver contains
' the device-independent part (as defined above) followed by a driver-specific part
' that varies in size and content with each driver and driver version.
' Because of this driver dependence, it is very important for applications
' to query the driver for the correct size of the DEVMODE structure before allocating a buffer for it.

'To make changes to print settings that are local to an application, an application should follow these steps:

'1.Get the number of bytes required for the full DEVMODE structure
' by calling DocumentProperties and specifying zero in the fMode parameter.
'2.Allocate memory for the full DEVMODE structure.
'3.Get the current printer settings by calling DocumentProperties.
' Pass a pointer to the DEVMODE structure allocated in Step 2 as
' the pDevModeOutput parameter and specify the DM_OUT_BUFFER value.
'4.Modify the appropriate members of the returned DEVMODE structure and indicate
' which members were changed by setting the corresponding bits in the dmFields member of the DEVMODE.
'5.Call DocumentProperties and pass the modified DEVMODE structure back as both
' the pDevModeInput and pDevModeOutput parameters and specify both
' the DM_IN_BUFFER and DM_OUT_BUFFER values (which are combined using the OR operator).
' The DEVMODE structure returned by the third call to DocumentProperties can be used
' as an argument in a call to the CreateDC function.

'To create a handle to a printer-device context using the current printer settings,
'you only need to call DocumentProperties twice, as described above.
'The first call gets the size of the full DEVMODE and the second call initializes
'the DEVMODE with the current printer settings.
'Pass the initialized DEVMODE to CreateDC to obtain the handle to the printer device context.
    
    ret = DocumentProperties(0&, hPrinter, NomImpr, ByVal pFullDevMode, ByVal pFullDevMode, DM_IN_BUFFER Or DM_OUT_BUFFER)
    
    ' Mise à jour des propriétés (Windows) de l'imprimante
    ' http://support.microsoft.com/kb/140285/fr
    'Les paramètres de SetPrinter() sont comme suit :
      'hPrinter
      'Le premier paramètre est un handle vers l'imprimante dont les paramètres doivent être modifiés.
      'Cela doit être récupéré à partir de OpenPrinter().
      'dwLevel
      'Le deuxième paramètre spécifie la structure des données passées à SetPrinter().
      'Pour Windows 95, cela peut être 0, 2, 3, 4 ou 5.
      'Pour Windows NT, cela peut être 0, 2 ou 3.
      'Ces chiffres correspondent au type de données (PRINTER_INFO_n) transmis via le troisième paramètre.
      'lpbPrinter
      'Le troisième paramètre est une structure PRINTER_INFO_n où n correspond au nombre dans le second paramètre.
      'Cette structure peut entraîner une certaine confusion,
      'car il n'est pas simplement une mémoire tampon de la taille de la structure.
      'Ces structures contiennent des informations indépendantes du périphérique,
      'mais sont immédiatement suivis en mémoire une quantité variable d'informations dépendants de périphériques,
      'qui sont fournies par le pilote de périphérique.
      'Par conséquent, un peu de travail est impliqué pour déterminer quelle taille cette mémoire tampon doit être.
      'Pour cela, appel GetPrinter(), qui définira pcbNeeded à la taille totale nécessaire.
      '
      'En outre, la mémoire tampon a généralement une grande quantité d'informations indépendantes du périphérique
      'et dépendants de périphériques qu'il contient.
      'Votre application ne va pas à connaître ou intéressent les valeurs dans la plupart de ces membres de structure.
      'Par conséquent, lorsque vous apportez les modifications qui vous intéressez,
      'vous devez brancher dans les valeurs correctes pour tous ces autres portions de données.
      'Ces autres portions de données sont définies lorsque vous appelez GetPrinter() une deuxième fois.
      'dwCommand
      'Le quatrième paramètre est utilisé pour suspendre l'impression, de reprendre l'impression
      'ou de désactiver tous les travaux d'impression.
      'Cela est généralement pas utilisé en même temps que lpbPrinter est utilisé.
      'Cet article concerne pas la définition de l'état d'imprimante,
      'afin que l'exemple de code définit ce paramètre sur zéro.

    ret = SetPrinter(hPrinter, 2, TabInfos(0), 0&)
    'Erreur de SetPrinter : erreurs 0, 6, 1722 ignorées (6 résultant d'un pb de droits utilisateur, 1722, d'une imprimante hors connexion)
    If ret = False Then
        LastError = GetLastError()
        Select Case LastError
            Case 0 To 6

            Case 1722
                MsgBox "Votre imprimante par défaut est hors connexion. Si c'est une imprimante réseau, vérifier que le poste qui y est rattaché est bien allumé", vbExclamation
                Exit Function
            Case Else
                MsgBox "Erreur de l'API GetPrinter Code: " & LastError
                Exit Function
        End Select
    End If
    
    ' Fermeture du handler de l'imprimante
    ClosePrinter (hPrinter)
    ChangePrinterSettings = True
    Exit Function
    
ChangePrinterSettingsError:
    Select Case Err.Number
        Case 9
            'Err 9 : Débordement de tableau causé par un gestionnaire d'imprimante absent ou non accessible
            Exit Function
        Case 484
            ' Problème pour obtenir des informations du gestionnaire d'imprimante du système.
            '  Assurez-vous que le gestionnaire d'imprimante est installé correctement
            'Err 484 : Pb d'accès au gestionnaire d'imprimante
            Exit Function
        
        Case Else
            rep = MsgBox("Erreur : " & Err.Number & "(" & Err.Description & ")" & vbCrLf & "Continuez l'exécution de cette procédure ?", vbQuestion + vbYesNo, "APIs.BAS[ChangePrinterSettings]")
            If rep = vbYes Then Resume Next Else Exit Function
    End Select
End Function

Private Function CodeBacSel() As Integer
' Retourne le code du bac sélectionné
    Dim hPrinter As Long           ' Handle de l'imprimante
    Dim pd As PRINTER_DEFAULTS
    Dim ret As Long, i As Long
    Dim TabInfos() As Long
    Dim TabInfosSizeNeed As Long   ' Taille du tableau nécessaire
    Dim NewDevMode As DEVMODE
    Dim pFullDevMode As Long
    Dim LastError As Long
    Dim rep As Long
    'On Error GoTo ChangePrinterSettingsError
    
    ' Affecte les membres de PRINTER_DEFAULTS
    With pd
        .pDatatype = 0&
        .pDevMode = 0&
        .DesiredAccess = PRINTER_ALL_ACCESS
    End With
    
    ' Fournit un hPrinter si l'imprimante est ccessible
    ret = OpenPrinter(ModèleImpr, hPrinter, pd)
    ' Echec de l'ouverture de l'imprimante
    If ret = False Then
        'pb de droits sur l'imprimante : on rééssaye avec des droits de simple utilisateur (Win NT/2000/XP seulement)
        pd.DesiredAccess = (STANDARD_RIGHTS_REQUIRED Or PRINTER_ACCESS_USE)
        ret = OpenPrinter(ModèleImpr, hPrinter, pd)
        If ret = False Then
          LastError = GetLastError()
          Select Case LastError
                Case 0 To 6
                Case 1722
                    MsgBox "Votre imprimante par défaut est hors connexion. Si c'est une imprimante réseau, vérifier que le poste qui y est rattaché est bien allumé", vbExclamation
                    Exit Function
                Case Else
                    MsgBox "Erreur de l'API OpenPrinter Code: " & LastError
                    Exit Function
            End Select
        End If
    End If
    ' Recherche le bac sélectionné
     ' ... appel à GetPrinter() pour la récupération des infos
     ret = GetPrinter(hPrinter, 2, ByVal 0&, 0, TabInfosSizeNeed)
    ' Pas de vérification de GetLastError ici (normalement -> échec avec une erreur 122 - ERROR_INSUFFICIENT_BUFFER)

    ' Redimensionnement de TabInfos selon les besoins
    ReDim TabInfos((TabInfosSizeNeed \ 4))
    ' ... appel à GetPrinter() pour la récupération des infos
    ret = GetPrinter(hPrinter, 2, TabInfos(0), TabInfosSizeNeed, TabInfosSizeNeed)

    'Erreur de GetPrinter : erreurs 0, 6, 1722 ignorées
    ' (6 résultant d'un pb de droits utilisateur, 1722, d'une imprimante hors connexion)
    If ret = False Then
        LastError = GetLastError()
          Select Case LastError
            Case 0 To 6
            Case 1722
                MsgBox "Votre imprimante par défaut est hors connexion. Si c'est une imprimante réseau, vérifier que le poste qui y est rattaché est bien allumé", vbExclamation
                Exit Function
            Case Else
                MsgBox "Erreur de l'API GetPrinter Code: " & LastError
                Exit Function
        End Select
    End If

    'Extrait de la structure PRINTER_INFO2 la portion DEVMODE
    pFullDevMode = TabInfos(7)
    
    'Copie la portion pointée dans la sous-structure créée à cet effet
    Call CopyMemory(NewDevMode, ByVal pFullDevMode, Len(NewDevMode))
    CodeBacSel = NewDevMode.dmDefaultSource
  
    ' Fermeture du handler de l'imprimante
    ClosePrinter (hPrinter)
    Exit Function
    
ChangePrinterSettingsError:
    Select Case Err.Number
        Case 9
            'Err 9 : Débordement de tableau causé par un gestionnaire d'imprimante absent ou non accessible
            Exit Function
        Case 9, 484
            'Err 484 : Pb d'accès au gestionnaire d'imprimante
            Exit Function
        Case Else
            rep = MsgBox("Erreur : " & Err.Number & "(" & Err.Description & ")" & vbCrLf & "Continuez l'exécution de cette procédure ?", vbQuestion + vbYesNo, "APIs.BAS[ChangePrinterSettings]")
            If rep = vbYes Then Resume Next Else Exit Function
    End Select
    CodeBacSel = 0
End Function

Private Sub InventaireBacsImpr()
' Mémorise les noms et les codes des bacs de l'imprimante NomImpr
' http://support.microsoft.com/kb/194789
   'Dim Port As String
   Dim NbBacs As Long
   Dim ct As Long
   Dim ListeDesNoms As String
   Dim ch1 As String
   Dim ch2 As String
     ' le paramètre dc_bins permet d'obtenir le nombre de bacs de papier
     NbBacs = DeviceCapabilities(ModèleImpr, Port, DC_BINS, ByVal vbNullString, 0)
     If NbBacs <= 0 Then
       MsgBox "L'imprimante : '" & ModèleImpr & "' n'a pas de bacs.", vbCritical
       End
     End If
     
     ReDim NomsBacs(1 To NbBacs)
     ReDim NumerosBacs(1 To NbBacs)
     ListeDesNoms = String(24 * NbBacs, 0)
     'Récupère les codes numériques des bacs
     NbBacs = DeviceCapabilities(ModèleImpr, Port, DC_BINS, NumerosBacs(1), 0)
     ' Récupère les noms des bacs
     NbBacs = DeviceCapabilities(ModèleImpr, Port, DC_BINNAMES, ByVal ListeDesNoms, 0)
     ' les noms et les codes des bacs sont placés dans les tableau Bacs
     For ct = 1 To NbBacs
       ch1 = Mid(ListeDesNoms, 24 * (ct - 1) + 1, 24)
       ch2 = Left(ch1, InStr(1, ch1, Chr(0)) - 1)
       ' Si on souhaite connaitre les codes et les noms des bacs, activer la ligne suivante.
       ' Debug.Print NumerosBacs(ct), "'" & ch2 & "'"
       NomsBacs(ct) = ch2
     Next ct
End Sub

Private Sub ChoixBacT(nomBac As String)
' Sélectionner le bac dont le nom est passé en paramètre
Dim i As Integer
Dim Ancien As Integer
Dim Numbac As Integer
  InventaireBacsImpr
  ' Recherche du nom dans la liste des noms de bacs
  Numbac = 0
  For i = 1 To UBound(NomsBacs)
    If UCase(NomsBacs(i)) = UCase(nomBac) Then Numbac = i
  Next
  If Numbac = 0 Then
    MsgBox "Imprimante : " & NomImpr & "  Nom de bac inexistant : " & nomBac, vbCritical
    End
  Else
    'Debug.Print "SP ChoixBac Bac trouvé : " & Numbac & " / " & NomsBacs(Numbac)
  End If
  ' Retourne le code du bac précédemment affecté dans : Ancien (ne sert pas)
  ' et affecte le nouveau bac
  Call ChangePrinterSettings(Ancien, NumerosBacs(Numbac))
End Sub

Private Sub ChoixBacN(N As Integer)
' Sélectionner le bac dont le code est passé en paramètre
Dim Ancien As Integer
  Call ChangePrinterSettings(Ancien, N)
End Sub

Sub ChangerBac()
Dim Classeur As Excel.Workbook
Dim Feuille As Excel.Worksheet
Dim DocPub As Publisher.Document
Dim p As Integer
Dim k As Integer
Dim Numbac As Integer

Set appExcel = Application
' Imprimante par défaut
Set Classeur = appExcel.ActiveWorkbook
Set Feuille = appExcel.ActiveSheet
NomImpr = appExcel.ActivePrinter
' séparer modèle et port sous la forme " sur unité"
p = InStr(NomImpr, " sur ")
If p <= 0 Then
  ModèleImpr = NomImpr
  Port = "LPT1:"
Else
  ModèleImpr = Left(NomImpr, p - 1)
  Port = Mid(NomImpr, p + 5)
  Port = Trim(Port)
End If
'rempli le tableau imprimantes et ports et place le numéro de l'imprimante activeInventaireBacsImpr
ListeDesImprimantes
ChoixBacT (BacPourImprimer)
' Le bac par défaut de l'imprimante Windows est modifié
' mais pas pour l'application.

End Sub

Private Function ListeDesImprimantes()
' Renseigne : TableauDesImprimantes
' et la numéro d'ordre de la première imprimante sur nul:
' est mémorisé
' ainsi que le numéro de l'imprimante active.

Dim WshNetwork As Object
Dim CollImpr As Object
Dim i As Integer
Dim j As Integer
Set WshNetwork = CreateObject("WScript.Network")
Set CollImpr = WshNetwork.EnumPrinterConnections
ReDim TableauDesImprimantes(CollImpr.Length / 2)
j = 1
NumImprimanteActive = 0
NumImprimanteSurNul = 0
For i = 1 To CollImpr.Count - 1 Step 2
  TableauDesImprimantes(j) = CollImpr.Item(i) & " sur " & CollImpr.Item(i - 1)
  If CollImpr.Item(i) = ModèleImpr Then NumImprimanteActive = j
  If CollImpr.Item(i - 1) = "nul:" Then NumImprimanteSurNul = j
  Debug.Print " Liste des imprimantes : " & j & " " & TableauDesImprimantes(j)
  j = j + 1
Next
 If NumImprimanteSurNul > 0 Then
  Debug.Print " imprimante sur nul " & NumImprimanteSurNul & " " & TableauDesImprimantes(NumImprimanteSurNul)
 End If
If NumImprimanteActive > 0 Then
  Debug.Print " imprimante active: " & NumImprimanteActive & " " & TableauDesImprimantes(NumImprimanteActive)
End If
ListeDesImprimantes = CollImpr.Count
End Function



