Announcement

Collapse
No announcement yet.

Truncated cFileName attribute when calling FtpFindFirstFile through VB

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Truncated cFileName attribute when calling FtpFindFirstFile through VB



    Dear Friends

    I am attempting to download data from an FTP server using VB; however the cFileName attribute of the WIN32_FIND_DATA structure returned by calling the FtpFindFirstFile function is returning only the final few characters of the filename. This prevents me from using the FtpGetFile function to download the file.

    The number of characters it returns is anywhere from two to eight. For instance, if the filename is filename.xlsx, the cFileName attribute after trimming nulls might give sx or ame.xlsx

    I have already spent quite a few weeks trying to crack this, including an extensive online search and trialling using different computers but to no avail. I am using a 64-bit Windows 10 Pro operating system.

    I have copied the simplest version of the code below, with the connection details obscured for sensitivity. Would anyone be able to help me please?

    Best



    Rich

    Code:
    Const MAX_PATH = 260
    '
    Public Type FILETIME
        dwLowDateTime As Long
        dwHighDateTime As Long
    End Type
    '
    Private Type WIN32_FIND_DATA
        dwFileAttributes As LongPtr
        ftCreationTime As FILETIME
        ftLastAccessTime As FILETIME
        ftLastWriteTime As FILETIME
        nFileSizeHigh As LongPtr
        nFileSizeLow As LongPtr
        dwReserved0 As LongPtr
        dwReserved1 As LongPtr
        cFileName As String * MAX_PATH
        cAlternateFileName As String * 14
    End Type
    '
    Private Declare PtrSafe Function InternetOpen Lib "WININET.DLL" Alias "InternetOpenA" _
        (ByVal sAgent As String, _
        ByVal lAccessType As LongPtr, _
        ByVal sProxyName As String, _
        ByVal sProxyBypass As String, _
        ByVal lFlags As LongPtr) As LongPtr
    '
    Private Declare PtrSafe Function InternetConnect Lib "WININET.DLL" Alias "InternetConnectA" _
        (ByVal hInternetSession As LongPtr, _
        ByVal sServerName As String, _
        ByVal nServerPort As Integer, _
        ByVal sUsername As String, _
        ByVal sPassword As String, _
        ByVal lService As LongPtr, _
        ByVal lFlags As LongPtr, _
        ByVal lContext As LongPtr) As LongPtr
    '
    Private Declare PtrSafe Function FtpSetCurrentDirectory Lib "WININET.DLL" Alias "FtpSetCurrentDirectoryA" _
        (ByVal hFtpSession As LongPtr, _
        ByVal lpszDirectory As String) As Boolean
    '
    Private Declare PtrSafe Function FtpFindFirstFile Lib "WININET.DLL" Alias "FtpFindFirstFileA" _
        (ByVal hFtpSession As LongPtr, _
        ByVal lpszSearchFile As String, _
        lpFindFileData As WIN32_FIND_DATA, _
        ByVal dwFlags As LongPtr, _
        ByVal dwContent As LongPtr) As LongPtr
    '
    Private Declare PtrSafe Function InternetCloseHandle Lib "WININET.DLL" _
        (ByVal hInet As LongPtr) As Integer
    '
    '
    Sub Test()
    '
    hostName = "ftpaddress"
    port = 21
    username = "username"
    password = "password"
    remoteDirectory = "directory/"
    remoteMatchFiles = "*.xls*"
    '
    Dim fileFind As WIN32_FIND_DATA
    '
    hOpen = InternetOpen("ftp VBA", 1, vbNullString, vbNullString, 0)
    Debug.Print hOpen
    '
    If hOpen > 0 Then hConn = InternetConnect(hOpen, hostName, port, username, password, 1, 0, 0)
    Debug.Print hConn
    '
    If hConn > 0 Then hFind = FtpFindFirstFile(hConn, remoteDirectory & remoteMatchFiles, fileFind, 0, 0)
    Debug.Print hFind
    Debug.Print fileFind.cFileName
    '
    InternetCloseHandle hConn
    InternetCloseHandle hOpen
    '
    End Sub

  • #2
    Well I've not done much with the API's you're using, actually never done anything. However, the issue is that because I don't have an FTP file to download I can't troubleshoot your code. Do you know of a FTP location that works that you can reference.

    Have you tried changing the constant max_path?
    What value is in the AlternateFileName variable?
    Regards,
    Barry

    My Favorite New Thing:
    Dynamic Named Ranges



    The alternative for
    "Press Any Key To Continue."

    and we all have one we'd like to use it on

    1. Cross Posting Etiquette
    2. Are You Here To Learn: What Have You Tried?
    3. Your Best Resource to begin learning VBA: RECORD A MACRO . . . Then Study It!

    Comment


    • #3
      Hi Barry

      Thanks for your reply and sorry for the delayed response

      I have tried virtually everything including changing MAX_PATH and using the AlternateFileName attribute

      Unfortunately I do not have an FTP server which I can share with you because the FTP server is run by our client and has sensitive information on it

      Best wishes


      Rich

      Comment


      • #4
        Try some of these routines. Note how nulls are trimmed.

        Free public ftp sites are hard to find these days. Look for ftpsearch and you can search for some. Simtelnet was one of the most mirrored sites at one time. Here is one you can try using. It looks like a file that I uploaded back in the 90's.

        ftp://ftp.gnome.org/mirror/archive/f...n/convuni2.zip

        Code:
        'https://www.mrexcel.com/forum/excel-questions/1069517-vba-ftp-file-download-doesnt-work-64bit-machines.html
        
        'Flags, https://docs.microsoft.com/en-us/windows/desktop/wininet/api-flags
        Const INTERNET_FLAG_RELOAD = &H80000000
        Const INTERNET_FLAG_NO_CACHE_WRITE = &H4000000
        Const INTERNET_FLAG_TRANSFER_BINARY = &H2
        Const INTERNET_FLAG_TRANSFER_ASCII = &H1
        Const MAX_PATH = 260
        Const INTERNET_SERVICE_FTP = 1
         
        Private Type WIN32_FIND_DATA
            dwFileAttributes As Long
            ftCreationTime As Currency          'low & high 32 bits as 64-bit data type
            ftLastAccessTime As Currency
            ftLastWriteTime As Currency
            nFileSizeHigh As Long
            nFileSizeLow As Long
            dwReserved0 As Long
            dwReserved1 As Long
            cFileName As String * MAX_PATH
            cAlternate As String * 14
        End Type
         
        #If Win64 Then
        'Trebuie facuta trecerea si pe x64
        
        Private Declare PtrSafe Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" _
            (ByVal sAgent As String, _
            ByVal lAccessType As Long, _
            ByVal sProxyName As String, _
            ByVal sProxyBypass As String, _
            ByVal lFlags As Long) As Long
        
        Private Declare PtrSafe Function InternetConnect Lib "wininet.dll" Alias "InternetConnectA" _
            (ByVal hInternetSession As Long, _
            ByVal sServerName As String, _
            ByVal nServerPort As Integer, _
            ByVal sUsername As String, _
            ByVal sPassword As String, _
            ByVal lService As Long, _
            ByVal lFlags As Long, _
            ByVal lContext As Long) As Long
         
        Private Declare PtrSafe Function FtpGetFile Lib "wininet.dll" Alias "FtpGetFileA" _
            (ByVal hFtpSession As Long, _
            ByVal lpszRemoteFile As String, _
            ByVal lpszNewFile As String, _
            ByVal fFailIfExists As Boolean, _
            ByVal dwFlagsAndAttributes As Long, _
            ByVal dwFlags As Long, _
            ByVal dwContext As Long) As Boolean
         
        Private Declare PtrSafe Function FtpPutFile Lib "wininet.dll" Alias "FtpPutFileA" _
            (ByVal hFtpSession As Long, _
            ByVal lpszLocalFile As String, _
            ByVal lpszRemoteFile As String, _
            ByVal dwFlags As Long, _
            ByVal dwContext As Long) As Boolean
         
        Private Declare PtrSafe Function FtpSetCurrentDirectory Lib "wininet.dll" Alias "FtpSetCurrentDirectoryA" _
            (ByVal hFtpSession As Long, _
            ByVal lpszDirectory As String) As Boolean
         
        Private Declare PtrSafe Function FtpFindFirstFile Lib "wininet.dll" Alias "FtpFindFirstFileA" _
            (ByVal hFtpSession As Long, _
            ByVal lpszSearchFile As String, _
            lpFindFileData As WIN32_FIND_DATA, _
            ByVal dwFlags As Long, _
            ByVal dwContent As Long) As Long
         
        Private Declare PtrSafe Function InternetFindNextFile Lib "wininet.dll" Alias "InternetFindNextFileA" _
            (ByVal hFind As Long, _
            lpFindFileData As WIN32_FIND_DATA) As Long
        
        Private Declare PtrSafe Function InternetCloseHandle Lib "wininet.dll" _
            (ByVal hInet As Long) As Integer
        
        #Else
        
        Private Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" _
            (ByVal sAgent As String, _
            ByVal lAccessType As Long, _
            ByVal sProxyName As String, _
            ByVal sProxyBypass As String, _
            ByVal lFlags As Long) As Long
        
        Private Declare Function InternetConnect Lib "wininet.dll" Alias "InternetConnectA" _
            (ByVal hInternetSession As Long, _
            ByVal sServerName As String, _
            ByVal nServerPort As Integer, _
            ByVal sUsername As String, _
            ByVal sPassword As String, _
            ByVal lService As Long, _
            ByVal lFlags As Long, _
            ByVal lContext As Long) As Long
         
        Private Declare Function FtpGetFile Lib "wininet.dll" Alias "FtpGetFileA" _
            (ByVal hFtpSession As Long, _
            ByVal lpszRemoteFile As String, _
            ByVal lpszNewFile As String, _
            ByVal fFailIfExists As Boolean, _
            ByVal dwFlagsAndAttributes As Long, _
            ByVal dwFlags As Long, _
            ByVal dwContext As Long) As Boolean
         
        Private Declare Function FtpPutFile Lib "wininet.dll" Alias "FtpPutFileA" _
            (ByVal hFtpSession As Long, _
            ByVal lpszLocalFile As String, _
            ByVal lpszRemoteFile As String, _
            ByVal dwFlags As Long, _
            ByVal dwContext As Long) As Boolean
         
        Private Declare Function FtpSetCurrentDirectory Lib "wininet.dll" Alias "FtpSetCurrentDirectoryA" _
            (ByVal hFtpSession As Long, _
            ByVal lpszDirectory As String) As Boolean
         
        Private Declare Function FtpFindFirstFile Lib "wininet.dll" Alias "FtpFindFirstFileA" _
            (ByVal hFtpSession As Long, _
            ByVal lpszSearchFile As String, _
            lpFindFileData As WIN32_FIND_DATA, _
            ByVal dwFlags As Long, _
            ByVal dwContent As Long) As Long
         
        Private Declare Function InternetFindNextFile Lib "wininet.dll" Alias "InternetFindNextFileA" _
            (ByVal hFind As Long, _
            lpFindFileData As WIN32_FIND_DATA) As Long
        
        Private Declare Function InternetCloseHandle Lib "wininet.dll" _
            (ByVal hInet As Long) As Integer
        
        #End If
        
         
        Public Sub Ftp_Download_Newest_File()
            
            Dim hOpen As Long, hConn As Long, hFind As Long
            Dim ret As Long
            Dim hostName As String, port As Long, username As String, password As String
            Dim localFolder As String
            Dim remoteDirectory As String, remoteMatchFiles As String
            Dim ftpMode As Long
            Dim fileFind As WIN32_FIND_DATA
            Dim newestFileTime As Currency
            Dim newestFileName As String
            
            '========== User-defined settings ==========
            
            localFolder = Environ("temp")
            hostName = "incasuta.ddns.net"
            port = 21
            username = "test"
            password = "test"
            remoteDirectory = "/"
            remoteMatchFiles = "*"
            '===========================================
            
            ftpMode = 0                         'active mode FTP
            'ftpMode = INTERNET_FLAG_PASSIVE    'passive mode FTP
            
            ret = InternetOpen("ftp VBA", 1, vbNullString, vbNullString, 0)
            hOpen = ret
            
            If ret > 0 Then
                ret = InternetConnect(hOpen, hostName, port, username, password, INTERNET_SERVICE_FTP, ftpMode, 0)
                hConn = ret
            End If
            
            If ret > 0 Then
                ret = FtpSetCurrentDirectory(hConn, remoteDirectory)
            End If
            
            If ret > 0 Then
                
                'Find first matching file
                
                fileFind.cFileName = String(MAX_PATH, vbNullChar)
                ret = FtpFindFirstFile(hConn, remoteMatchFiles, fileFind, INTERNET_FLAG_RELOAD Or INTERNET_FLAG_NO_CACHE_WRITE, 0)
                hFind = ret
            
                While ret > 0
                    Debug.Print TrimNulls(fileFind.cFileName)
                    
                    'Is this file newer than the newest file seen so far?
                    
                    If fileFind.ftLastWriteTime > newestFileTime Then
                        newestFileTime = fileFind.ftLastWriteTime
                        newestFileName = TrimNulls(fileFind.cFileName)
                    End If
                    
                    'Find next matching file
                    
                    fileFind.cFileName = String(MAX_PATH, vbNullChar)
                    ret = InternetFindNextFile(hFind, fileFind)
                
                Wend
                
                Debug.Print "Newest "; newestFileName
                
                'Download the newest file to local folder
                
                ret = FtpGetFile(hConn, newestFileName, localFolder & newestFileName, False, 0, INTERNET_FLAG_TRANSFER_BINARY Or INTERNET_FLAG_RELOAD, 0)
                
                If ret = 0 Then
                    Debug.Print "FtpGetFile error "; Err.LastDllError
                Else
                MsgBox "download ok"
                End If
            
            End If
            
            'Release handles
            
            InternetCloseHandle hFind
            InternetCloseHandle hConn
            InternetCloseHandle hOpen
            
        End Sub
        
        Private Function TrimNulls(buffer As String) As String
            TrimNulls = Left(buffer, InStr(buffer, vbNullChar) - 1)
        End Function
        Last edited by Kenneth Hobson; 2 weeks ago.

        Comment


        • #5
          Hi Kenneth

          Thanks for your reply

          Actually the code that you posted is the code that I originally tried before my four weeks of labouring at getting the code to work! Thanks for your suggestion though.

          Best


          Rich

          Comment


          • #6
            Use a free site and post details. Site, folder, username, password.

            Get a windows ftp client to see how those work. Freeftp, cuteftp, winsock, winftp, etc. Passive or not matters.

            Comment


            • #7


              Thanks for your suggestion Kenneth I shall give it a go and let you know how it goes

              Comment

              Working...
              X