WinSock2.ahk
WinSock2.ahk // a rewrite by derRaphael (w) Sep, 9 2008
based on the WinLIRC Script from Chris
http://www.autohotkey.com/docs/scripts/WinLIRC.htm
and on the WinLIRC Rewrite by ZedGecko
http://www.autohotkey.com/forum/viewtopic.php?t=13829
__WSA_GetHostByName - Parts based upon scripts from DarviK and Tasman. Not much left of the origin source, but it was their achievement by doing the neccessary research.
- /
- WS2 Connect - This establishes a connection to a named resource
- The parameter is to be passed in an URI
- Port manner.
- Returns the socket upon successfull connection, otherwise it
- returns -1. In the latter case more Information is in the global
- variable __WSA_ErrMsg
- Usage-Example
- Pop3_Socket
- = WS2_Connect("mail.isp.com:110")
- See the Doc for more Information.
WS2_Connect(lpszUrl) {
Global
; split our targetURI __WinINet_InternetCrackURL("info://" lpszUrl,"__WSA")
; name our port WS2_Port := __WSA_nPort ; Init the Winsock connection if ( !( __WSA_ScriptInit() ) ; Init the Scriptvariables || !( __WSA_Startup() ) ) { ; Fire up the WSA WS2_CleanUp() ; Do a premature cleanup return -1 ; and return an error indication } ; check the URI if it's valid if (RegExMatch(__WSA_lpszHostName,"[^\d.]+")) ; Must be different than IP { WS2_IPAddress := __WSA_GetHostByName(__WSA_lpszHostName) } else { ; Let's check if the IP is valid StringSplit,__WSA_tmpIPFragment, __WSA_lpszHostName,. Loop,4 If ( ( __WSA_tmpIPFragment%A_Index%<0 ) || ( __WSA_tmpIPFragment%A_Index%>255 ) || ( __WSA_tmpIPFragment0!=4 ) ) { __WSA_IPerror = 1 Break } If (__WSA_IPerror=1) __WSA_ErrMsg .= "No valid IP Supplied" else WS2_IPAddress := __WSA_lpszHostName }
; CONVERSIONS
; The htons function returns the value in TCP/IP network byte order. ; http://msdn.microsoft.com/en-us/library/ms738557(VS.85).aspx __WSA_Port := DllCall("Ws2_32\htons", "UShort", WS2_Port)
; The inet_addr function converts a string containing an IPv4 dotted-decimal ; address into a proper address for the IN_ADDR structure. ; inet_addr: http://msdn.microsoft.com/en-us/library/ms738563(VS.85).aspx ; IN_ADDR: http://msdn.microsoft.com/en-us/library/ms738571(VS.85).aspx __WSA_InetAddr := DllCall("Ws2_32\inet_addr", "Str", WS2_IPAddress)
If ( ( __WSA_Socket:=__WSA_Socket() ) && ( __WSA_Connect() ) ) return __WSA_Socket ; All went OK, return the SocketID Else { WS2_CleanUp() ; Do a premature cleanup return -1 ; and return an error indication }
}
- WS2 OnMessage - This function defines, whatever should happen when
- a Message is received on the socket.
- Expected Parameter
- Ws2_Socket => Socket returned from WS2_Connect() Call
- UDF => An UserDefinedFunction to which the received
- Data will be passed to
- Optional Parameter
- WindowMessage => A number indicating upon which WM_Message to react
- Returns -1 on error, 0 on success
WS2_AsyncSelect(Ws2_Socket,UDF,WindowMessage="") {
Global __WSA_ErrMsg If ( ( StrLen(Ws2_Socket)=0 ) || ( StrLen(UDF)=0 ) ) { res := -1 } else { If ( (StrLen(WindowMessage)=0) || (WindowMessage+0=0) ) WindowMessage := 0x5000 res := __WSA_AsyncSelect(Ws2_Socket, UDF, WindowMessage) } return res
}
WS2_SendData(WS2_Socket,DataToSend) {
Global __WSA_ErrMsg If (__WSA_send(WS2_Socket, DataToSend)=-1) { MsgBox, 16, %A_ScriptName%: Send-Error, % __WSA_ErrMsg }
}
- WS2 Cleanup - This needs to be called whenever Your Script exits
- Usually this is invoked by some OnExit, Label subroutines.
WS2_CleanUp() {
DllCall("Ws2_32\WSACleanup")
}
WS2_Disconnect(WS2_Socket) {
Global __WSA_ErrMsg if (res := __WSA_CloseSocket(WS2_Socket)) MsgBox, 16, %A_ScriptName%: CloseSocket-Error, % __WSA_ErrMsg
}
- WS2 ScriptInit - for internal use only
- Initializes neccessary variables for this Script.
__WSA_ScriptInit() {
; CONTANTS
; We're working with version 2 of Winsock Local VersionRequested := 2 ; from http://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx Local AF_INET := 2 Local SOCK_STREAM := 1 Local IPPROTO_TCP := 6 Local FD_READ := 0x1 Local FD_CLOSE := 0x20
__AI_PASSIVE := 1
__WSA_WSVersion := VersionRequested __WSA_SocketType := SOCK_STREAM __WSA_SocketProtocol := IPPROTO_TCP __WSA_SocketAF := AF_INET __WSA_lEvent := FD_READ|FD_CLOSE
__WSA_WOULDBLOCK := 10035 ; http://www.sockets.com/err_lst1.htm#WSAECONNRESET __WSA_CONNRESET := 10054 ; http://www.sockets.com/err_lst1.htm#WSAECONNRESET
return 1
}
- WS2 Startup - for internal use only
- Initializes the Winsock 2 Adapter
__WSA_Startup() {
Global WSAData, __WSA_ErrMsg, __WSA_WSVersion
; It's a good idea, to have a __WSA_ErrMsg Container, so any Error Msgs ; may be catched by the script. __WSA_ErrMsg := ""
; Generate Structure for the lpWSAData ; as stated on http://msdn.microsoft.com/en-us/library/ms742213.aspx ; More on WSADATA (structure) to be found here: ; http://msdn.microsoft.com/en-us/library/ms741563(VS.85).aspx VarSetCapacity(WSAData, 32) result := DllCall("Ws2_32\WSAStartup", "UShort", __WSA_WSVersion, "UInt", &WSAData)
if (ErrorLevel) __WSA_ErrMsg .= "Ws2_32\WSAStartup could not be called due to error " ErrorLevel "`n" . "Winsock 2.0 or higher is required.`n" if (result!=0) __WSA_ErrMsg .= "Ws2_32\WSAStartup " __WSA_GetLastError()
If (StrLen(__WSA_ErrMsg)>0) Return -1 Else Return 1
}
- WS2 Socket Descriptor - for internal use only
- Sets type and neccessary structures for a successfull connection
__WSA_Socket() {
Global __WSA_ErrMsg, __WSA_SocketProtocol, __WSA_SocketType, __WSA_SocketAF
; Supposed to return a descriptor referencing the new socket ; http://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx __WSA_Socket := DllCall("Ws2_32\socket" , "Int", __WSA_SocketAF , "Int", __WSA_SocketType , "Int", __WSA_SocketProtocol) if (socket = -1) __WSA_ErrMsg .= "Ws2_32\socket " __WSA_GetLastError()
If (StrLen(__WSA_ErrMsg)>0) Return -1 Else Return __WSA_Socket
}
- WS2 Connection call - for internal use only
- Establishes a connection to a foreign IP at the specified port
__WSA_Connect() {
Global __WSA_ErrMsg, __WSA_Port, __WSA_Socket, __WSA_InetAddr, __WSA_SocketAF
; Generate socketaddr structure for the connect() ; http://msdn.microsoft.com/en-us/library/ms740496(VS.85).aspx __WSA_SockAddrNameLen := 16 VarSetCapacity(__WSA_SockAddr, __WSA_SockAddrNameLen) NumPut(__WSA_SocketAF, __WSA_SockAddr, 0, "UShort") NumPut(__WSA_Port, __WSA_SockAddr, 2, "UShort") NumPut(__WSA_InetAddr, __WSA_SockAddr, 4)
; The connect function establishes a connection to a specified socket. ; http://msdn.microsoft.com/en-us/library/ms737625(VS.85).aspx result := DllCall("Ws2_32\connect" , "UInt", __WSA_Socket , "UInt", &__WSA_SockAddr , "Int" , __WSA_SockAddrNameLen) if (result) __WSA_ErrMsg .= "Ws2_32\connect " __WSA_GetLastError()
If (StrLen(__WSA_ErrMsg)>0) Return -1 Else Return 1
}
/*
This code based originally upon an example by DarviK http://www.autohotkey.com/forum/topic8871.html and on the modifcations by Tasman http://www.autohotkey.com/forum/viewtopic.php?t=9937
- /
- Resolves canonical domainname to IP
__WSA_GetHostByName(url) {
Global __WSA_ErrMsg ; gethostbyname returns information about a domainname into a Hostent Structure ; http://msdn.microsoft.com/en-us/library/ms738524(VS.85).aspx IP := "" if ((PtrHostent:=DllCall("Ws2_32\gethostbyname","str",url)) != 0) { Loop, 1 ; 3 is max No of retrieved addresses If (PtrTmpIP := NumGet(NumGet(PtrHostent+12)+(offset:=(A_Index-1)*4),offset)) { IP := (IP) ? IP "|" : "" Loop, 4 ; Read our IP address IP .= NumGet(PtrTmpIP+offset,(A_Index-1 ),"UChar") "." IP := SubStr(IP,1,-1) } else ; No more IPs left Break result := IP } else { __WSA_ErrMsg .= "Ws2_32\gethostbyname failed`n " result := -1 } return result
}
- Return the last Error with a lil bit o' text if neccessary
- Note
- the txt variable is set to 0 when checking for received content
__WSA_GetLastError(txt=1) {
Err := DllCall("Ws2_32\WSAGetLastError") ExtraInfo := __WSA_ErrLookUp(RegExReplace(Err,"[^\d]")) If ((InStr(ExtraInfo,"Sorry, no")) || (txt!=1)) ExtraInfo := "" Return ( txt ? "indicated Winsock error " : "") . Err . ( txt ? "`n" ExtraInfo : "")
}
- WS2 AsyncSelect - for internal use only
- Sets up an Notification Handler for Receiving Messages
- Expected Parameters
- Socket from Initialisation
- Optional
- NotificationMsg - default 0x5000
- WSA_DataReiceiver - an different Name to standard
- wm_* processor function.
- default __WSA_ReceiveData
- Returns -1 on Error, 0 on success
__WSA_AsyncSelect(__WSA_Socket, UDF, __WSA_NotificationMsg=0x5000
,__WSA_DataReceiver="__WSA_recv")
{
Global
__WSA_UDF := UDF
OnMessage(__WSA_NotificationMsg, __WSA_DataReceiver) ; The WSAAsyncSelect function requests Windows message-based notification ; of network events for a socket. ; http://msdn.microsoft.com/en-us/library/ms741540(VS.85).aspx Result := DllCall("Ws2_32\WSAAsyncSelect" , "UInt", __WSA_Socket , "UInt", __WSA_GetThisScriptHandle() , "UInt", __WSA_NotificationMsg , "Int", __WSA_lEvent) if (Result) { __WSA_ErrMsg .= "Ws2_32\WSAAsyncSelect() " . __WSA_GetLastError() Result := -1 } Return Result
}
- WS2 Receive - for internal use only
- Triggers upon Notification Handler when Receiving Messages
__WSA_recv(wParam, lParam) {
Global __WSA_UDF, __WSA_ErrMsg ; __WSA_UDF containes the name of the UserDefinedFunction to call when the event ; has been triggered and text may be processed (allthough the reveived text might ; be inclomplete, especially when receiving large chunks of data, like in eMail- ; attachments or sometimes in IRC). The UDF needs to accept two parameter: socket ; and the received buffer __WSA_Socket := wParam __WSA_BufferSize = 4096 Loop { VarSetCapacity(__WSA_Buffer, __WSA_BufferSize, 0) __WSA_BufferLength := DllCall("Ws2_32\recv" , "UInt", __WSA_Socket , "Str", __WSA_Buffer , "Int", __WSA_BufferSize , "Int", 0 ) if (__WSA_BufferLength = 0) break if (__WSA_BufferLength = -1) { __WSA_Err := __WSA_GetLastError(0) ; __WSA_WOULDBLOCK (from http://www.sockets.com/) ; The socket is marked as non-blocking (non-blocking operation mode), and ; the requested operation is not complete at this time. The operation is ; underway, but as yet incomplete. if (__WSA_Err = __WSA_WOULDBLOCK ) return 1
; __WSA_CONNRESET: (from http://www.sockets.com/) ; A connection was forcibly closed by a peer. This normally results from ; a loss of the connection on the remote socket due to a timeout or a reboot. if (__WSA_Err != __WSA_CONNRESET) __WSA_ErrMsg .= "Ws2_32\recv indicated Winsock error " __WSA_Err "`n" break }
if (StrLen(__WSA_UDF)!=0) ; If set, call UserDefinedFunction and pass Buffer to it %__WSA_UDF%(__WSA_Socket,__WSA_Buffer) } return 1
}
- WSA Send - for internal use only
- Users are encouraged to use the WS2_SendData() Function
__WSA_send(__WSA_Socket, __WSA_Data) {
Global __WSA_ErrMsg
Result := DllCall("Ws2_32\send" , "UInt", __WSA_Socket , "Str", __WSA_Data , "Int", StrLen(__WSA_Data) , "Int", 0) If (Result = -1) __WSA_ErrMsg .= "Ws2_32\send " __WSA_GetLastError() Return Result
}
- Closes Open Socket - for internal use only
- Returns 0 on success
__WSA_CloseSocket(__WSA_Socket) {
Global __WSA_ErrMsg
Result := DllCall("Ws2_32\closesocket" , "UInt", __WSA_Socket) If (Result != 0) __WSA_ErrMsg .= "Ws2_32\closesocket " __WSA_GetLastError()
Return result
}
- GetThisScriptHandle - for internal use only
- Returns the handle of the executing script
__WSA_GetThisScriptHandle() {
HiddenWindowsSave := A_DetectHiddenWindows
DetectHiddenWindows On ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " DllCall("GetCurrentProcessId")) DetectHiddenWindows %HiddenWindowsSave%
Return ScriptMainWindowId
}
- Lookup Winsock ErrCode - for internal use only
- This list is form http://www.sockets.com
__WSA_ErrLookUp(sNumber) { WSA_ErrorList = (LTrim Join`n
10004 - Interrupted system call 10009 - Bad file number 10013 - Permission denied 10014 - Bad address 10022 - Invalid argument 10024 - Too many open files 10035 - Operation would block 10036 - Operation now in progress 10037 - Operation already in progress 10038 - Socket operation on non-socket 10039 - D estination address required 10040 - Message too long 10041 - Protocol wrong type for socket 10042 - Bad protocol option 10043 - Protocol not supported 10044 - Socket type not supported 10045 - Operation not supported on socket 10046 - Protocol family not supported 10047 - Address family not supported by protocol family 10048 - Address already in use 10049 - Can't assign requested address 10050 - Network is down 10051 - Network is unreachable 10052 - Net dropped connection or reset 10053 - Software caused connection abort 10054 - Connection reset by peer 10055 - No buffer space available 10056 - Socket is already connected 10057 - Socket is not connected 10058 - Can't send after socket shutdown 10059 - Too many references, can't splice 10060 - Connection timed out 10061 - Connection refused 10062 - Too many levels of symbolic links 10063 - File name too long 10064 - Host is down 10065 - No Route to Host 10066 - Directory not empty 10067 - Too many processes 10068 - Too many users 10069 - Disc Quota Exceeded 10070 - Stale NFS file handle 10091 - Network SubSystem is unavailable 10092 - WINSOCK DLL Version out of range 10093 - Successful WSASTARTUP not yet performed 10071 - Too many levels of remote in path 11001 - Host not found 11002 - Non-Authoritative Host not found 11003 - Non-Recoverable errors: FORMERR, REFUSED, NOTIMP 11004 - Valid name, no data record of requested type 11004 - No address, look for MX record
) ExNr := 0, ExErr := "Sorry, but no definition available." Loop,Parse,WSA_ErrorList,`n {
RegExMatch(A_LoopField,"(?P<Nr>\d+) - (?P<Err>.*)",Ex) if (sNumber = ExNr) break
} Return ExNr " means " ExErr "`n" }
- WinINet InternetCrackURL - for internal use only
- v 0.1 / (w) 25.07.2008 by derRaphael / zLib-Style release
- This routine was originally posted here
- http://www.autohotkey.com/forum/viewtopic.php?p=209957#209957
__WinINet_InternetCrackURL(lpszUrl,arrayName="URL") {
local hModule, offset_name_length hModule := DllCall("LoadLibrary", "Str", "WinINet.Dll")
; SetUpStructures for URL_COMPONENTS / needed for InternetCrackURL ; http://msdn.microsoft.com/en-us/library/aa385420(VS.85).aspx offset_name_length:= "4-lpszScheme-255|16-lpszHostName-1024|28-lpszUserName-1024|" . "36-lpszPassword-1024|44-lpszUrlPath-1024|52-lpszExtrainfo-1024"
VarSetCapacity(URL_COMPONENTS,60,0) ; Struc Size ; Scheme Size ; Max Port Number NumPut(60,URL_COMPONENTS,0), NumPut(255,URL_COMPONENTS,12), NumPut(0xffff,URL_COMPONENTS,24) Loop,Parse,offset_name_length,| { RegExMatch(A_LoopField,"(?P<Offset>\d+)-(?P<Name>[a-zA-Z]+)-(?P<Size>\d+)",iCU_) VarSetCapacity(%iCU_Name%,iCU_Size,0) NumPut(&%iCU_Name%,URL_COMPONENTS,iCU_Offset) NumPut(iCU_Size,URL_COMPONENTS,iCU_Offset+4) }
; Split the given URL; extract scheme, user, pass, authotity (host), port, path, and query (extrainfo) ; http://msdn.microsoft.com/en-us/library/aa384376(VS.85).aspx DllCall("WinINet\InternetCrackUrlA","Str",lpszUrl,"uInt",StrLen(lpszUrl),"uInt",0,"uInt",&URL_COMPONENTS) ; Update variables to retrieve results Loop,Parse,offset_name_length,| { RegExMatch(A_LoopField,"-(?P<Name>[a-zA-Z]+)-",iCU_) VarSetCapacity(%iCU_Name%,-1) %arrayName%_%iCU_Name% := % %iCU_Name% } %arrayName%_nPort:=NumGet(URL_COMPONENTS,24,"uInt") DllCall("FreeLibrary", "UInt", hModule) ; unload the library}</lang>