Hi all
i have written this lil script, cuz i was tired of modifying always the same set up of Chris good ol' WinLIRC script or ZedGecko's Mod of it. Also i missed some functions, like Host2Ip. its not perfect yet, but shows the ease how it can be used.
a simple (very simple) Usage exampleCode:
; ws2_testunit.ahk
OnExit, CleanUp
Socket := WS2_Connect("someserver.tld:110")
if ((Socket<=0) || (StrLen(__WSA_ErrMsg)) || (StrLen(Socket)=0)) {
MsgBox,16,%A_ScriptName%: Error,% __WSA_ErrMsg
ExitApp
}
res := WS2_AsyncSelect(Socket,"MsgBoxThis")
if ((Res!=0) || (StrLen(__WSA_ErrMsg))) {
MsgBox,16,%A_ScriptName%: Error,% __WSA_ErrMsg
ExitApp
}
return
CleanUp:
WS2_CleanUp()
ExitApp
return
#include winsock2.ahk
MsgBoxThis(Txt) {
MsgBox, % Txt
}
K, The commands which are userland comaptible are as follows:
The most important command:
WS2_Connect("someserver.tld:port")
This command returns a valid socket number on success, otherwise -1 or nothing but an ErrMsg which can be accessed thru the var __WSA_ErrMsg
Expects either an ip followed by a colon and an port number or a FQDN followed by a colon and a port. (FQDN supported are only Web-Domains atm)
The 2nd important command:
WS2_AsyncSelect(Socket,"MsgBoxThis")
This command returns 0 upon failure, the ErrMsg can be accessed thru the var __WSA_ErrMsg
It expects a socket as returned by WS2_Connect and a UserDefinedFunction, which will receive the texts received upon window message notification.
Optional third Parameter may be a WindowMessage Number, upon the UDF will be triggered.
Two more functions are included:
WS2_SendData(WS2_Socket,DataToSend)
This command returns a nonzero value upon success, and drops a message upon failure. the ErrMsg will be displayed in a MsgBox
It expects a socket as returned by WS2_Connect and the data which will be transmitted thru the socketconnection.
finally WS2_CleanUp()
This function should be either accessed upon script termination via OnExit or when the connections are no more needed.
WinSock2.ahkCode:
/*
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
}
Greets
dR
Version History:
0.0.1 - Sep, 9 2008 - Initial release
0.0.1b - Sep, 27 2008 - Lil BugFix of WS ERR returns