View difference between Paste ID: sB2b7F4H and 5cFVU2Hu
SHOW: | | - or go back to the newest paste.
1
Option Explicit
2
3-
' //********************************************\\
3+
' =========================================
4-
' |**********************************************|
4+
' CFTPclient: wininet ftp functions wrapper
5-
' |* CFTPclient: wininet ftp functions wrapper  *|
5+
'             needs IE version 4 or above
6-
' |*             needs IE version 4 or above    *|
6+
' =========================================
7-
' |**********************************************|
7+
8-
' \\********************************************//
8+
' FTP file transfer modes
9
Public Enum enFtpFileMode
10-
' FTP transfer modes
10+
  ffm_Ascii = 1&
11
  ffm_Binary = 2&
12-
    ffm_Ascii = 1&
12+
13-
    ffm_Binary = 2&
13+
14
' Public events
15
Public Event Connected(ByVal sServer As String, ByVal lPort As Long)
16
Public Event Message(ByVal sMsg As String)
17
Public Event DirLine(ByVal vAttr As VbFileAttribute, ByVal dtDateTime As Date, ByVal dSize As Double, ByVal sName As String)
18
Public Event Progress(ByVal lCurrent As Long, ByVal lTotal As Long, bCancel As Boolean)
19-
Public Event Progress(ByVal lCurrent As Long, ByVal lTotal As Long)
19+
20
Public Event Disconnected(ByVal sServer As String, ByVal lPort As Long)
21
22
' ftp defaults (used in constructor)
23
Private Const FTP_DEFAULT_SITE = "ftp.cisco.com"
24-
    sServer         As String   ' server name/ip
24+
Private Const FTP_DEFAULT_USER = "Anonymous"
25-
    nPort           As Long     ' server port
25+
Private Const FTP_DEFAULT_PASS = "guest@example.com"
26-
    sUser           As String   ' username
26+
Private Const FTP_DEFAULT_PASV = True
27-
    sPassword       As String   ' password
27+
28-
    bPassive        As Boolean  ' true=passive mode
28+
29-
    hInternet       As Long     ' internet session handle
29+
30-
    hConnect        As Long     ' ftp session handle
30+
  sServer         As String   ' server name/ip
31-
    hCtx            As Long     ' context (not used)
31+
  nPort           As Long     ' server port
32-
    dwLastError     As Long     ' last error #
32+
  sUser           As String   ' username
33-
    szLastError     As String   ' last error msg
33+
  sPassword       As String   ' password
34
  bPassive        As Boolean  ' true=passive mode
35
  hInternet       As Long     ' internet session handle
36-
' Storage area
36+
  hConnect        As Long     ' ftp session handle
37
  hCtx            As Long     ' context (not used)
38
  dwLastError     As Long     ' last error #
39
  szLastError     As String   ' last error msg
40
End Type
41
42
' private storage area
43-
    INTERNET_OPEN_TYPE_PRECONFIG = 0                     ' use system (IE) configuration
43+
44-
    INTERNET_OPEN_TYPE_DIRECT = 1                        ' direct to net
44+
Private msAppName   As String             ' application name w/o ".exe"
45-
    INTERNET_OPEN_TYPE_PROXY = 3                         ' via named proxy
45+
46-
    INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY = 4   ' prevent using java/script/INS
46+
47
48
' Type of internet connection
49
Private Enum INTERNET_OPEN_TYPE
50-
Private Const INTERNET_INVALID_PORT_NUMBER = 0           ' invalid port
50+
  INTERNET_OPEN_TYPE_PRECONFIG = 0                        ' use system (IE) configuration
51-
Private Const INTERNET_DEFAULT_FTP_PORT = 21             ' default for FTP servers
51+
  INTERNET_OPEN_TYPE_DIRECT = 1                           ' direct to net
52-
Private Const INTERNET_SERVICE_FTP = 1                   ' FTP service type selector
52+
  INTERNET_OPEN_TYPE_PROXY = 3                            ' via named proxy
53-
Private Const INTERNET_FLAG_PASSIVE = &H8000000          ' passive FTP connections
53+
  INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY = 4      ' preconfig, no proxy autodiscovery
54-
Private Const INTERNET_FLAG_RELOAD = &H80000000          ' avoid data caching
54+
55
56-
Private Const MAX_PATH = 260                             ' max path/file size
56+
57-
Private Const ERROR_NO_MORE_FILES = 18                   ' end of dir listing
57+
Private Const INTERNET_INVALID_PORT_NUMBER = 0            ' invalid port
58
Private Const INTERNET_DEFAULT_FTP_PORT = 21              ' default for FTP servers
59
Private Const INTERNET_SERVICE_FTP = 1                    ' FTP service type selector
60
Private Const INTERNET_FLAG_PASSIVE = &H8000000           ' passive FTP connections
61-
    dwLowDateTime       As Long
61+
Private Const INTERNET_FLAG_RELOAD = &H80000000           ' avoid data caching
62-
    dwHighDateTime      As Long
62+
63
Private Const MAX_PATH = 260                              ' max path/file size
64
Private Const ERROR_NO_MORE_FILES = 18                    ' end of dir listing
65
66
' FileTime (see below)
67-
    dwFileAttributes    As Long
67+
68-
    ftCreationTime      As FILETIME
68+
  dwLowDateTime       As Long
69-
    ftLastAccessTime    As FILETIME
69+
  dwHighDateTime      As Long
70-
    ftLastWriteTime     As FILETIME
70+
71-
    nFileSizeHigh       As Long
71+
72-
    nFileSizeLow        As Long
72+
73-
    dwReserved0         As Long
73+
74-
    dwReserved1         As Long
74+
  dwFileAttributes    As Long
75-
    cFileName           As String * MAX_PATH
75+
  ftCreationTime      As FILETIME
76-
    cAlternate          As String * 14
76+
  ftLastAccessTime    As FILETIME
77
  ftLastWriteTime     As FILETIME
78
  nFileSizeHigh       As Long
79
  nFileSizeLow        As Long
80
  dwReserved0         As Long
81-
    wYear               As Integer
81+
  dwReserved1         As Long
82-
    wMonth              As Integer
82+
  cFileName           As String * MAX_PATH
83-
    wDayOfWeek          As Integer
83+
  cAlternate          As String * 14
84-
    wDay                As Integer
84+
85-
    wHour               As Integer
85+
86-
    wMinute             As Integer
86+
87-
    wSecond             As Integer
87+
88-
    wMilliseconds       As Integer
88+
  wYear               As Integer
89
  wMonth              As Integer
90
  wDayOfWeek          As Integer
91
  wDay                As Integer
92
  wHour               As Integer
93-
    vFileAttributes     As VbFileAttribute
93+
  wMinute             As Integer
94-
    dtCreationTime      As Date
94+
  wSecond             As Integer
