I have added enumeration capabilities:
- FtpFindFirstFile
- FtpFindNextFile
- FtpGetFileInfo

I also renamed INetOpen() and INetClose() to INetStart() and INetStop(). So it's less confusing (INetOpen() doesn't actually open a connection).
I have not wrapped FtpCommand because it needs a socket to listen to asynchronous responses. And since all Ftp functions are now wrapped, all common actions can done.

There's one thing that doesn't work: getting creation, last access and last write times. The FILETIME structure must be converted to a SYSTEMTIME structure. I have done that using "FileTimeToSystemTime", but I always get "1/1/1601 0:0:0.0".
I don't know what I miss. Maybe something related to time zones.
If somebody could take a look at that, it would be great.
I have done some testing, not intensive though. So let me know if there're bugs (or any kind of amelioration).
Code:
; Script Title:
; INet.ahk
; Requires AutoHotkey 1.0.47+
;
; Script Function:
; Wraps usage of WinINet.dll functions.
; For now, mostly only FTP functions are available.
;
; Notes:
; Getting times with FtpGetFileInfo doesn't work. It's always "1/1/1601 0:0:0.0".
;
; Sources:
; WinINet Functions: http://msdn2.microsoft.com/en-us/library/aa385473.aspx
; FTP Sessions: http://msdn2.microsoft.com/en-us/library/aa384180.aspx
;
; Examples:
; ; init internet resources
; INetStart()
; ; connection to the FTP server
; hFTP := INetConnect(Server, Port, User, Pwd, "ftp")
; if(hFTP)
; {
; if(FtpPutFile(hFTP, "somefile.txt"))
; Msgbox File uploaded successfuly
; else
; Msgbox "Upload failed: " %A_LastError%
; ; close the FTP connection
; INetCloseHandle(hFTP)
; }
; ; release internet resources
; INetStop()
; ______________________________________________________________
;
; if((hEnum := FtpFindFirstFile(hFTP, "*.*", FTPData)))
; {
; Loop
; {
; FileName := FtpGetFileInfo(FTPData, "Name")
; Size := FtpGetFileInfo(FTPData, "Size")
; IsDir := FtpGetFileInfo(FTPData, "IsDirectory")
; Attrib := FtpGetFileInfo(FTPData, "Attrib")
; CreationTime := FtpGetFileInfo(FTPData, "CreationTime")
; msgbox FileName=%FileName%`nSize=%Size%`nDir=%IsDir%`nAttrib=%Attrib%`nCreationTime=%CreationTime%
; if(!FtpFindNextFile(hEnum, FTPData))
; break
; }
; }
; INetCloseHandle(hEnum)
; ******************************************************************************
; ------------------------------------------------------------------------------
; This function loads and tells the Internet DLL to initialize internal data structures
; and prepare for future calls from the application.
;
; Internet functions are not available before this function is called.
;
; param Proxy [in]
; Specifies an optional name of a proxy server.
; param ProxyBypass [in]
; Specifies an optional list of host names or IP addresses, or both, that should not be routed
; through the proxy when a proxy is used.
; param Agent [in]
; Specifies the name of the application or entity calling the WinINet functions.
; This name is used as the user agent in the HTTP protocol.
; When empty, the name of the script is used
; return
; [bool] true if the function succeeds, otherwise false
;
INetStart(Proxy="", ProxyBypass="", Agent="")
{
global
; get a handle to the WinINet library
inet_hModule := DllCall("LoadLibrary", "str", "wininet.dll")
if(!inet_hModule) {
inet_hModule = 0
return false
}
; INTERNET_OPEN_TYPE_PRECONFIG 0 // use registry configuration
; INTERNET_OPEN_TYPE_DIRECT 1 // direct to net
; INTERNET_OPEN_TYPE_PROXY 3 // via named proxy
; INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY 4 // prevent using java/script/INS
inet_hInternet := DllCall("wininet\InternetOpenA"
, "str", (Agent != "" ? Agent : A_ScriptName)
, "UInt", (Proxy != "" ? 3 : 1)
, "str", Proxy
, "str", ProxyBypass
, "UInt", 0)
If(!inet_hInternet) {
INetCloseHandle(inet_hModule)
return false
}
return true
}
; ------------------------------------------------------------------------------
; Terminates the application's use of WinINet functions and unloads the Internet DLL.
;
; Internet functions are not available after this function call is called.
;
INetStop()
{
global
INetCloseHandle(inet_hInternet)
DllCall("FreeLibrary", "UInt", inet_hModule)
inet_hModule = inet_hInternet = 0
}
; ------------------------------------------------------------------------------
; Closes a single Internet handle, previously opened by INetConnect, FtpOpenFile, ...
;
; param hConnection [in]
; The connection handle to be closed.
; return
; [bool] true if the function succeeds, otherwise false
;
INetCloseHandle(hInternet)
{
return DllCall("wininet\InternetCloseHandle", "UInt", hInternet)
}
; ------------------------------------------------------------------------------
; Opens an File Transfer Protocol (FTP), Gopher, or HTTP session for a given site.
;
; FtpPassive [in]
; The value 1 causes the application to use passive FTP semantics
;
; param Server [in]
; The host name or IP number of a server.
; param Server [in]
; The TCP/IP port to connect to.
; param Username [in]
; The name of the user to log on (default is anonymous)
; param Password [in]
; The password to use to log on (default is anonymous)
; param Service [in]
; Type of service to access. Must be of the following value: http (default), ftp, url or gopher
; param FtpPassive [in]
; Option specific to ftp service. Setting 1 causes the application to use passive FTP semantics.
; return
; the handle of the opened connection
;
INetConnect(Server, Port, Username="anonymous", Password="anonymous", Service="http", FtpPassive=0)
{
global inet_hInternet
hConnection := DllCall("wininet\InternetConnectA"
, "uint", inet_hInternet
, "str", Server
, "uint", Port
, "str", Username
, "str", Password
, "uint", (Service = "ftp" ? 1 : (Service = "gopher" ? 2 : 3)) ; INTERNET_SERVICE_xxx
, "uint", (FtpPassive != 0 ? 0x08000000 : 0) ; INTERNET_FLAG_PASSIVE
, "uint", 0)
return hConnection
}
; ------------------------------------------------------------------------------
; Creates a directory on a FTP server.
;
; param hConnection [in]
; A valid connection handle returned by an INetConnect call (using Type="ftp").
; param Directory [in]
; The name of the directory to be created. This can be a fully qualified path
; or a name relative to the current directory.
; return
; [bool] true if the function succeeds, otherwise false
;
FtpCreateDirectory(hConnection, Directory)
{
return DllCall("wininet\FtpCreateDirectoryA", "uint", hConnection, "str", Directory)
}
; ------------------------------------------------------------------------------
; Removes a directory on a FTP server.
;
; param hConnection [in]
; A valid connection handle returned by an INetConnect call (using Type="ftp").
; param Directory [in]
; The name of the directory to be deleted. This can be a fully qualified path
; or a name relative to the current directory.
; return
; [bool] true if the function succeeds, otherwise false
;
FtpRemoveDirectory(hConnection, Directory)
{
return DllCall("wininet\FtpRemoveDirectoryA", "uint", hConnection, "str", Directory)
}
; ------------------------------------------------------------------------------
; Sets the current working directory for a specified FTP session.
;
; param hConnection [in]
; A valid connection handle returned by an INetConnect call (using Type="ftp").
; param Directory [in]
; The name of the directory to become the current working directory. This can be a fully qualified path
; or a name relative to the current directory.
; return
; [bool] true if the function succeeds, otherwise false
;
FtpSetCurrentDirectory(hConnection, Directory)
{
return DllCall("wininet\FtpSetCurrentDirectoryA", "uint", hConnection, "str", Directory)
}
; ------------------------------------------------------------------------------
; Gets the current working directory of a specified FTP session.
;
; param hConnection [in]
; a valid connection handle returned by an INetConnect call (using Type="ftp")
; param Directory [in]
; a variable that receives the absolute path of the current directory
; return
; [bool] true if the function succeeds, otherwise false
;
FtpGetCurrentDirectory(hConnection, ByRef @Directory)
{
len := 261 ; MAX_PATH 260
VarSetCapacity(@Directory, len)
result := DllCall("wininet\FtpGetCurrentDirectoryA", "uint", hConnection, "str", @Directory, "uint*", len)
VarSetCapacity(@Directory, -1)
return result
}
; ------------------------------------------------------------------------------
; Uploads a file to a FTP server (the remote file is overwritten if it already exists).
;
; param hConnection [in]
; a valid connection handle returned by an INetConnect call (using Type="ftp")
; param LocalFile [in]
; name of the local file to upload
; param RemoteFile [in]
; name of the remote file to create (when empty, the remote file created will have the same name than the local one)
; param TransferType [in]
; "A" for ascii transfert or "B" (default) for binary mode
; return
; [bool] true if the function succeeds, otherwise false
;
FtpPutFile(hConnection, LocalFile, RemoteFile="", TransferType="B")
{
return DllCall("wininet\FtpPutFileA"
, "uint", hConnection
, "str", LocalFile
, "str", (RemoteFile != "" ? RemoteFile : LocalFile)
, "uint", (TransferType == "A" ? 1 : 2) ; FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY
, "uint", 0)
}
; ------------------------------------------------------------------------------
; Downloads a file from a FTP server and stores it localy.
; No caching options for now.
;
; param hConnection [in]
; a valid connection handle returned by an INetConnect call (using Type="ftp")
; param RemoteFile [in]
; name of the remote file to download
; param LocalFile [in]
; name of the local file to create (when empty, the local file created will have the same name than the remote one)
; param TransferType [in]
; "A" for ascii transfert or "B" (default) for binary mode
; param OverWrite [in]
; 0 (default) or 1 to allow the function to proceed if a local file with the specified name already exist
; param LocalAttrib [in]
; File attributes for the new file. This parameter can be any combination of the FILE_ATTRIBUTE_* below.
; FILE_ATTRIBUTE_READONLY 1 0x0001
; FILE_ATTRIBUTE_HIDDEN 2 0x0002
; FILE_ATTRIBUTE_SYSTEM 4 0x0004
; FILE_ATTRIBUTE_NORMAL 128 0x0080
; FILE_ATTRIBUTE_TEMPORARY 256 0x0100
; FILE_ATTRIBUTE_ENCRYPTED 16384 0x4000
; return
; [bool] true if the function succeeds, otherwise false
;
; Caching option:
; INTERNET_FLAG_HYPERLINK 0x00000400 // asking wininet to do hyperlinking semantic which works right for scripts
; INTERNET_FLAG_NEED_FILE 0x00000010 // need a file for this request
; INTERNET_FLAG_RELOAD 0x80000000 // retrieve the original item
; INTERNET_FLAG_RESYNCHRONIZE 0x00000800 // asking wininet to update an item if it is newer
FtpGetFile(hConnection, RemoteFile, LocalFile="", TransferType="B", OverWrite=0, LocalAttrib=0)
{
return DllCall("wininet\FtpGetFileA"
, "uint", hConnection
, "str", RemoteFile
, "str", (LocalFile != "" ? LocalFile : RemoteFile)
, "int", !OverWrite
, "uint", LocalAttrib
, "uint", (TransferType == "A" ? 1 : 2) ; FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY
, "uint", 0)
}
; ------------------------------------------------------------------------------
; Gets the size in bytes of a file stored on a FTP server
;
; param hConnection [in]
; a valid connection handle returned by an INetConnect call (using Type="ftp")
; param File [in]
; name of the remote file
; param TransferType [in]
; "A" for ascii transfert or "B" (default) for binary mode
; return
; [uint] the size in bytes of the remote file
;
FtpGetFileSize(hConnection, File, TransferType="B")
{
hFile := DllCall("wininet\FtpOpenFileA"
, "uint", hConnection
, "str", File
, "uint", 0x80000000 ; GENERIC_READ
, "uint", (TransferType = "A" ? 1 : 2) ; FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY
, "uint", 0)
if(!hFile)
return -1
size := DllCall("wininet\FtpGetFileSize", "uint", hFile, "uint*", size)
INetCloseHandle(hFile)
return size
}
; ------------------------------------------------------------------------------
; Renames (or moves) a file stored on a FTP server
;
; param hConnection [in]
; a valid connection handle returned by an INetConnect call (using Type="ftp")
; param ExistingName [in]
; name of the remote file to be renamed (can be a relative path from the current working directory)
; param NewName [in]
; new name of the remote file (can be a relative path from the current working directory)
; return
; [bool] true if the function succeeds, otherwise false
;
FtpRenameFile(hConnection, ExistingName, NewName)
{
return DllCall("wininet\FtpRenameFileA", "uint", hConnection, "str", ExistingName, "str", NewName)
}
; ------------------------------------------------------------------------------
; Deletes a file stored on a FTP server
;
; param hConnection [in]
; a valid connection handle returned by an INetConnect call (using Type="ftp")
; param File [in]
; the name of the file to be deleted
; return
; [bool] true if the function succeeds, otherwise false
;
FtpDeleteFile(hConnection, File)
{
return DllCall("wininet\FtpDeleteFileA", "uint", hConnection, "str", File)
}
; ------------------------------------------------------------------------------
; Initiates an enumration of files and directories existing on a FTP server.
;
; Note: After calling FtpFindFirstFile and until calling INetCloseHandle, the application cannot call
; FtpFindFirstFile again on the given FTP session handle (or the call will fail)
;
; param hConnection [in]
; a valid connection handle returned by an INetConnect call (using Type="ftp")
; param SearchFile [in]
; Specifies a valid directory path or file name for the FTP server's file system.
; The string can contain wildcards, but no blank spaces are allowed.
; If the value is an empty string, the function finds the first file in the current directory on the server.
; param @FindData [out]
; a variable that will contain raw information about the found item
; return
; [HANDLE] a non-zero handle if a match is found, otherwise 0
;
FtpFindFirstFile(hConnection, SearchFile, ByRef @FindData)
{
; WIN32_FIND_DATA structure size is 4 + 3*8 + 4*4 + 260*4 + 14*4 = 1140
VarSetCapacity(@FindData, 1140, 0)
hEnum := DllCall("wininet\FtpFindFirstFileA"
, "uint", hConnection
, "str", SearchFile
, "uint", &@FindData
, "uint", 0
, "uint", 0)
if(!hEnum)
VarSetCapacity(@FindData, 0)
return hEnum
}
; ------------------------------------------------------------------------------
; Continues an enumeration of files and directories initiated with FtpFindFirstFile.
;
; param hEnum [in]
; a valid enumeration handle returned by a previous call to FtpFindFirstFile or FtpFindNextFile
; param @FindData [out]
; the variable that will contain raw information about the found item already used by FtpFindFirstFile or FtpFindNextFile
; return
; [HANDLE] a non-zero handle if a match is found, otherwise 0
;
FtpFindNextFile(hEnum, ByRef @FindData)
{
return DllCall("wininet\InternetFindNextFileA"
, "uint", hEnum
, "uint", &@FindData)
}
; ------------------------------------------------------------------------------
; Extracts a specified information about a file or directory found on a FTP server
;
; param @FindData [in]
; The variable that contain raw information filled by FtpFindFirstFile or FtpFindNextFile
; ByRef is used only to prevent a useless copy of a large structure.
; param InfoName [in]
; Specifies which information to retrieve. Can be one of the following:
; - "Name"
; - "Size" (in bytes)
; - "CreationTime" (MM/DD/YYYY hh:mm:se.ms)
; - "LastAccessTime" (MM/DD/YYYY hh:mm:se.ms)
; - "LastWriteTime" (MM/DD/YYYY hh:mm:se.ms)
; - "Attrib" return a string containing attributes
; N = normal
; D = directory
; R = read-only
; H = hidden
; S = system
; A = archive
; T = temporary
; E = encrypted
; C = compressed
; V = virtual
; - "IsNormal"
; - "IsDirectory"
; - "IsReadOnly"
; - "IsHidden"
; - "IsSystem"
; - "IsArchive"
; - "IsTemp"
; - "IsEncrypted"
; - "IsCompressed"
; - "IsVirtual"
;
; return
; [string] the information or empty string
;
FtpGetFileInfo(ByRef @FindData, InfoName)
{
if(InfoName == "Name")
{
VarSetCapacity(value, 1040, 0)
DllCall("RtlMoveMemory", "str", value, "uint", &@FindData + 44, "uint", 1040)
VarSetCapacity(value, -1)
}
else if(InfoName == "CreationTime")
{
value := NumGet(@FindData, 4) << 32 | NumGet(@FindData, 8)
value := FileTimeToStr(value)
}
else if(InfoName == "LastAccessTime")
{
value := NumGet(@FindData, 12) << 32 | NumGet(@FindData, 16)
value := FileTimeToStr(value)
}
else if(InfoName == "LastWriteTime")
{
value := NumGet(@FindData, 20) << 32 | NumGet(@FindData, 24)
value := FileTimeToStr(value)
}
else if(InfoName == "Size")
{
value := NumGet(@FindData, 28) << 32 | NumGet(@FindData, 32)
}
else if(InfoName == "Attrib")
{
if(FtpGetFileInfo(@FindData, "IsNormal"))
value .= "N"
if(FtpGetFileInfo(@FindData, "IsDirectory"))
value .= "D"
if(FtpGetFileInfo(@FindData, "IsReadOnly"))
value .= "R"
if(FtpGetFileInfo(@FindData, "IsHidden"))
value .= "H"
if(FtpGetFileInfo(@FindData, "IsSystem"))
value .= "S"
if(FtpGetFileInfo(@FindData, "IsArchive"))
value .= "A"
if(FtpGetFileInfo(@FindData, "IsTemp"))
value .= "T"
if(FtpGetFileInfo(@FindData, "IsEncrypted"))
value .= "E"
if(FtpGetFileInfo(@FindData, "IsCompressed"))
value .= "C"
if(FtpGetFileInfo(@FindData, "IsVirtual"))
value .= "V"
}
else if(InfoName == "IsReadOnly")
{
value := (NumGet(@FindData, 0) & 1) != 0 ; FILE_ATTRIBUTE_READONLY
}
else if(InfoName == "IsHidden")
{
value := (NumGet(@FindData, 0) & 2) != 0 ; FILE_ATTRIBUTE_HIDDEN
}
else if(InfoName == "IsSystem")
{
value := (NumGet(@FindData, 0) & 4) != 0 ; FILE_ATTRIBUTE_SYSTEM
}
else if(InfoName == "IsDirectory")
{
value := (NumGet(@FindData, 0) & 16) != 0 ; FILE_ATTRIBUTE_DIRECTORY
}
else if(InfoName == "IsArchive")
{
value := (NumGet(@FindData, 0) & 32) != 0 ; FILE_ATTRIBUTE_ARCHIVE
}
else if(InfoName == "IsNormal")
{
value := (NumGet(@FindData, 0) & 128) != 0 ; FILE_ATTRIBUTE_NORMAL
}
else if(InfoName == "IsTemp")
{
value := (NumGet(@FindData, 0) & 256) != 0 ; FILE_ATTRIBUTE_TEMPORARY
}
else if(InfoName == "IsEncrypted")
{
value := (NumGet(@FindData, 0) & 2048) != 0 ; FILE_ATTRIBUTE_OFFLINE
}
else if(InfoName == "IsOffline") O
{
value := (NumGet(@FindData, 0) & 4096) != 0 ; FILE_ATTRIBUTE_ENCRYPTED
}
else if(InfoName == "IsCompressed") C
{
value := (NumGet(@FindData, 0) & 16384) != 0 ; FILE_ATTRIBUTE_COMPRESSED
}
else if(InfoName == "IsVirtual") V
{
value := (NumGet(@FindData, 0) & 65536) != 0 ; FILE_ATTRIBUTE_VIRTUAL
}
return value
}
; FileTimeToSystemTime: http://msdn2.microsoft.com/en-us/library/ms724280.aspx
FileTimeToStr(FileTime)
{
VarSetCapacity(SystemTime, 16, 0)
DllCall("FileTimeToSystemTime", "uint", &FileTime, "uint", &SystemTime)
return NumGet(SystemTime,2,"short")
. "/" . NumGet(SystemTime,6,"short")
. "/" . NumGet(SystemTime,0,"short")
. " " . NumGet(SystemTime,8,"short")
. ":" . NumGet(SystemTime,10,"short")
. ":" . NumGet(SystemTime,12,"short")
. "." . NumGet(SystemTime,14,"short")
}
Edited to fix typo in example, thanks olfen