1 year ago

#229592

test-img

daholtz

VBA buffer not being filled with data by asynchronous (overlapped) Kernel32.dll ReadFile of COM port, but works synchronously

I'm wondering if someone might have some insight into a problem I'm having with VBA reading asynchronously from a serial COM port using the kernel32.dll ReadFile function in overlapped mode?

Basically, everything works except, only with a COM port, the data buffer passed to ReadFile never gets filled with the data but GetOverlappedResults does indicate that the correct number of characters have been read and the asynchronous (overlapped) operation has completed successfully.

The odd thing is that it works synchronously with the COM port, it works asynchronously with a file (instead of COM port), and it works asynchronously if COM port data is available before ReadFile is called (in which case it completes synchronously).

To test that I wasn't misunderstanding how it is supposed to work, I wrote the same code in good old C and it worked perfectly. The data buffer gets filled as expected whereas in the VBA code the data buffer doesn't get filled with the received data as it should.

I've looked at this problem in many ways over many days and don't know what to do next to make progress.

Here is my simple VBA code distilled to the very basics that demonstrate the problem:

Private Type OVERLAPPED
        Internal As Long
        InternalHigh As Long
        offset As Long
        OffsetHigh As Long
        hEvent As Long
End Type

Declare Function CreateFileA Lib "kernel32" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As String, ByVal nNumberOfBytesToRead As Long, ByRef lpNumberOfBytesRead As Long, lpOverlapped As OVERLAPPED) As Long
Declare Function GetOverlappedResult Lib "kernel32" (ByVal hFile As Long, ByRef lpOverlapped As OVERLAPPED, lpNumberOfBytesTransferred As Long, ByVal bWait As Long) As Long
Declare Function GetTickCount Lib "kernel32" () As Long

Private Const FILE_GENERIC_READ = &H120089
Private Const FILE_SHARE_READ = &H1
Private Const OPEN_EXISTING = 3
Private Const FILE_FLAG_OVERLAPPED = &H40000000
Private Const ERROR_IO_INCOMPLETE = 996

Sub test()
    Dim fp As Long
    Dim l As Long
    Dim buffer As String * 1000
    Dim OVERLAPPED As OVERLAPPED
    Dim r As Long
    Dim t As Long
    Dim filename As String
    
    filename = "COM4"
'    filename = "c:\users\dh\text.txt"
    
    fp = CreateFile(filename, FILE_GENERIC_READ, FILE_SHARE_READ, ByVal 0&, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0)

    t = GetTickCount
    
    l = ReadFile(fp, buffer, 11, r, OVERLAPPED) 'read 11 characters asynchronously 
    Debug.Print "l=" & l & " r=" & r & " GetLastError=" & GetLastError & " t=" & (GetTickCount - t) / 1000
    
    While Not GetOverlappedResult(fp, OVERLAPPED, r, 0&) And GetLastError = ERROR_IO_INCOMPLETE
        Debug.Print "l=" & l & " r=" & r & " GetLastError=" & GetLastError & " t=" & (GetTickCount - t) / 1000
        DoEvents 'do other stuff while waiting for FileRead to asyncronously complete.
    Wend
    
    Debug.Print "l=" & l & " r=" & r & " GetLastError=" & GetLastError & " t=" & (GetTickCount - t) / 1000

    For t = 1 To r
        Debug.Print "t:" & t & " C:" & Hex(Asc(Mid(buffer, t, 1)))
    Next
    
    CloseHandle (fp)
    
End Sub

It feels like VBA is treating the buffer as non-volatile so VBA doesn't know that the buffer can spontaneously change.

I can post the analogous C code if anyone wants to see it.

Thanks for your help.

D

vba

asynchronous

readfile

com-port

0 Answers

Your Answer

Accepted video resources