95-
    dtLastAccessTime    As Date
95+
  wMilliseconds       As Integer
96-
    dtLastWriteTime     As Date
96+
97-
    dFileSize           As Double
97+
98-
    sFileName           As String
98+
99-
    sAlternate          As String
99+
100
  vFileAttributes     As VbFileAttribute
101
  dtCreationTime      As Date
102
  dtLastAccessTime    As Date
103
  dtLastWriteTime     As Date
104
  dFileSize           As Double
105
  sFileName           As String
106
  sAlternate          As String
107
End Type
108
109
' Date/Time and memory manipulation
110
Private Declare Function FileTimeToLocalFileTime Lib "kernel32" (lpFILETIME As FILETIME, lpLocalFileTime As FILETIME) As Long
111
Private Declare Function FileTimeToSystemTime Lib "kernel32" (lpFILETIME As FILETIME, lpSystemTime As SYSTEMTIME) As Long
112
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, source As Any, ByVal bytes As Long)
113
114
' error constants
115-
    GENERIC_READ = &H80000000                           ' file read mode (download)
115+
Private Const ERROR_INTERNET_EXTENDED_ERROR = &H2EE3
116-
    GENERIC_WRITE = &H40000000                          ' file write mode (upload)
116+
117
118
' Error messages decoding
119
Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" (ByVal dwFlags As Long, ByVal lpSource As Long, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, ByVal lpBuffer As String, ByVal nSize As Long, dwArguments As Long) As Long
120
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpLibFileName As String) As Long
121-
    FTP_TRANSFER_TYPE_UNKNOWN = &H0&                    ' unknown (binary)
121+
122-
    FTP_TRANSFER_TYPE_ASCII = &H1&                      ' ASCII
122+
123-
    FTP_TRANSFER_TYPE_BINARY = &H2&                     ' binary
123+
124-
    FTP_TRANSFER_NO_CACHE = &H80000000                  ' no caching
124+
  GENERIC_READ = &H80000000                           ' file read mode (download)
125
  GENERIC_WRITE = &H40000000                          ' file write mode (upload)
126
End Enum
127
128
' FTP transfer types (private)
129
Private Enum FTP_TRANSFER_TYPE
130
  FTP_TRANSFER_TYPE_UNKNOWN = &H0&                    ' unknown (binary)
131
  FTP_TRANSFER_TYPE_ASCII = &H1&                      ' ASCII
132
  FTP_TRANSFER_TYPE_BINARY = &H2&                     ' binary
133
  FTP_TRANSFER_NO_CACHE = &H80000000                  ' no caching
134
End Enum
135
136
137
' open/close an internet session
138
Private Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" (ByVal lpszAgent As String, ByVal dwAccessType As INTERNET_OPEN_TYPE, ByVal lpszProxyName As String, ByVal lpszProxyBypass As String, ByVal dwFlags As Long) As Long
139
Private Declare Function InternetClose Lib "wininet.dll" Alias "InternetCloseHandle" (ByVal hInternet As Long) As Long
140
141
' last error code/message
142
Private Declare Function InternetError Lib "wininet.dll" Alias "InternetGetLastResponseInfoA" (lpdwError As Long, ByVal lpszBuffer As String, lpdwBufferLength As Long) As Long
143
144
' connect/disconnect an ftp server
145
Private Declare Function FtpConnect Lib "wininet.dll" Alias "InternetConnectA" (ByVal hInternet As Long, ByVal lpszServerName As String, ByVal nServerPort As Long, ByVal lpszUserName As String, ByVal lpszPassword As String, ByVal dwService As Long, ByVal dwFlags As Long, dwContext As Long) As Long
146
Private Declare Function FtpDisconnect Lib "wininet.dll" Alias "InternetCloseHandle" (ByVal hInternet As Long) As Long
147
148
' directory operations
149
Private Declare Function FtpQueryDir Lib "wininet.dll" Alias "FtpGetCurrentDirectoryA" (ByVal hConnect As Long, ByVal lpszCurrentDirectory As String, lpdwCurrentDirectory As Long) As Long
150
Private Declare Function FtpChangeDir Lib "wininet.dll" Alias "FtpSetCurrentDirectoryA" (ByVal hConnect As Long, ByVal lpszDirectory As String) As Long
151
Private Declare Function FtpCreateDir Lib "wininet.dll" Alias "FtpCreateDirectoryA" (ByVal hConnect As Long, ByVal lpszDirectory As String) As Long
152
Private Declare Function FtpRemoveDir Lib "wininet.dll" Alias "FtpRemoveDirectoryA" (ByVal hConnect As Long, ByVal lpszDirectory As String) As Long
153
Private Declare Function FtpFindFirstFile Lib "wininet.dll" Alias "FtpFindFirstFileA" (ByVal hConnect As Long, ByVal lpszSearchFile As String, lpFindFileData As WIN32_FIND_DATA, ByVal dwFlags As Long, dwContext As Long) As Long
154
Private Declare Function FtpFindNextFile Lib "wininet.dll" Alias "InternetFindNextFileA" (ByVal hFind As Long, lpFindFileData As WIN32_FIND_DATA) As Long
155
Private Declare Function FtpFindClose Lib "wininet.dll" Alias "InternetCloseHandle" (ByVal hInternet As Long) As Long
156
157
' file operations
158
Private Declare Function FtpOpenFile Lib "wininet.dll" Alias "FtpOpenFileA" (ByVal hConnect As Long, ByVal lpszFileName As String, ByVal dwAccess As FTP_FILE_OPEN_MODE, ByVal dwFlags As FTP_TRANSFER_TYPE, dwContext As Long) As Long
159
Private Declare Function FtpFileSize Lib "wininet.dll" Alias "FtpGetFileSize" (ByVal hFile As Long, lpdwFileSizeHigh As Long) As Long
160
Private Declare Function FtpBuffSize Lib "wininet.dll" Alias "InternetQueryDataAvailable" (ByVal hFile As Long, lpdwNumberOfBytesAvailable As Long, ByVal dwFlags As Long, ByVal dwContext As Long) As Long
161
Private Declare Function FtpReadFile Lib "wininet.dll" Alias "InternetReadFile" (ByVal hFile As Long, ByRef lpBuffer As Byte, ByVal dwNumberOfBytesToRead As Long, lpdwNumberOfBytesRead As Long) As Long
162
Private Declare Function FtpWriteFile Lib "wininet.dll" Alias "InternetWriteFile" (ByVal hFile As Long, ByRef lpBuffer As Byte, ByVal dwNumberOfBytesToWrite As Long, lpdwNumberOfBytesWritten As Long) As Long
163-
    ' setup some reasonable defaults
163+
164-
    With mFTP
164+
165-
        .sServer = "ftp.cisco.com"
165+
166-
        .nPort = INTERNET_DEFAULT_FTP_PORT
