Page 1 of 2

[Class] FTP

Posted: 27 Jul 2020, 07:36
by jNizM
Class FTP
AutoHotkey wrapper for FTP Sessions (msdn-docs)


Source
Mirror: Release on GitHub

Code: Select all

; ===============================================================================================================================
; AutoHotkey wrapper for FTP Sessions
;
; Author ....: jNizM
; Released ..: 2020-07-26
; Modified ..: 2020-07-31
; Github ....: https://github.com/jNizM/Class_FTP
; Forum .....: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=79142
; ===============================================================================================================================


class FTP
{

	static hWININET := DllCall("LoadLibrary", "str", "wininet.dll", "ptr")


	; ===== PUBLIC METHODS ======================================================================================================

	Close(hInternet)
	{
		if (hInternet)
			if (this.InternetCloseHandle(hInternet))
				return true
		return false
	}


	Connect(hInternet, ServerName, Port := 21, UserName := "", Password := "", FTP_PASV := 1)
	{
		if (hConnect := this.InternetConnect(hInternet, ServerName, Port, UserName, Password, FTP_PASV))
			return hConnect
		return false
	}


	CreateDirectory(hConnect, Directory)
	{
		if (DllCall("wininet\FtpCreateDirectory", "ptr", hConnect, "ptr", &Directory))
			return true
		return false
	}


	DeleteFile(hConnect, FileName)
	{
		if (DllCall("wininet\FtpDeleteFile", "ptr", hConnect, "ptr", &FileName))
			return true
		return false
	}


	Disconnect(hConnect)
	{
		if (hConnect)
			if (this.InternetCloseHandle(hConnect))
				return true
		return false
	}


	FindFiles(hConnect, SearchFile := "*.*")
	{
		static FILE_ATTRIBUTE_DIRECTORY := 0x10

		Files := []
		find := this.FindFirstFile(hConnect, hEnum, SearchFile)
		if !(find.FileAttr & FILE_ATTRIBUTE_DIRECTORY)
			Files.Push(find)

		while (find := this.FindNextFile(hEnum))
			if !(find.FileAttr & FILE_ATTRIBUTE_DIRECTORY)
				Files.Push(find)
		this.Close(hEnum)
		return Files
	}


	FindFolders(hConnect, SubDirectories := "*.*")
	{
		static FILE_ATTRIBUTE_DIRECTORY := 0x10

		Folders := []
		find := this.FindFirstFile(hConnect, hEnum, SubDirectories)
		if (find.FileAttr & FILE_ATTRIBUTE_DIRECTORY)
			Folders.Push(find)
		while (find := this.FindNextFile(hEnum))
			if (find.FileAttr & FILE_ATTRIBUTE_DIRECTORY)
				Folders.Push(find)
		this.Close(hEnum)
		return Folders
	}


	GetCurrentDirectory(hConnect)
	{
		static MAX_PATH := 260 + 8

		BUFFER_SIZE := VarSetCapacity(CurrentDirectory, MAX_PATH, 0)
		if (DllCall("wininet\FtpGetCurrentDirectory", "ptr", hConnect, "ptr", &CurrentDirectory, "uint*", BUFFER_SIZE))
			return StrGet(&CurrentDirectory)
		return false
	}


	GetFile(hConnect, RemoteFile, NewFile, OverWrite := 0, Flags := 0)
	{
		if (DllCall("wininet\FtpGetFile", "ptr", hConnect, "ptr", &RemoteFile, "ptr", &NewFile, "int", !OverWrite, "uint", 0, "uint", Flags, "uptr", 0))
			return true
		return false
	}


	GetFileSize(hConnect, FileName, SizeFormat := "auto", SizeSuffix := false)
	{
		static GENERIC_READ := 0x80000000

		if (hFile := this.OpenFile(hConnect, FileName, GENERIC_READ))
		{
			VarSetCapacity(FileSizeHigh, 8)
			if (FileSizeLow := DllCall("wininet\FtpGetFileSize", "ptr", hFile, "uint*", FileSizeHigh, "uint"))
			{
				this.InternetCloseHandle(hFile)
				return this.FormatBytes(FileSizeLow + (FileSizeHigh << 32), SizeFormat, SizeSuffix)
			}
			this.InternetCloseHandle(hFile)
		}
		return false
	}


