Results 1 to 8 of 8

Thread: [RESOLVED] Using the EnumPrinters API call in VBA7 64-bit

  1. #1

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,937

    Resolved [RESOLVED] Using the EnumPrinters API call in VBA7 64-bit

    Yeah, I know that this isn't the exact right place to post this, and I do apologize. It's just that this forum is about 10 times more active than the VBA forum. If it gets moved, hey ho.

    I'm looking for a working example that uses EnumPrinters under VBA7 64-bit. I've got VB6 code I've used for years, but 64-bit requires a change to the API declarations. Also, sadly, the VBA doesn't have the "Printers" collection which forces me to do it the hard (API) way.

    Since I'm actually using Excel through VB6 automation, one solution I'm toying with is to just get the list in VB6, and then send it into Excel via the xls.Application.Run feature. However, this feels sloppy. It'd be nice to just do it directly from an Excel macro.

    The three API calls that are involved (or, at least, the three I use) are:

    Code:
    Private Declare Function EnumPrinters Lib "winspool.drv" Alias "EnumPrintersA" (ByVal Flags As Long, ByVal Name As String, ByVal Level As Long, pPrinterEnum As Long, ByVal cdBuf As Long, pcbNeeded As Long, pcReturned As Long) As Long
    Private Declare Function PtrToStr Lib "kernel32" Alias "lstrcpyA" (ByVal RetVal As String, ByVal Ptr As Long) As Long
    Private Declare Function StrLen Lib "kernel32" Alias "lstrlenA" (ByVal ptr As Long) As Long
    An array of pointers to printer names is returned, and pointers are now 64 bit, so it's a bit complex.

    Any help (or a VBA example using EnumPrinters) on this would be greatly appreciated.

    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  2. #2
    Hyperactive Member
    Join Date
    Sep 2014
    Posts
    341

    Re: Using the EnumPrinters API call in VBA7 64-bit

    Dear Elroy. Thinking that you are practicing 64bit vba, can you help me out a bit on this?

  3. #3
    PowerPoster
    Join Date
    Dec 2004
    Posts
    25,618

    Re: Using the EnumPrinters API call in VBA7 64-bit

    have a look at
    https://social.msdn.microsoft.com/Fo...y?forum=isvvba

    there is an api example, that possibly could be made to work

    but the solution link provided does not truly enumerate the printers, just gets them from the registry

    there is also some useful information
    http://www.jkp-ads.com/articles/apideclarations.asp
    i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
    Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next

    dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part

    come back and mark your original post as resolved if your problem is fixed
    pete

  4. #4
    Frenzied Member Gruff's Avatar
    Join Date
    Jan 2014
    Location
    Scappoose Oregon USA
    Posts
    1,293

    Re: Using the EnumPrinters API call in VBA7 64-bit

    The method in this link reportedly works in both 32 and 64 bit VBA.

    BTW are you using VBA7 conditional 32 / 64 bit testing in your code?
    Code:
    #If Vba7 Then 
         ' Code is running in  32-bit or 64-bit VBA7. 
         #If Win64 Then 
              ' Code is running in 64-bit VBA7. 
         #Else 
              ' Code is not running in 64-bit VBA7. 
         #End If 
    #Else 
         ' Code is NOT running in 32-bit or 64-bit VBA7. 
    #End If
    Burn the land and boil the sea
    You can't take the sky from me


    ~T

  5. #5

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,937

    Re: Using the EnumPrinters API call in VBA7 64-bit

    Well, I think I've worked it out. I managed to get rid of the API calls for getting the printer list. WestConn1, I had already found your code, but I liked the code below a bit better.

    Code:
    Private Function ListPrintersNew() As String()
        Dim owshNetwork As Object   'Network Port/Name Identifier collection
        Dim oPrinters As Object     'Printer Port/Name Identifier collection
        Dim i As Integer
        Dim StrPrinters() As String
        '
        Set owshNetwork = CreateObject("WScript.Network")
        Set oPrinters = owshNetwork.EnumPrinterConnections
        '
        ' Set the size of the Printer NAMES array
        ReDim StrPrinters(0 To oPrinters.Count \ 2 - 1)
        '
        ' Load this array element with the Name from the list
        For i = 0 To UBound(StrPrinters)
            StrPrinters(i) = oPrinters.Item(i * 2 + 1)
            'Debug.Print "'"; oPrinters.Item(i * 2 + 1); "'"
        Next i
        ListPrintersNew = StrPrinters
    End Function
    However, I still had the problem in that I wanted to change the default printer, which does take API calls. Here's what I came up with:

    Code:
    Option Explicit
    '
    #If (VBA7 And Win64) Then
        Private Declare PtrSafe Function GetProfileString Lib "kernel32" Alias "GetProfileStringA" (ByVal lpAppName As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long) As Long ' Return isn't handle.
        Private Declare PtrSafe Function WriteProfileString Lib "kernel32" Alias "WriteProfileStringA" (ByVal lpszSection As String, ByVal lpszKeyName As String, ByVal lpszString As String) As Long ' Return isn't handle.
        Private Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As String) As LongPtr
    #Else
        Private Declare Function GetProfileString Lib "kernel32" Alias "GetProfileStringA" (ByVal lpAppName As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long) As Long
        Private Declare Function WriteProfileString Lib "kernel32" Alias "WriteProfileStringA" (ByVal lpszSection As String, ByVal lpszKeyName As String, ByVal lpszString As String) As Long
        Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As String) As Long
    #End If
    '
    Private Const HWND_BROADCAST = &HFFFF
    Private Const WM_WININICHANGE = &H1A
    '
    
    Public Function SetPrinterAsDefault(ByVal DeviceName As String) As Boolean
        Dim buffer As String
        Dim r As Long
        Dim sPrinterDevName As String
        Dim DriverName As String
        Dim PrinterPort As String
        Dim DeviceLine As String
        Dim l As Long
        Dim iDriver As Integer
        Dim iPort As Integer
        '
        ' Set up for changing the default.
        buffer = Space(8192)
        r = GetProfileString("windows", "Device", "", buffer, Len(buffer))
        If r <> 0 Then
            'Remove the wasted space
            buffer = Mid(buffer, 1, r)
            'Store the current default printer before we change it
            sPrinterDevName = Mid(buffer, 1, InStr(buffer, ",") - 1)
            'm_sPrevPrinterDriver = Mid(Buffer, InStr(Buffer, ",") + 1, InStrRev(Buffer, ",") - InStr(Buffer, ",") - 1)
            'm_sPrevPrinterPort = Mid(Buffer, InStrRev(Buffer, ",") + 1)
        End If
        '
        'If its not currently set as the default then set it...
        If sPrinterDevName <> DeviceName Then
            buffer = Space(1024)
            r = GetProfileString("PrinterPorts", DeviceName, "", buffer, Len(buffer))
            '
            ' Parse the driver name and port name out of the buffer
            DriverName = ""
            PrinterPort = ""
        
            'The driver name is first in the string terminated by a comma
            iDriver = InStr(buffer, ",")
            If iDriver > 0 Then
                'Strip out the driver name
                DriverName = Left(buffer, iDriver - 1)
                'The port name is the second entry after the driver name separated by commas.
                iPort = InStr(iDriver + 1, buffer, ",")
                If iPort > 0 Then
                    'Strip out the port name
                    PrinterPort = Mid(buffer, iDriver + 1, iPort - iDriver - 1)
                End If
            End If
            '
            If DriverName <> "" And PrinterPort <> "" Then
                DeviceLine = DeviceName & "," & DriverName & "," & PrinterPort
                'Store the new printer information in the [WINDOWS] section of the WIN.INI file for the DEVICE= item
                r = WriteProfileString("windows", "Device", DeviceLine)
                If r Then
                    'Cause all applications to reload the INI file:
                    SendMessage HWND_BROADCAST, WM_WININICHANGE, 0, "windows"
                    SetPrinterAsDefault = True
                Else
                    SetPrinterAsDefault = False
                End If
            Else
                SetPrinterAsDefault = False
            End If
        Else
            SetPrinterAsDefault = True
        End If
    End Function
    And Gruff, yeah, I spotted the VBA7 and Win64 flags pretty early in my research.

    bPrice, I'll take a look at yours now.

    Thanks to Everyone,
    Elroy

    p.s. I think I'll mark this one as resolved.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  6. #6
    PowerPoster
    Join Date
    Dec 2004
    Posts
    25,618

    Re: [RESOLVED] Using the EnumPrinters API call in VBA7 64-bit

    can the setdefaultprinter API work in x64?

    or here is something else i found, that could incorporate into your owshNetwork
    Code:
    Option Explicit
    Dim objNetwork, strUNCPrinter
    strUNCPrinter = "\\LittleServer\HP LaserJet 2420"
    Set objNetwork = CreateObject("WScript.Network")
    objNetwork.AddWindowsPrinterConnection strUNCPrinter
    
    ' Here is where we set the default printer to strUNCPrinter
    objNetwork.SetDefaultPrinter strUNCPrinter
    WScript.Echo "Check the Printers folder for : " & strUNCPrinter
    http://www.computerperformance.co.uk...nter_Bonus.htm
    i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
    Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next

    dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part

    come back and mark your original post as resolved if your problem is fixed
    pete

  7. #7

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,937

    Re: [RESOLVED] Using the EnumPrinters API call in VBA7 64-bit

    Hey westconn1,

    I'm actually not sure if the "set printer default" will work on 64-bit systems or not. However, just last night, I killed it from my VBA macros. I figured out that the Excel VBA .PrintOut method has an optional argument whereby the printer can be specified. This, along with the ListPrintersNew code I provided above, solved all my problems. At least for this section of my VBA macros, I eliminated all of my API calls, which makes it identical code for both the 32-bit and 64-bit environments.

    I do have a couple of API calls I'm using in other areas, but I think I've got them worked out as well with respect to the PtrSafe and LongPtr stuff.

    Thanks though,
    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  8. #8
    PowerPoster
    Join Date
    Dec 2004
    Posts
    25,618

    Re: [RESOLVED] Using the EnumPrinters API call in VBA7 64-bit

    VBA .PrintOut method has an optional argument whereby the printer can be specified.
    not in older versions
    but you have always been able to change /set the application.activeprinter for the current session
    Code:
    activeprinter = printername on portname
    i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
    Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next

    dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part

    come back and mark your original post as resolved if your problem is fixed
    pete

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width