166+
167-
        .sUser = "anonymous"
167+
168-
        .sPassword = "guest@example.com"
168+
169-
        .bPassive = True
169+
170-
        .hInternet = 0
170+
171-
        .hConnect = 0
171+
172-
        .hCtx = 0
172+
  ' setup some reasonable defaults so that
173-
        .dwLastError = 0
173+
  ' just instancing the class and calling
174-
        .szLastError = ""
174+
  ' the connect method, it will work
175-
    End With
175+
  With mFTP
176-
    msAgent = App.EXEName & "(" & App.Major & ":" & App.Minor & ":" & App.Revision & ")"
176+
    .sServer = FTP_DEFAULT_SITE
177-
    mbDebug = False
177+
    .nPort = INTERNET_DEFAULT_FTP_PORT
178
    .sUser = FTP_DEFAULT_USER
179
    .sPassword = FTP_DEFAULT_PASS
180
    .bPassive = FTP_DEFAULT_PASV
181
    .hInternet = 0
182
    .hConnect = 0
183
    .hCtx = 0
184
    .dwLastError = 0
185
    .szLastError = ""
186
  End With
187
  msAppName = GetAppName()
188-
    mFTP.sServer = sServer
188+
  msAgent = msAppName & "/" & App.Major & "." & App.Minor & " (build=" & App.Revision & ")"
189
  mbDebug = False
190
End Sub
191
192-
    Server = mFTP.sServer
192+
193
Private Sub Class_Terminate()
194
  On Local Error Resume Next
195
  Disconnect
196
End Sub
197-
    mFTP.nPort = nPort
197+
198
' .:::: Server name or IP address
199
Public Property Let Server(ByVal sServer As String)
200
  mFTP.sServer = sServer
201-
    Port = mFTP.nPort
201+
202
203
Public Property Get Server() As String
204
  Server = mFTP.sServer
205
End Property
206-
    mFTP.sUser = sUser
206+
207
' .:::: server port #
208
Public Property Let Port(ByVal nPort As Long)
209
  mFTP.nPort = nPort
210-
    User = mFTP.sUser
210+
211
212
Public Property Get Port() As Long
213
  Port = mFTP.nPort
214
End Property
215-
    mFTP.sPassword = sPassword
215+
216
' .:::: logon user name
217
Public Property Let User(ByVal sUser As String)
218
  mFTP.sUser = sUser
219-
    Password = mFTP.sPassword
219+
220
221
Public Property Get User() As String
222
  User = mFTP.sUser
223
End Property
224-
    mFTP.bPassive = bYesNo
224+
225
' .:::: logon password
226
Public Property Let Password(ByVal sPassword As String)
227
  mFTP.sPassword = sPassword
228-
    Passive = mFTP.bPassive
228+
229
230
Public Property Get Password() As String
231
  Password = mFTP.sPassword
232
End Property
233-
    msAgent = sAgent
233+
234
' .:::: passive mode
235
Public Property Let Passive(ByVal bYesNo As Boolean)
236
  mFTP.bPassive = bYesNo
237-
    UserAgent = msAgent
237+
238
239
Public Property Get Passive() As Boolean
240
  Passive = mFTP.bPassive
241
End Property
242-
    mbDebug = bYesNo
242+
243
' .:::: user agent string
244
Public Property Let UserAgent(ByVal sAgent As String)
245
  msAgent = sAgent
246-
    DebugMode = mbDebug
246+
247
248
Public Property Get UserAgent() As String
249
  UserAgent = msAgent
250
End Property
251-
    Dim lErrNo As Long, lFlags As Long
251+
252-
    Dim bRet As Boolean
252+
253-
    
253+
254
  mbDebug = bYesNo
255
End Property
256-
    Connect = bRet
256+
257-
    
257+
258-
    DbgTrace "Connect " & mFTP.sServer
258+
  DebugMode = mbDebug
259-
    
259+
260-
    Err.Clear
260+
261-
    mFTP.hInternet = InternetOpen(msAgent, INTERNET_OPEN_TYPE_PRECONFIG, vbNullString, vbNullString, 0&)
261+
' .:::: last error number/message
262-
    If (mFTP.hInternet = 0) Then
262+
Public Property Get LastErrorNum() As Long
263
  LastErrorNum = mFTP.dwLastError
264-
        Disconnect
264+
265-
        Exit Function
265+
266
Public Property Get LastErrorMsg() As String
267-
    
267+
  LastErrorMsg = mFTP.szLastError
268-
    lFlags = IIf(mFTP.bPassive, INTERNET_FLAG_PASSIVE, 0&)
268+
269-
    Err.Clear
269+
270-
    mFTP.hConnect = FtpConnect(mFTP.hInternet, mFTP.sServer, mFTP.nPort, mFTP.sUser, mFTP.sPassword, INTERNET_SERVICE_FTP, lFlags, mFTP.hCtx)
270+
271-
    If (mFTP.hConnect = 0) Then
271+
272
  Dim lErrNo As Long, lFlags As Long
273-
        Disconnect
273+
  Dim bRet As Boolean
274-
        Exit Function
274+
  
275
  On Local Error Resume Next
276
  bRet = False
277-
    bRet = True
277+
  Connect = bRet
278-
    
278+
  
279-
    Connect = bRet
279+
  DbgTrace "Connect " & mFTP.sServer & ":" & mFTP.nPort & " " & msAgent
280
  
281
  ' acquire an internet handle
282
  Err.Clear
283
  mFTP.hInternet = InternetOpen(msAgent, INTERNET_OPEN_TYPE_PRECONFIG, vbNullString, vbNullString, 0&)
284
  If (mFTP.hInternet = 0) Then
285
    DecodeError
286-
    If mFTP.hConnect <> 0 Then
286+
287-
        Call FtpDisconnect(mFTP.hConnect)
287+
288-
        FtpResponse
288+
  End If
289-
        mFTP.hConnect = 0
289+
  
290
  ' connect to the FTP server
291-
    If mFTP.hInternet <> 0 Then
291+
  lFlags = IIf(mFTP.bPassive, INTERNET_FLAG_PASSIVE, 0&)
292-
        Call InternetClose(mFTP.hInternet)
292+
  Err.Clear
293-
        mFTP.hInternet = 0
293+
  mFTP.hConnect = FtpConnect(mFTP.hInternet, mFTP.sServer, mFTP.nPort, mFTP.sUser, mFTP.sPassword, INTERNET_SERVICE_FTP, lFlags, mFTP.hCtx)
294
  If (mFTP.hConnect = 0) Then
295
    DecodeError
296
    Disconnect
297
    Exit Function
298
  End If
299-
    Dim sDir As String, lSize As Long, bRet As Boolean
299+
  
300-
    
300+
  RaiseEvent Connected(mFTP.sServer, mFTP.nPort)