	Open(Agent, Proxy := "", ProxyBypass := "")
	{
		if (hInternet := this.InternetOpen(Agent, Proxy, ProxyBypass))
			return hInternet
		return false
	}


	PutFile(hConnect, LocaleFile, RemoteFile, Flags := 0)
	{
		if (DllCall("wininet\FtpPutFile", "ptr", hConnect, "ptr", &LocaleFile, "ptr", &RemoteFile, "uint", Flags, "uptr", 0))
			return true
		return false
	}


	RemoveDirectory(hConnect, Directory)
	{
		if (DllCall("wininet\FtpRemoveDirectory", "ptr", hConnect, "ptr", &Directory))
			return true
		return false
	}


	RenameFile(hConnect, ExistingFile, NewFile)
	{
		if (DllCall("wininet\FtpRenameFile", "ptr", hConnect, "ptr", &ExistingFile, "ptr", &NewFile))
			return true
		return false
	}


	SetCurrentDirectory(hconnect, Directory)
	{
		if (DllCall("wininet\FtpSetCurrentDirectory", "ptr", hConnect, "ptr", &Directory))
			return true
		return false
	}


	; ===== PRIVATE METHODS =====================================================================================================

	FileAttributes(Attributes)
	{
		static FILE_ATTRIBUTE := { 0x1: "READONLY", 0x2: "HIDDEN", 0x4: "SYSTEM", 0x10: "DIRECTORY", 0x20: "ARCHIVE", 0x40: "DEVICE", 0x80: "NORMAL"
						, 0x100: "TEMPORARY", 0x200: "SPARSE_FILE", 0x400: "REPARSE_POINT", 0x800: "COMPRESSED", 0x1000: "OFFLINE"
						, 0x2000: "NOT_CONTENT_INDEXED", 0x4000: "ENCRYPTED", 0x8000: "INTEGRITY_STREAM", 0x10000: "VIRTUAL"
						, 0x20000: "NO_SCRUB_DATA", 0x40000: "RECALL_ON_OPEN", 0x400000: "RECALL_ON_DATA_ACCESS" }
		GetFileAttributes := []
		for k, v in FILE_ATTRIBUTE
			if (k & Attributes)
				GetFileAttributes.Push(v)
		return GetFileAttributes
	}


	FindData(ByRef WIN32_FIND_DATA, SizeFormat := "auto", SizeSuffix := false)
	{
		static MAX_PATH := 260
		static MAXDWORD := 0xffffffff

		addr := &WIN32_FIND_DATA
		FIND_DATA := []
		FIND_DATA["FileAttr"]          := NumGet(addr + 0, "uint")
		FIND_DATA["FileAttributes"]    := this.FileAttributes(NumGet(addr + 0, "uint"))
		FIND_DATA["CreationTime"]      := this.FileTime(NumGet(addr +  4, "uint64"))
		FIND_DATA["LastAccessTime"]    := this.FileTime(NumGet(addr + 12, "uint64"))
		FIND_DATA["LastWriteTime"]     := this.FileTime(NumGet(addr + 20, "uint64"))
		FIND_DATA["FileSize"]          := this.FormatBytes((NumGet(addr + 28, "uint") * (MAXDWORD + 1)) + NumGet(addr + 32, "uint"), SizeFormat, SizeSuffix)
		FIND_DATA["FileName"]          := StrGet(addr + 44, "utf-16")
		FIND_DATA["AlternateFileName"] := StrGet(addr + 44 + MAX_PATH * (A_IsUnicode ? 2 : 1), "utf-16")
		return FIND_DATA
	}


