Jeff Tennessen
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(pstrPrinter Name 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(pstrPrinterNam e, 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
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(pstrPrinter
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(pstrPrinterNam
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
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
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
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
ASKER
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
Thanks man!
Jeff
ASKER
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
Jeff
You are using A2002 then? I want to try it here.
JimD
ASKER
Yep. Let me know how it goes for you.
Jeff
Jeff
ASKER
Well, looks like I struck out on this one. Jim, you deserve points for your efforts to help. I appreciate it!
Jeff
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
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
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
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
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
ASKER
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
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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
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
The declare for AddPortEx is incorrect. Access/VBA does not have an "any" type.
JimD