301
  FtpResponse
302-
    lSize = 2048
302+
  bRet = True
303-
    sDir = String(lSize, vbNullChar)
303+
  
304-
    bRet = FtpQueryDir(mFTP.hConnect, sDir, lSize)
304+
  Connect = bRet
305-
    If bRet = False Then
305+
306
307-
        sDir = vbNullString
307+
308-
    Else
308+
309-
        FtpResponse
309+
  On Local Error Resume Next
310
  If (mFTP.hConnect <> 0) Or (mFTP.hInternet <> 0) Then
311-
    sDir = GetSZ(sDir)
311+
    RaiseEvent Disconnected(mFTP.sServer, mFTP.nPort)
312-
    DbgTrace "GetCurrentDirectory = " & sDir
312+
313-
    GetCurrentDirectory = sDir
313+
  End If
314
  If mFTP.hConnect <> 0 Then
315
    Call FtpDisconnect(mFTP.hConnect)
316
    FtpResponse
317
    mFTP.hConnect = 0
318-
    Dim bRet As Boolean
318+
  End If
319
  If mFTP.hInternet <> 0 Then
320
    Call InternetClose(mFTP.hInternet)
321-
    DbgTrace "SetCurrentDirectory " & sDirectory
321+
    mFTP.hInternet = 0
322-
    bRet = FtpChangeDir(mFTP.hConnect, sDirectory)
322+
  End If
323-
    If bRet = False Then
323+
324
325-
    Else
325+
326-
        FtpResponse
326+
327
  Dim sDir As String, lSize As Long, bRet As Boolean
328-
    SetCurrentDirectory = bRet
328+
  
329
  On Local Error Resume Next
330
  lSize = 2048
331
  sDir = String(lSize, vbNullChar)
332
  bRet = FtpQueryDir(mFTP.hConnect, sDir, lSize)
333-
    Dim bRet As Boolean
333+
  If bRet = False Then
334-
    
334+
    DecodeError
335
    sDir = vbNullString
336-
    DbgTrace "CreateDirectory " & sDirectory
336+
  Else
337-
    bRet = FtpCreateDir(mFTP.hConnect, sDirectory)
337+
338-
    If bRet = False Then
338+
  End If
339
  sDir = GetSZ(sDir)
340-
    Else
340+
  DbgTrace "GetCurrentDirectory = " & sDir
341-
        FtpResponse
341+
  GetCurrentDirectory = sDir
342
End Function
343-
    CreateDirectory = bRet
343+
344
' .:::: set current dir path
345
Public Function SetCurrentDirectory(ByVal sDirectory As String) As Boolean
346
  Dim bRet As Boolean
347
  
348-
    Dim bRet As Boolean
348+
  On Local Error Resume Next
349-
    
349+
  DbgTrace "SetCurrentDirectory " & sDirectory
350
  bRet = FtpChangeDir(mFTP.hConnect, sDirectory)
351-
    DbgTrace "RemoveDirectory " & sDirectory
351+
  If bRet = False Then
352-
    bRet = FtpRemoveDir(mFTP.hConnect, sDirectory)
352+
    DecodeError
353-
    If bRet = False Then
353+
  Else
354
    FtpResponse
355-
    Else
355+
  End If
356-
        FtpResponse
356+
  SetCurrentDirectory = bRet
357
End Function
358-
    RemoveDirectory = bRet
358+
359
' .:::: create a directory
360
Public Function CreateDirectory(ByVal sDirectory As String) As Boolean
361
  Dim bRet As Boolean
362
  
363-
    Dim hFind As Long, tFIND As WIN32_FIND_DATA
363+
  On Local Error Resume Next
364-
    Dim bFind As Boolean, lFiles As Long
364+
  DbgTrace "CreateDirectory " & sDirectory
365-
    
365+
  bRet = FtpCreateDir(mFTP.hConnect, sDirectory)
366
  If bRet = False Then
367-
    lFiles = -1
367+
    DecodeError
368-
    ReadDirList = lFiles
368+
  Else
369-
    DbgTrace "ReadDirList " & sFileMask
369+
370-
    
370+
  End If
371-
    Err.Clear
371+
  CreateDirectory = bRet
372-
    hFind = FtpFindFirstFile(mFTP.hConnect, sFileMask, tFIND, INTERNET_FLAG_RELOAD, mFTP.hCtx)
372+
373-
    If (hFind = 0) Then
373+
374
' .:::: remove a directory (directory must be empty)
375-
        Exit Function
375+
376
  Dim bRet As Boolean
377
  
378-
    
378+
  On Local Error Resume Next
379-
    lFiles = 0
379+
  DbgTrace "RemoveDirectory " & sDirectory
380
  bRet = FtpRemoveDir(mFTP.hConnect, sDirectory)
381-
        lFiles = lFiles + 1
381+
  If bRet = False Then
382-
        DecodeDirEntry tFIND
382+
    DecodeError
383-
        bFind = FtpFindNextFile(hFind, tFIND)
383+
  Else
384-
        If bFind = False Then
384+
385-
            If Err.LastDllError <> ERROR_NO_MORE_FILES Then
385+
  End If
386-
                DecodeError
386+
  RemoveDirectory = bRet
387-
                lFiles = -1
387+
388
389
' .:::: read directory listing
390-
    Loop While bFind <> False
390+
391-
    
391+
  Dim hFind As Long, tFIND As WIN32_FIND_DATA
392-
    Call FtpFindClose(hFind)
392+
  Dim bFind As Boolean, lFiles As Long
393-
    If lFiles <> -1 Then
393+
  
394-
        FtpResponse
394+
  On Local Error Resume Next
395
  lFiles = -1
396-
    ReadDirList = lFiles
396+
  ReadDirList = lFiles
397
  DbgTrace "ReadDirList " & sFileMask
398
  
399
  ' start the listing
400
  Err.Clear
401-
    Dim bRet As Boolean
401+
  hFind = FtpFindFirstFile(mFTP.hConnect, sFileMask, tFIND, INTERNET_FLAG_RELOAD, mFTP.hCtx)
402-
    
402+
  If (hFind = 0) Then
403
    DecodeError
404-
    DbgTrace "DeleteFile " & sFileName
404+
405-
    bRet = FtpDeleteFile(mFTP.hConnect, sFileName)
405+
  End If
406-
    If bRet = False Then
406+
  FtpResponse
407
  
408-
    Else
408+
  ' loop over FTP folder items
409-
        FtpResponse
409+
  lFiles = 0
410
  Do
411-
    DeleteFile = bRet
411+
    lFiles = lFiles + 1
412
    DecodeDirEntry tFIND
413
    bFind = FtpFindNextFile(hFind, tFIND)
414
    If bFind = False Then
415
      If Err.LastDllError <> ERROR_NO_MORE_FILES Then