	FindFirstFile(hConnect, ByRef hFind, SearchFile := "*.*", SizeFormat := "auto", SizeSuffix := false)
	{
		VarSetCapacity(WIN32_FIND_DATA, (A_IsUnicode ? 592 : 320), 0)
		if (hFind := DllCall("wininet\FtpFindFirstFile", "ptr", hConnect, "str", SearchFile, "ptr", &WIN32_FIND_DATA, "uint", 0, "uint*", 0))
			return this.FindData(WIN32_FIND_DATA, SizeFormat, SizeSuffix)
		VarSetCapacity(WIN32_FIND_DATA, 0)
		return false
	}


	FindNextFile(hFind, SearchFile := "*.*", SizeFormat := "auto", SizeSuffix := false)
	{
		VarSetCapacity(WIN32_FIND_DATA, (A_IsUnicode ? 592 : 320), 0)
		if (DllCall("wininet\InternetFindNextFile", "ptr", hFind, "ptr", &WIN32_FIND_DATA))
			return this.FindData(WIN32_FIND_DATA, SizeFormat, SizeSuffix)
		VarSetCapacity(WIN32_FIND_DATA, 0)
		return false
	}


	FileTime(addr)
	{
		this.FileTimeToSystemTime(addr, SystemTime)
		this.SystemTimeToTzSpecificLocalTime(&SystemTime, LocalTime)
		return Format("{:04}{:02}{:02}{:02}{:02}{:02}"
					, NumGet(LocalTime,  0, "ushort")
					, NumGet(LocalTime,  2, "ushort")
					, NumGet(LocalTime,  6, "ushort")
					, NumGet(LocalTime,  8, "ushort")
					, NumGet(LocalTime, 10, "ushort")
					, NumGet(LocalTime, 12, "ushort"))
	}


	FileTimeToSystemTime(FileTime, ByRef SystemTime)
	{
		VarSetCapacity(SystemTime, 16, 0)
		if (DllCall("FileTimeToSystemTime", "int64*", FileTime, "ptr", &SystemTime))
			return true
		return false
	}


	FormatBytes(bytes, SizeFormat := "auto", suffix := false)
	{
		static SFBS_FLAGS_ROUND_TO_NEAREST_DISPLAYED_DIGIT    := 0x0001
		static SFBS_FLAGS_TRUNCATE_UNDISPLAYED_DECIMAL_DIGITS := 0x0002
		static S_OK := 0

		if (SizeFormat = "auto")
		{
			size := VarSetCapacity(buf, 1024, 0)
			if (DllCall("shlwapi\StrFormatByteSizeEx", "int64", bytes, "int", SFBS_FLAGS_ROUND_TO_NEAREST_DISPLAYED_DIGIT, "str", buf, "uint", size) = S_OK)
				output := buf
		}
		else if (SizeFormat = "kilobytes" || SizeFormat = "kb")
			output := Round(bytes / 1024, 2) . (suffix ? " KB" : "")
		else if (SizeFormat = "megabytes" || SizeFormat = "mb")
			output := Round(bytes / 1024**2, 2) . (suffix ? " MB" : "")
		else if (SizeFormat = "gigabytes" || SizeFormat = "gb")
			output := Round(bytes / 1024**3, 2) . (suffix ? " GB" : "")
		else if (SizeFormat = "terabytes" || SizeFormat = "tb")
			output := Round(bytes / 1024**4, 2) . (suffix ? " TB" : "")
		else
			output := Round(bytes, 2) . (suffix ? " Bytes" : "")
		return output
	}


	InternetCloseHandle(hInternet)
	{
		if (DllCall("wininet\InternetCloseHandle", "ptr", hInternet))
			return true
		return false
	}


