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 |