Link to home
Start Free TrialLog in
Avatar of Jeff Tennessen
Jeff TennessenFlag for United States of America

asked on

Problem with AddPortEx API call in VBA (Access) vs. VB

I have a weird problem. I use the following code to change the port to which a particular printer is attached:

    Private Type PORT_INFO_1
      pPortName As String
    End Type
   
    Private Type PRINTER_DEFAULTS
      pDatatype     As String
      pDevMode      As Long
      DesiredAccess As Long
    End Type
   
    Private Const STANDARD_RIGHTS_REQUIRED = &HF0000
    Private Const PRINTER_ACCESS_USE = &H8
    Private Const PRINTER_ACCESS_ADMINISTER = &H4
    Private Const PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or _
                                        PRINTER_ACCESS_ADMINISTER Or _
                                        PRINTER_ACCESS_USE)
    Private Const ERROR_INSUFFICIENT_BUFFER = 122
   
    Private Declare Function AddPortEx _
      Lib "winspool.drv" _
      Alias "AddPortExA" ( _
      ByVal pName As String, _
      ByVal pLevel As Long, _
      lpBuffer As Any, _
      ByVal pMonitorName As String) 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 Long, _
      ByVal cbBuf As Long, _
      pcbNeeded As Long) As Long
    Private Declare Function lstrcpy _
      Lib "kernel32" _
      Alias "lstrcpyA" ( _
      ByVal lpString1 As Long, _
      ByVal lpString2 As String) 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 Long, _
      ByVal Command As Long) As Long
   
    Public Function SetPrinterPort(pstrPrinterName As String, _
                                   pstrPortName As String) As Boolean
   
      Dim pi1        As PORT_INFO_1
      Dim fRet       As Boolean
      Dim alngBuf()  As Long
      Dim pdDefaults As PRINTER_DEFAULTS
      Dim lngHPrntr  As Long
      Dim lngNeeded  As Long
     
      pi1.pPortName = pstrPortName
      fRet = AddPortEx(vbNullString, 1, pi1, "Local Port")
     
      ReDim alngBuf(1)
     
      pdDefaults.DesiredAccess = PRINTER_ALL_ACCESS
      pdDefaults.pDatatype = "RAW"
      pdDefaults.pDevMode = 0
     
      If OpenPrinter(pstrPrinterName, lngHPrntr, pdDefaults) = 0 Then
        Exit Function
      End If
     
      GetPrinter lngHPrntr, 2, alngBuf(0), 0, lngNeeded
      If Err.LastDllError <> ERROR_INSUFFICIENT_BUFFER Then
        ClosePrinter lngHPrntr
        Exit Function
      End If
     
      ReDim alngBuf((lngNeeded \ 4) + (Len(pstrPortName) \ 2) + 2)
      If GetPrinter(lngHPrntr, 2, alngBuf(0), lngNeeded, lngNeeded) = 0 Then
        ClosePrinter lngHPrntr
        Exit Function
      End If
     
      lstrcpy VarPtr(alngBuf((lngNeeded \ 4) + 1)), pstrPortName
      alngBuf(3) = VarPtr(alngBuf((lngNeeded \ 4) + 1))
     
      If SetPrinter(lngHPrntr, 2, alngBuf(0), 0) = 0 Then
        ClosePrinter lngHPrntr
        Exit Function
      End If
     
      ClosePrinter lngHPrntr
     
      SetPrinterPort = True
   
    End Function

When that exact code is in a standard module in a VB6 project, it works perfectly. When I put that exact code into a standard module in Access 2002 (XP) on the very same machine, it does not work. The AddPortEx() call does not add the port, so the call to SetPrinter() fails. I am completely stumped. I know that there are some differences between VB and VBA, but I didn't think it would affect API calls. Is there something that has to be different about the Declare in VB vs. VBA? Any tips you can give me would be most appreciated!

TIA,

Jeff
Avatar of Jim Dettman (EE MVE)
Jim Dettman (EE MVE)
Flag of United States of America image

Jeff,

  The declare for  AddPortEx is incorrect.  Access/VBA does not have an "any" type.

JimD
Avatar of Jeff Tennessen

ASKER

Jim,

Thanks for the input. That's strange, though, because the Access 2002 VBA Help file specifically mentions it in the "Declare Statement" topic:

"Within arglist, you can use an As clause to specify the data type of any of the arguments passed to the procedure. In addition to specifying any of the standard data types, you can specify As Any in arglist to inhibit type checking and allow any data type to be passed to the procedure."