416-
    Dim bRet As Boolean
416+
417-
    
417+
        lFiles = -1
418
      End If
419-
    DbgTrace "RenameFile " & sFileName & " -> " & sNewName
419+
420-
    bRet = FtpRenameFile(mFTP.hConnect, sFileName, sNewName)
420+
  Loop While bFind <> False
421-
    If bRet = False Then
421+
  
422
  ' cleanup and return
423-
    Else
423+
  Call FtpFindClose(hFind)
424-
        FtpResponse
424+
  If lFiles <> -1 Then
425
    FtpResponse
426-
    RenameFile = bRet
426+
  End If
427
  ReadDirList = lFiles
428
End Function
429
430
' .:::: delete a file
431-
    Dim lMode As FTP_TRANSFER_TYPE
431+
432-
    Dim hLocal As Long, hRemote As Long
432+
  Dim bRet As Boolean
433-
    Dim lSizeLow As Long, lSizeHigh As Long
433+
  
434-
    Dim lSize As Long, lCurrent As Long
434+
  On Local Error Resume Next
435-
    Dim bRet As Boolean, bRead As Boolean
435+
  DbgTrace "DeleteFile " & sFileName
436-
    Dim cbBuff() As Byte, lBuff As Long, lBytes As Long
436+
  bRet = FtpDeleteFile(mFTP.hConnect, sFileName)
437-
    
437+
  If bRet = False Then
438
    DecodeError
439
  Else
440-
    lCurrent = 0
440+
441-
    DownloadFile = bRet
441+
  End If
442-
    DbgTrace "DownloadFile " & sRemoteFile & " -> " & sLocalFile
442+
  DeleteFile = bRet
443-
    
443+
444-
    hLocal = OpenFile(sLocalFile, True)
444+
445-
    If hLocal = 0 Then
445+
446-
        Exit Function
446+
447
  Dim bRet As Boolean
448-
    
448+
  
449-
    If nMode = ffm_Ascii Then
449+
  On Local Error Resume Next
450-
        lMode = FTP_TRANSFER_TYPE_ASCII + FTP_TRANSFER_NO_CACHE
450+
  DbgTrace "RenameFile " & sFileName & " -> " & sNewName
451-
    Else
451+
  bRet = FtpRenameFile(mFTP.hConnect, sFileName, sNewName)
452-
        lMode = FTP_TRANSFER_TYPE_BINARY + FTP_TRANSFER_NO_CACHE
452+
  If bRet = False Then
453
    DecodeError
454-
    Err.Clear
454+
  Else
455-
    hRemote = FtpOpenFile(mFTP.hConnect, sRemoteFile, GENERIC_READ, lMode, mFTP.hCtx)
455+
456-
    If (hRemote = 0) Then
456+
  End If
457
  RenameFile = bRet
458-
        Close #hLocal
458+
459-
        Exit Function
459+
460
' .:::: download (receive) a file from ftp server
461
Public Function DownloadFile(ByVal sRemoteFile As String, ByVal sLocalFile As String, Optional ByVal nMode As enFtpFileMode = ffm_Binary) As Boolean
462-
    
462+
  Dim lMode As FTP_TRANSFER_TYPE
463-
    lSizeLow = FtpFileSize(hRemote, lSizeHigh)
463+
  Dim hLocal As Long, hRemote As Long
464-
    lSize = DecodeSize(lSizeLow, lSizeHigh)
464+
  Dim lSizeLow As Long, lSizeHigh As Long
465-
    RaiseEvent Progress(lCurrent, lSize)
465+
  Dim lSize As Long, lCurrent As Long
466-
    
466+
  Dim bRet As Boolean, bRead As Boolean
467-
    ' loop: read from ftp and write to file
467+
  Dim cbBuff() As Byte, lBuff As Long, lBytes As Long
468
  Dim bCancel As Boolean
469
  
470-
        bRead = FtpBuffSize(hRemote, lBuff, 0&, mFTP.hCtx)
470+
  On Local Error Resume Next
471-
        If lBuff < 1 Then
471+
  bRet = False
472-
            lBuff = 1024
472+
  lCurrent = 0
473
  DownloadFile = bRet
474-
        ReDim cbBuff(lBuff + 1)
474+
  DbgTrace "DownloadFile " & sRemoteFile & " -> " & sLocalFile
475
  
476-
        bRead = FtpReadFile(hRemote, cbBuff(0), lBuff, lBytes)
476+
  ' open local file for writing (delete existing)
477-
        If (bRead <> False) Then
477+
  hLocal = OpenFile(sLocalFile, True)
478-
            If (lBytes > 0) Then
478+
  If hLocal = 0 Then
479-
                ReDim Preserve cbBuff(lBytes - 1)
479+
480-
                Err.Clear
480+
  End If
481-
                Put #hLocal, , cbBuff
481+
  
482-
                If Err.Number <> 0 Then
482+
  ' open remote file for reading
483-
                    RaiseEvent Failure(Err.Number, Err.Description)
483+
  If nMode = ffm_Ascii Then
484-
                    DbgTrace "Error: " & Err.Number & " " & Err.Description
484+
    lMode = FTP_TRANSFER_TYPE_ASCII
485-
                    bRead = False
485+
  Else
486-
                    bRet = False
486+
    lMode = FTP_TRANSFER_TYPE_BINARY
487-
                Else
487+
  End If
488-
                    lCurrent = lCurrent + lBytes
488+
  lMode = lMode + FTP_TRANSFER_NO_CACHE
489-
                    RaiseEvent Progress(lCurrent, lSize)
489+
  Err.Clear
490-
                End If
490+
  hRemote = FtpOpenFile(mFTP.hConnect, sRemoteFile, GENERIC_READ, lMode, mFTP.hCtx)
491-
            Else
491+
  If (hRemote = 0) Then
492-
                bRead = False
492+
    DecodeError
493-
                bRet = True
493+
494
    Exit Function
495
  End If
496-
            DecodeError
496+
  FtpResponse
497
  
498
  ' read the remote file size
499
  lSizeLow = FtpFileSize(hRemote, lSizeHigh)
500
  lSize = DecodeSize(lSizeLow, lSizeHigh)
501-
    
501+
  RaiseEvent Progress(lCurrent, lSize, bCancel)
502-
    If bRet = True Then
502+
  
503-
        RaiseEvent Progress(lCurrent, lSize)
503+
  ' loop: read from ftp and write to file
504
  If Not bCancel Then
505-
    Call FtpCloseFile(hRemote)
505+
506-
    If bRet = True Then
506+
507-
        FtpResponse
507+
      ' find out the current read buffer size
508
      bRead = FtpBuffSize(hRemote, lBuff, 0&, mFTP.hCtx)
509
      If lBuff < 1 Then
510-
    
510+
        lBuff = 1024