	InternetConnect(hInternet, ServerName, Port := 21, UserName := "", Password := "", FTP_PASV := 1)
	{
		static INTERNET_DEFAULT_FTP_PORT := 21
		static INTERNET_SERVICE_FTP      := 1
		static INTERNET_FLAG_PASSIVE     := 0x08000000

		if (hConnect := DllCall("wininet\InternetConnect", "ptr",    hInternet
														 , "ptr",    &ServerName
														 , "ushort", (Port = 21 ? INTERNET_DEFAULT_FTP_PORT : Port)
														 , "ptr",    (UserName ? &UserName : 0)
														 , "ptr",    (Password ? &Password : 0)
														 , "uint",   INTERNET_SERVICE_FTP
														 , "uint",   (FTP_PASV ? INTERNET_FLAG_PASSIVE : 0)
														 , "uptr",   0
														 , "ptr"))
			return hConnect
		return false
	}


	InternetOpen(Agent, Proxy := "", ProxyBypass := "")
	{
		static INTERNET_OPEN_TYPE_DIRECT := 1
		static INTERNET_OPEN_TYPE_PROXY  := 3

		if (hInternet := DllCall("wininet\InternetOpen", "ptr",  &Agent
													   , "uint", (Proxy ? INTERNET_OPEN_TYPE_PROXY : INTERNET_OPEN_TYPE_DIRECT)
													   , "ptr",  (Proxy ? &Proxy : 0)
													   , "ptr",  (ProxyBypass ? &ProxyBypass : 0)
													   , "uint", 0
													   , "ptr"))
			return hInternet
		return false
	}


	OpenFile(hConnect, FileName, Access)
	{
		static FTP_TRANSFER_TYPE_BINARY := 2

		if (hFTPSESSION := DllCall("wininet\FtpOpenFile", "ptr", hConnect, "ptr", &FileName, "uint", Access, "uint", FTP_TRANSFER_TYPE_BINARY, "uptr", 0))
			return hFTPSESSION
		return false
	}


	SystemTimeToTzSpecificLocalTime(SystemTime, ByRef LocalTime)
	{
		VarSetCapacity(LocalTime, 16, 0)
		if (DllCall("SystemTimeToTzSpecificLocalTime", "ptr", 0, "ptr", SystemTime, "ptr", &LocalTime))
			return true
		return false
	}

}

; ===============================================================================================================================

Questions / Bugs / Issues
If you notice any kind of bugs or issues, report them here. Same for any kind of questions.


Copyright and License
The Unlicense

Re: [Class] FTP

Posted: 27 Jul 2020, 07:36
by jNizM
Examples
Writes a file to the server.

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
FTP.PutFile(hSession, "C:\Temp\testfile.txt", "testfile.txt")
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Retrieves a file from the server.

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
FTP.GetFile(hSession, "testfile.txt", "C:\Temp\testfile.txt")
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Retrieves the file size of the requested FTP resource.

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
MsgBox % FTP.GetFileSize(hSession, "testfile.txt")
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Deletes a file from the server.

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
FTP.DeleteFile(hSession, "testfile.txt")
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Creates a new directory on the server.

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
FTP.CreateDirectory(hSession, "Test_Folder")
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Changes the client's current directory on the server.

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
FTP.SetCurrentDirectory(hSession, "Test_Folder")
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Returns the client's current directory on the server.

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
MsgBox % FTP.GetCurrentDirectory(hSession)
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Deletes a directory on the server.

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
FTP.RemoveDirectory(hSession, "Test_Folder")
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Enumerate all Files in root directory. (!!! EXPERIMENTAL !!!)

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
for k, File in FTP.FindFiles(hSession)
	MsgBox % File.FileName
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Enumerate all Files in a subdirectory. (!!! EXPERIMENTAL !!!)

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
for k, File in FTP.FindFiles(hSession, "/Folder 2")
	MsgBox % File.FileName
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Enumerate all Folders in root directory. (!!! EXPERIMENTAL !!!)

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
for k, Folder in FTP.FindFolders(hSession)
	MsgBox % Folder.FileName
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Enumerate all Folders in a subdirectory. (!!! EXPERIMENTAL !!!)

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
for k, Folder in FTP.FindFolders(hSession, "/Folder 2")
	MsgBox % Folder.FileName
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Todo
- FtpFindFirstFile
- InternetFindNextFile
- InternetReadFile