Wouldn't be the first time the documentation was incorrect, of course. What do you suggest as an alternative? I tried PORT_INFO_1 as the type, and though it did not cause new problems, AddPortEx() still didn't work. Do I need to create a pointer to the PORT_INFO_1 variable and pass that?

Thanks!

Jeff
Scratch that last comment.  VBA does suport he any datatype.  But I would try this:

    Private Declare Function AddPortEx _
      Lib "winspool.drv" _
      Alias "AddPortExA" ( _
      ByVal pName As String, _
      ByVal pLevel As Long, _
      ByRef lpBuffer As PORT_INFO_1,
      ByVal pMonitorName As String) As Long

 See if that makes a difference.  Also, is anything supposed to be returned in lbBuffer?

JimD
 
Gave it a shot, but no dice. Same thing -- AddPortEx() does not work, but doesn't blow up, either. lpBuffer doesn't return anything, but it has to be passed ByRef because you can't pass a UDT ByVal. Let me know if you can think of anything else I should try.

Thanks man!

Jeff
Well, this just gets weirder and weirder. I thought I'd try to compile the code that works in VB 6 into an ActiveX DLL, then call that from Access. But even then, the same thing happens -- AddPortEx() fails. If I reference the DLL in a VB 6 project and call it from there, it works fine. I can only conclude that something in Access' implementation of VBA doesn't like the AddPrinterEx() function. I'm going to leave the question open a few more days to see if there are any other suggestions, but as it looks right now, I'm just not going to be able to make this work. :-(

Jeff

  You are using A2002 then?  I want to try it here.

JimD
Yep. Let me know how it goes for you.

Jeff
Well, looks like I struck out on this one. Jim, you deserve points for your efforts to help. I appreciate it!

Jeff
Jeff,

  Naw, you gave up to soon<g>.  I still plan on trying this, but have gotten busy.  In the meantime, I'll have some other Experts look at this.  I'll also re-open the question if you don't mind.

JimD
Avatar of puppydogbuddy
puppydogbuddy

Jeff/Jim,
Don't know if this link will help, but it discusses various API's governing print spooling, some of which may be relevant to what Jeff is trying to accomplish.
                     (start with page 12)
http://aolsearch.aol.com/aol/redir?src=websearch&requestId=f88992b493dcc569&clickedItemRank

A port monitor is an intergral part of the Windows NT printing architecture. Every port monitor supports a standard set of APIs. The functions that need to be supported by the port monitor are as follows:
• InitializePrintMonitor
• DllEntryPoint
• OpenPort
• OpenPortEx
• ClosePort
• StartDocPort
• WritePort
• ReadPort
• EndDocPort
• AddPort or AddPortEx
• DeletePort
• EnumPorts
• ConfigurePort
• SetPortTimeOuts
• GetPrinterDataFromPort

 pdb,

  We've already got the right call, it's just that it won't work in Access VBA and it does in VB.  And I didn't get anywhere when I clicked on your link other then a blank AOL search page.

JimD
Jim,
Here is the link:   www.cswl.com/downloads/w_bitmap.pdf

My thought was that maybe several API's together (API Set) was required.

PDB
Heh, OK. I'm certainly willing to keep plugging along at this one if you are. :-) I just didn't want to leave the Q hanging open for too long without resolution. I'm going to take a look at the link that PDB posted. Jim, let me know once you've had a chance to try it in Access 2002. I certainly understand being busy, so no worries there!

If it would be the best thing to do, I can repost this to reopen it. If we can get this resolved in some sort of workable fashion, it's worth almost any amount of points to me.

Thanks guys!

Jeff
ASKER CERTIFIED SOLUTION
Avatar of Jim Dettman (EE MVE)
Jim Dettman (EE MVE)
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hey Jim,

Well, I now know part of them problem. Your most recent comment got me to thinking why it would work for you, but not for me. I'd been testing this on Windows Vista, but it hadn't occurred to me that this could be part of the problem because it worked correctly from VB. However, I just tried it from Access 2002 under Windows XP, and guess what? It works just fine! Looks like Vista *is* part of the problem. That doesn't explain why it works in VB on Vista, of course, but it certainly does make the situation somewhat clearer.

Anyway, I did find another approach to solving the problem for which this code was intended that works in both Vista and XP. I thank you very much for your comments and suggestions -- and especially for your persistence! It definitely helps to bring a sense of closure to a mystery that was pretty baffling.

Best regards,

Jeff