511-
    DownloadFile = bRet
511+
      End If
512
      ReDim cbBuff(lBuff + 1)
513
      lBytes = 0
514
      ' read a chunk from remote file
515
      bRead = FtpReadFile(hRemote, cbBuff(0), lBuff, lBytes)
516-
    Dim lMode As FTP_TRANSFER_TYPE
516+
      If (bRead <> False) Then
517-
    Dim hLocal As Long, hRemote As Long
517+
        If (lBytes > 0) Then
518-
    Dim lSizeLow As Long, lSizeHigh As Long
518+
          ' write local file
519-
    Dim lSize As Long, lCurrent As Long, lRemainder As Long
519+
          ReDim Preserve cbBuff(lBytes - 1)
520-
    Dim bRet As Boolean, bWrite As Boolean
520+
          Err.Clear
521-
    Dim cbBuff() As Byte, lBuff As Long, lBytes As Long
521+
          Put #hLocal, , cbBuff
522-
    
522+
          If Err.Number <> 0 Then
523
            RaiseEvent Failure(Err.Number, Err.Description)
524
            DbgTrace "Error: " & Err.Number & " " & Err.Description
525-
    lCurrent = 0
525+
526-
    UploadFile = bRet
526+
527-
    DbgTrace "UploadFile " & sLocalFile & " -> " & sRemoteFile
527+
          Else
528-
    
528+
            lCurrent = lCurrent + lBytes
529-
    hLocal = OpenFile(sLocalFile, False)
529+
            RaiseEvent Progress(lCurrent, lSize, bCancel)
530-
    If hLocal = 0 Then
530+
            If bCancel Then
531-
        Exit Function
531+
              bRead = False
532
              bRet = False
533-
    
533+
534-
    If nMode = ffm_Ascii Then
534+
          End If
535-
        lMode = FTP_TRANSFER_TYPE_ASCII + FTP_TRANSFER_NO_CACHE
535+
536-
    Else
536+
          bRead = False
537-
        lMode = FTP_TRANSFER_TYPE_BINARY + FTP_TRANSFER_NO_CACHE
537+
          bRet = True
538
        End If
539-
    Err.Clear
539+
      Else
540-
    hRemote = FtpOpenFile(mFTP.hConnect, sRemoteFile, GENERIC_WRITE, lMode, mFTP.hCtx)
540+
541-
    If (hRemote = 0) Then
541+
        bRead = False
542
        bRet = False
543-
        Close #hLocal
543+
      End If
544-
        Exit Function
544+
545
  End If
546
  
547-
    
547+
  If bRet = True Then
548-
    lSize = FileLen(sLocalFile)
548+
    RaiseEvent Progress(lCurrent, lSize, bCancel)
549-
    RaiseEvent Progress(lCurrent, lSize)
549+
  End If
550-
    
550+
  Call FtpCloseFile(hRemote)
551-
    ' loop: read from file and write to ftp
551+
  If bRet = True Then
552
    FtpResponse
553
  End If
554
  Close #hLocal
555-
        If lRemainder < 1024 Then
555+
  
556-
            lBuff = lRemainder
556+
  DownloadFile = bRet
557
End Function
558-
            lBuff = 1024
558+
559
' .:::: upload (send) a file to ftp server
560-
        ReDim cbBuff(lBuff - 1)
560+
561-
        lRemainder = lRemainder - lBuff
561+
  Dim lMode As FTP_TRANSFER_TYPE
562-
        Err.Clear
562+
  Dim hLocal As Long, hRemote As Long
563-
        Get #hLocal, , cbBuff
563+
  Dim lSizeLow As Long, lSizeHigh As Long
564-
        If Err.Number <> 0 Then
564+
  Dim lSize As Long, lCurrent As Long, lRemainder As Long
565
  Dim bRet As Boolean, bWrite As Boolean
566
  Dim cbBuff() As Byte, lBuff As Long, lBytes As Long
567
  Dim bCancel As Boolean
568
  
569
  On Local Error Resume Next
570-
            lBytes = 0
570+
  bRet = False
571-
            bWrite = FtpWriteFile(hRemote, cbBuff(0), lBuff, lBytes)
571+
  lCurrent = 0
572-
            If (bWrite <> False) Then
572+
  UploadFile = bRet
573-
                If (lBytes > 0) Then
573+
  DbgTrace "UploadFile " & sLocalFile & " -> " & sRemoteFile
574-
                    lCurrent = lCurrent + lBytes
574+
  
575-
                    RaiseEvent Progress(lCurrent, lSize)
575+
  ' open local file for reading
576-
                Else
576+
  hLocal = OpenFile(sLocalFile, False)
577-
                    bWrite = False
577+
  If hLocal = 0 Then
578-
                    bRet = True
578+
579-
                End If
579+
  End If
580-
                If lRemainder < 1 Then
580+
  
581-
                    bWrite = False
581+
  ' open remote file for writing
582-
                    bRet = True
582+
  If nMode = ffm_Ascii Then
583-
                End If
583+
    lMode = FTP_TRANSFER_TYPE_ASCII
584-
            Else
584+
  Else
585-
                DecodeError
585+
    lMode = FTP_TRANSFER_TYPE_BINARY
586-
                bWrite = False
586+
  End If
587-
                bRet = False
587+
  lMode = lMode + FTP_TRANSFER_NO_CACHE
588
  Err.Clear
589
  hRemote = FtpOpenFile(mFTP.hConnect, sRemoteFile, GENERIC_WRITE, lMode, mFTP.hCtx)
590
  If (hRemote = 0) Then
591-
    
591+
    DecodeError
592-
    If bRet = True Then
592+
593-
        RaiseEvent Progress(lCurrent, lSize)
593+
594
  End If
595-
    Call FtpCloseFile(hRemote)
595+
  FtpResponse
596-
    If bRet = True Then
596+
  
597-
        FtpResponse
597+
  lSize = FileLen(sLocalFile)
598
  RaiseEvent Progress(lCurrent, lSize, bCancel)
599
  
600-
    
600+
  ' loop: read from file and write to ftp
601-
    UploadFile = bRet
601+
  If Not bCancel Then
602
    bRet = False
603
    lRemainder = lSize
604
    Do
605
      ' read a chunk from local file
606
      If lRemainder < 1024 Then
607
        lBuff = lRemainder
608
      Else
609
        lBuff = 1024
610-
    Dim lSize As Long, lErrNo As Long, lRet As Long
610+
      End If
611-
    Dim hMod As Long
611+
      ReDim cbBuff(lBuff - 1)
612-
    
612+
      lRemainder = lRemainder - lBuff
613-
    lErrNo = Err.LastDllError
613+
      Err.Clear
614-
    lSize = 2048
614+
      Get #hLocal, , cbBuff
615-
    mFTP.dwLastError = 0