Re: [Class] FTP

Posted: 27 Jul 2020, 08:18
by Tre4shunter
Very Nice!

Was just looking like something like this for a project - i will be updating :)

Also a nice little Class/dllcall demo!

Thanks!

Re: [Class] FTP

Posted: 28 Jul 2020, 11:20
by ahk7

Re: [Class] FTP

Posted: 29 Jul 2020, 00:37
by jNizM
ahk7 wrote:
28 Jul 2020, 11:20
Is the the updated version of https://github.com/jNizM/AHK_Scripts/blob/master/src/ftp/Class_FTP.ahk ?
Something like that. I'll delete the old one on GitHub as soon as the class is done here.

Re: [Class] FTP

Posted: 30 Jul 2020, 07:49
by jNizM
Added FindFiles and FindFolders. Experimental! Please report bugs and issues.

Re: [Class] FTP

Posted: 13 Aug 2020, 16:39
by ahk7
I tested .FindFiles with a 30K+ files folder and works correctly as do other functions I tried. I'll try it in production next, but so far, so good.

Re: [Class] FTP

Posted: 07 Jun 2021, 13:22
by blackjoker
Great work!!! Worked for me... Thank you!

Re: [Class] FTP

Posted: 09 Jun 2021, 09:46
by blackjoker
Maybe someone could tell me, why these functions don't have filemove to other directory function? I dont want to use formula Download file then upload file to other dir, because it uses traffic. Where am i wrong? How do you move files from one FTP dir to other FTP dir?

Re: [Class] FTP

Posted: 13 Jun 2021, 05:44
by blackjoker
I had an issue, when repeatedly downloading files from FTP server. :?: When code checks server every 20 seconds, and downloads smth it starts to download from Win Cache :think: even if in server file have been deleted.
So for me helps additionals flags :dance: in method:

Code: Select all

	FindFiles(hConnect, SearchFile := "*.*")
	{
		static FILE_ATTRIBUTE_DIRECTORY := 0x10
		static INTERNET_FLAG_RELOAD := 0x80000000
		static INTERNET_FLAG_DONT_CACHE := 0x04000000
		

		Files := []
		
		find := this.FindFirstFile(hConnect, hEnum, SearchFile)
		if !(find.FileAttr & FILE_ATTRIBUTE_DIRECTORY & INTERNET_FLAG_NO_CACHE_WRITE & INTERNET_FLAG_RELOAD)
			Files.Push(find)

		while (find := this.FindNextFile(hEnum))
			if !(find.FileAttr & FILE_ATTRIBUTE_DIRECTORY & INTERNET_FLAG_NO_CACHE_WRITE & INTERNET_FLAG_RELOAD)
				Files.Push(find)
		this.Close(hEnum)
		find := 
		return Files
	}
From msdocs:
https://docs.microsoft.com/en-us/windows/win32/wininet/ftp-sessions
"If the application makes changes on the FTP server or if the FTP server changes frequently, the INTERNET_FLAG_NO_CACHE_WRITE and INTERNET_FLAG_RELOAD flags should be set in FtpFindFirstFile. These flags ensure that the directory information being retrieved from the FTP server is current."

Re: [Class] FTP

Posted: 13 Jun 2021, 06:35
by boiler
blackjoker wrote: Maybe someone could tell me, why these functions don't have filemove to other directory function?
It’s open source, so there’s nothing stopping you from adding that capability. I take it from how you stated your question (as opposed to a feature request) that you consider it is so trivial to add it that there is no reason it shouldn’t be there already.

Re: [Class] FTP