615+
      If Err.Number <> 0 Then
616
        RaiseEvent Failure(Err.Number, Err.Description)
617
        DbgTrace "Error: " & Err.Number & " " & Err.Description
618-
    If (lRet <> 0) And (mFTP.dwLastError <> 0) Then
618+
        bWrite = False
619-
        mFTP.szLastError = GetSZ(mFTP.szLastError)
619+
        bRet = False
620-
    Else
620+
      Else
621-
        mFTP.dwLastError = lErrNo
621+
        ' write a chunk to remote file
622-
        mFTP.szLastError = ""
622+
623-
        hMod = GetModuleHandle("wininet.dll")
623+
        bWrite = FtpWriteFile(hRemote, cbBuff(0), lBuff, lBytes)
624-
        If hMod <> 0 Then
624+
        If (bWrite <> False) Then
625-
            lSize = 2048
625+
          If (lBytes > 0) Then
626-
            mFTP.szLastError = String(lSize, vbNullChar)
626+
            lCurrent = lCurrent + lBytes
627-
            lRet = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, hMod, lErrNo, 0, mFTP.szLastError, lSize, 0)
627+
            RaiseEvent Progress(lCurrent, lSize, bCancel)
628-
            mFTP.dwLastError = lErrNo
628+
            If bCancel Then
629-
            mFTP.szLastError = GetSZ(mFTP.szLastError)
629+
              bWrite = False
630
              bRet = False
631-
        If Len(mFTP.szLastError) < 1 Then
631+
632-
            mFTP.szLastError = "Unexpected error"
632+
          Else
633
            bWrite = False
634
            bRet = True
635-
    RaiseEvent Failure(mFTP.dwLastError, mFTP.szLastError)
635+
          End If
636-
    DbgTrace "Failure: " & mFTP.dwLastError & " " & mFTP.szLastError
636+
          If lRemainder < 1 Then
637
            bWrite = False
638
            bRet = True
639
          End If
640
        Else
641-
    Dim lSize As Long, lErrNo As Long
641+
          DecodeError
642-
    Dim lCode As Long, sData As String
642+
          bWrite = False
643-
    Dim vaData As Variant, iData As Long
643+
          bRet = False
644-
    Dim sMsg As String
644+
645-
    
645+
      End If
646
    Loop While (bWrite <> False)
647-
    lSize = 2048
647+
  End If
648-
    lCode = 0
648+
  If bRet = True Then
649-
    sData = String(lSize, vbNullChar)
649+
    RaiseEvent Progress(lCurrent, lSize, bCancel)
650-
    If InternetError(lCode, sData, lSize) Then
650+
  End If
651-
        sData = GetSZ(sData)
651+
  Call FtpCloseFile(hRemote)
652-
        vaData = Split(sData, vbCrLf)
652+
  If bRet = True Then
653-
        For iData = LBound(vaData) To UBound(vaData)
653+
654-
            sMsg = Trim(Replace(vaData(iData), vbCr, " "))
654+
  End If
655-
            sMsg = Trim(Replace(sMsg, vbLf, " "))
655+
  Close #hLocal
656-
            If Len(sMsg) > 0 Then
656+
  
657-
                RaiseEvent Message(sMsg)
657+
  UploadFile = bRet
658-
                DbgTrace "Message " & sMsg
658+
659
660-
        Next iData
660+
661
' :: Internal service functions - not visible from outer program
662
' :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
663
664
' ==== decode error and signal it
665
Private Sub DecodeError()
666
  Dim lSize As Long, lErrNo As Long, lRet As Long
667-
    GetSZ = Mid(sBuff, 1, InStr(sBuff & vbNullChar, vbNullChar) - 1)
667+
  Dim hMod As Long
668
  
669
  lErrNo = Err.LastDllError
670
  lSize = 2048
671
  mFTP.dwLastError = 0
672-
    Dim tDATA As VB_FIND_DATA
672+
  If lErrNo = ERROR_INTERNET_EXTENDED_ERROR Then
673-
    Dim cTemp As Currency
673+
    ' got an FTP error message, try retrieving it
674-
    Dim st As SYSTEMTIME
674+
675-
    
675+
676
  End If
677-
    With tDATA
677+
  If (lRet <> 0) And (mFTP.dwLastError <> 0) Then
678-
        If (tFIND.dwFileAttributes And vbDirectory) = 0 Then
678+
    ' got the message, go on
679-
            CopyMemory cTemp, tFIND.nFileSizeLow, 4
679+
    mFTP.szLastError = GetSZ(mFTP.szLastError)
680-
            CopyMemory ByVal VarPtr(cTemp) + 4, tFIND.nFileSizeHigh, 4
680+
  Else
681-
            .dFileSize = CDbl(cTemp) * 10000#
681+
    ' retrieve the message from system
682
    mFTP.dwLastError = lErrNo
683-
        FileTimeToSystemTime tFIND.ftLastWriteTime, st
683+
    mFTP.szLastError = ""
684-
        .dtLastWriteTime = DateSerial(st.wYear, st.wMonth, st.wDay) + TimeSerial(st.wHour, st.wMinute, st.wSecond) + (st.wMilliseconds / 86400000)
684+
    hMod = GetModuleHandle("wininet.dll")
685-
        .vFileAttributes = tFIND.dwFileAttributes
685+
    If hMod <> 0 Then
686-
        .sFileName = GetSZ(tFIND.cFileName)
686+
      lSize = 2048
687-
        .sAlternate = GetSZ(tFIND.cAlternate) ' <-- not used
687+
      mFTP.szLastError = String(lSize, vbNullChar)
688-
        RaiseEvent DirLine(.vFileAttributes, .dtLastWriteTime, .dFileSize, .sFileName)
688+
      lRet = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, hMod, lErrNo, 0, mFTP.szLastError, lSize, 0)
689-
        DbgTrace "DirLine " & Format(.dtLastWriteTime, "YYYY-MM-DD HH:NN:SS") & " " & Format(.dFileSize, "#,###,###,###,##0") & " " & .sFileName
689+
      mFTP.dwLastError = lErrNo
690-
    End With
690+
      mFTP.szLastError = GetSZ(mFTP.szLastError)
691
    End If
692
    If Len(mFTP.szLastError) < 1 Then
693
      ' unable to retrieve the error message
694
      mFTP.szLastError = "Unexpected error."
695-
    Dim cTemp As Currency
695+
696-
    
696+
  End If
697
  RaiseEvent Failure(mFTP.dwLastError, mFTP.szLastError)
698-
    CopyMemory cTemp, lSizeLow, 4
698+
  DbgTrace "Failure: " & mFTP.dwLastError & " " & mFTP.szLastError
699-
    CopyMemory ByVal VarPtr(cTemp) + 4, lSizeHigh, 4
699+
700-
    DecodeSize = CDbl(cTemp) * 10000#
700+
701
' ==== FTP responses
702
Private Sub FtpResponse()
703
  Dim lSize As Long, lErrNo As Long
704
  Dim lCode As Long, sData As String
705-
    Dim hFile As Long, bRet As Boolean
705+
  Dim vaData As Variant, iData As Long
706-
    
706+
  Dim sMsg As String
707
  
708
  On Local Error Resume Next
709-
    hFile = FreeFile
709+
  lSize = 2048
710
  lCode = 0
711-
        On Local Error Resume Next
711+
  sData = String(lSize, vbNullChar)
712-
        SetAttr sFileName, vbNormal
712+
  ' retrieve the FTP response
713-
        Kill sFileName
713+
  If InternetError(lCode, sData, lSize) Then
714-
        On Local Error GoTo Catch
714+
    ' response may be multiline, split it
715-
        Open sFileName For Binary Access Write Lock Read Write As #hFile
715+
    sData = GetSZ(sData)
716-
    Else
716+
    vaData = Split(sData, vbCrLf)
717-
        Open sFileName For Binary Access Read Lock Read Write As #hFile
717+
    ' loop over message lines
718
    For iData = LBound(vaData) To UBound(vaData)
719-
    bRet = True
719+
      sMsg = Trim(Replace(vaData(iData), vbCr, " "))
720-
    
720+
      sMsg = Trim(Replace(sMsg, vbLf, " "))
721
      If Len(sMsg) > 0 Then
722
        RaiseEvent Message(sMsg)
723-
    If bRet = False Then
723+
        DbgTrace "Message " & sMsg
724-
        Close #hFile
724+
      End If
725-
        hFile = 0
725+
    Next iData
726-
        If bWrite Then
726+
  End If
727-
            Kill sFileName
727+
728
729
' ==== cut an AsciiZ to its length
730-
    OpenFile = hFile
730+
731
  On Local Error Resume Next
732-
    
732+
  GetSZ = Mid(sBuff, 1, InStr(sBuff & vbNullChar, vbNullChar) - 1)
733
End Function
734-
    RaiseEvent Failure(Err.Number, Err.Description)
734+
735-
    DbgTrace "Error: " & Err.Number & " " & Err.Description
735+
736
Private Sub DecodeDirEntry(tFIND As WIN32_FIND_DATA)
737-
    Resume BailOut
737+
  Dim tDATA As VB_FIND_DATA
738
  Dim cTemp As Currency
739
  Dim st As SYSTEMTIME
740
  
741
  On Local Error Resume Next
742-
    Dim sTxt As String
742+
  With tDATA
743-
    
743+
    ' if it's a regular file, calculate size
744
    If (tFIND.dwFileAttributes And vbDirectory) = 0 Then
745-
    If mbDebug = True Then
745+
      CopyMemory cTemp, tFIND.nFileSizeLow, 4
746-
        sTxt = "[" & App.EXEName & "][FTP] " & sMsg & vbCrLf
746+
      CopyMemory ByVal VarPtr(cTemp) + 4, tFIND.nFileSizeHigh, 4
747-
        Call OutputDebugString(sTxt)
747+
      .dFileSize = CDbl(cTemp) * 10000#
748
    End If
749
    ' convert file time and set other infos
750
    FileTimeToSystemTime tFIND.ftLastWriteTime, st
751
    .dtLastWriteTime = DateSerial(st.wYear, st.wMonth, st.wDay) + TimeSerial(st.wHour, st.wMinute, st.wSecond) + (st.wMilliseconds / 86400000)
752
    .vFileAttributes = tFIND.dwFileAttributes
753
    .sFileName = GetSZ(tFIND.cFileName)
754
    .sAlternate = GetSZ(tFIND.cAlternate) ' <-- not used
755
    RaiseEvent DirLine(.vFileAttributes, .dtLastWriteTime, .dFileSize, .sFileName)
756
    DbgTrace "DirLine " & Format(.dtLastWriteTime, "YYYY-MM-DD HH:NN:SS") & " " & Format(.dFileSize, "#,###,###,###,##0") & " " & .sFileName
757
  End With
758
End Sub
759
760
' ==== decode a qword quantity
761
Private Function DecodeSize(ByVal lSizeLow As Long, ByVal lSizeHigh As Long) As Double
762
  Dim cTemp As Currency
763
  
764
  On Local Error Resume Next
765
  CopyMemory cTemp, lSizeLow, 4
766
  CopyMemory ByVal VarPtr(cTemp) + 4, lSizeHigh, 4
767
  DecodeSize = CDbl(cTemp) * 10000#
768
End Function
769
770
' ==== Open a file for read or write
771
Private Function OpenFile(ByVal sFileName As String, Optional bWrite As Boolean = False) As Long
772
  Dim hFile As Long, bRet As Boolean
773
  
774
  On Local Error GoTo Catch
775
  bRet = False
776
  hFile = FreeFile
777
  If bWrite Then
778
    ' write mode, delete local file
779
    On Local Error Resume Next
780
    SetAttr sFileName, vbNormal
781
    Kill sFileName
782
    ' open file in binary write mode with exclusive access
783
    On Local Error GoTo Catch
784
    Open sFileName For Binary Access Write Lock Read Write As #hFile
785
  Else
786
    ' open file in binary read mode with exclusive access
787
    Open sFileName For Binary Access Read Lock Read Write As #hFile
788
  End If
789
  bRet = True
790
  
791
BailOut:
792
  ' common exit point
793
  On Local Error Resume Next
794
  If bRet = False Then
795
    ' got an error
796
    Close #hFile
797
    hFile = 0
798
    If bWrite Then
799
      Kill sFileName
800
    End If
801
  End If
802
  OpenFile = hFile
803
  Exit Function
804
  
805
Catch:
806
  ' error handler
807
  RaiseEvent Failure(Err.Number, Err.Description)
808
  DbgTrace "Error: " & Err.Number & " " & Err.Description
809
  bRet = False
810
  Resume BailOut
811
End Function
812
813
' ==== writes a debug message
814
Private Function DbgTrace(ByVal sMsg As String)
815
  Dim sTxt As String
816
  
817
  On Local Error Resume Next
818
  If mbDebug = True Then
819
    ' compose and emit the trace message
820
    Debug.Print sMsg
821
    sTxt = "[" & msAppName & "][FTP] " & sMsg & vbCrLf
822
    Call OutputDebugString(sTxt)
823
  End If
824
End Function
825
826
' ==== gets the application name
827
Private Function GetAppName() As String
828
  Dim nPos As Integer
829
  Dim sName As String
830
  
831
  sName = App.EXEName
832
  nPos = InStr(sName, ".")
833
  If nPos > 0 Then
834
    sName = Mid(sName, 1, nPos - 1)
835
  End If
836
  GetAppName = sName
837
End Function