Posted: 13 Jun 2021, 08:16
by blackjoker
boiler wrote:
13 Jun 2021, 06:35
blackjoker wrote: Maybe someone could tell me, why these functions don't have filemove to other directory function?
It’s open source, so there’s nothing stopping you from adding that capability. I take it from how you stated your question (as opposed to a feature request) that you consider it is so trivial to add it that there is no reason it shouldn’t be there already.
Well it was strange to me that there is no such thing and i thought i just missed something. I wanted to use MOVE file in server for backup insted DELETE it forever. And i dont want to use additional traffic for downloading file then UPLOADING file for backup purpose.
boiler from ypur post i understood that it is very hard to code file MOVE inside FTP and there is no function for that.

Re: [Class] FTP

Posted: 13 Jun 2021, 09:50
by malcev

Re: [Class] FTP

Posted: 13 Jun 2021, 10:28
by blackjoker
:superhappy:
Thank you malcev!

... :facepalm: on myself...

Re: [Class] FTP

Posted: 14 Jun 2021, 10:29
by blackjoker
Nothing special, but for me that was a cost of 2-3 days searching... so maybe helps for someone:

Moves file to another FTP location (changes dir location):

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
FTP.RenameFile(hSession, "/existing/directory/old_file_name.ext", "/future/directory/new_file_name.ext")
FTP.Disconnect(hSession)
FTP.Close(hFTP)
Renames file in FTP (changes only file name):

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
FTP.RenameFile(hSession, "/existing/directory/old_file_name.ext", "/existing/directory/new_file_name.ext")
FTP.Disconnect(hSession)
FTP.Close(hFTP)

Re: [Class] FTP

Posted: 27 May 2022, 09:55
by HeXaDeCiMaToR
I'm having issues connecting to an SFTP - SSH Server. Will this Class work with SFTP - SSH?

Re: [Class] FTP

Posted: 27 May 2022, 12:02
by ahk7
HeXaDeCiMaToR wrote:
27 May 2022, 09:55
I'm having issues connecting to an SFTP - SSH Server. Will this Class work with SFTP - SSH?
Have you tried the WinSCP library https://github.com/lipkau/WinSCP.ahk

Re: [Class] FTP

Posted: 12 Jul 2022, 08:18
by wozas123

Code: Select all

XX = %dd%

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "XXXX, 21, "XXX", "XXX")
FTP.PutFile(hSession, "D:\%dd%.txt", "/sdb1/00/00.txt")
FTP.Disconnect(hSession)
FTP.Close(hFTP)

no way to use variables
It doesn't make sense to use
only send one by one

Re: [Class] FTP

Posted: 12 Jul 2022, 10:35
by hd0202
try:

Code: Select all

FTP.PutFile(hSession, "D:\" . dd . ".txt", "/sdb1/00/00.txt")  ; it is an expression !
Hubert

Re: [Class] FTP

Posted: 09 Jun 2023, 15:39
by ShatterCoder
I'd like to submit an addition to the class: FIleRead() which allows you to read a file from the FTP server directly to a variable without needing to write to disk.

Code: Select all

ReadFile(hConnect, FileName)
	{
		static GENERIC_READ := 0x80000000
		if (hFile := this.OpenFile(hConnect, FileName, GENERIC_READ))
		{
			data_out := ""
			chunksize := 1024 ;chunk size to request per dllcall
			VarSetCapacity(buf, chunksize ) 
			VarSetCapacity(bytes_read, 4 * 4) ;dword
			bytes_read := 0
			while (DllCall("wininet\InternetReadFile", "ptr", hFile, "Uptr", &buf, "uint", chunksize , "ptr", &bytes_read) && bytes_read)
				data_out .= StrGet(&Buf + 0, "UTF-8")
			this.InternetCloseHandle(hFile)
			return data_out
		}
		return false
	}
I'm not sure how many people will have need of this particular method, but for myself it was needed because I was having to look through multiple files on an FTP server per second to look for a specific string so avoiding disk read/write time was essential.

example usage:

Code: Select all

hFTP := FTP.Open("AHK-FTP")
hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
Filecontent := FTP.ReadFile(hSession, "testfile.txt")
msgbox, % Filecontent
FTP.Disconnect(hSession)
FTP.Close(hFTP)