home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 4_2005-2006.ISO / data / Zips / View_Proce1983833292006.psc / Netstat / CSocketMaster.cls next >
Text File  |  2006-03-29  |  60KB  |  1,747 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "CSocketMaster"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. '********************************************************************************
  15. '
  16. 'Name.......... CSocketMaster
  17. 'File.......... CSocketMaster.cls
  18. 'Version....... 1.1
  19. 'Dependencies.. Requires modSocketMaster.bas code module
  20. 'Description... Winsock api implementation class
  21. 'Author........ Emiliano Scavuzzo <anshoku@yahoo.com>
  22. 'Date.......... February, 22nd 2004
  23.  
  24. 'Copyright (c) 2004 by Emiliano Scavuzzo
  25. 'Rosario, Argentina
  26. '
  27. 'Based on CSocket by Oleg Gdalevich
  28. 'Subclassing based on WinSubHook2 by Paul Caton <Paul_Caton@hotmail.com>
  29. '
  30. '********************************************************************************
  31.  
  32. Option Explicit
  33.  
  34. '==============================================================================
  35. 'API FUNCTIONS
  36. '==============================================================================
  37.  
  38. Private Declare Function api_socket Lib "ws2_32.dll" Alias "socket" (ByVal af As Long, ByVal s_type As Long, ByVal Protocol As Long) As Long
  39. Private Declare Function api_GlobalLock Lib "kernel32" Alias "GlobalLock" (ByVal hMem As Long) As Long
  40. Private Declare Function api_GlobalUnlock Lib "kernel32" Alias "GlobalUnlock" (ByVal hMem As Long) As Long
  41. Private Declare Function api_htons Lib "ws2_32.dll" Alias "htons" (ByVal hostshort As Integer) As Integer
  42. Private Declare Function api_ntohs Lib "ws2_32.dll" Alias "ntohs" (ByVal netshort As Integer) As Integer
  43. Private Declare Function api_connect Lib "ws2_32.dll" Alias "connect" (ByVal s As Long, ByRef name As sockaddr_in, ByVal namelen As Long) As Long
  44. Private Declare Function api_gethostname Lib "ws2_32.dll" Alias "gethostname" (ByVal host_name As String, ByVal namelen As Long) As Long
  45. Private Declare Function api_gethostbyname Lib "ws2_32.dll" Alias "gethostbyname" (ByVal host_name As String) As Long
  46. Private Declare Function api_bind Lib "ws2_32.dll" Alias "bind" (ByVal s As Long, ByRef name As sockaddr_in, ByRef namelen As Long) As Long
  47. Private Declare Function api_getsockname Lib "ws2_32.dll" Alias "getsockname" (ByVal s As Long, ByRef name As sockaddr_in, ByRef namelen As Long) As Long
  48. Private Declare Function api_getpeername Lib "ws2_32.dll" Alias "getpeername" (ByVal s As Long, ByRef name As sockaddr_in, ByRef namelen As Long) As Long
  49. Private Declare Function api_inet_addr Lib "ws2_32.dll" Alias "inet_addr" (ByVal cp As String) As Long
  50. Private Declare Function api_send Lib "ws2_32.dll" Alias "send" (ByVal s As Long, ByRef buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long
  51. Private Declare Function api_sendto Lib "ws2_32.dll" Alias "sendto" (ByVal s As Long, ByRef buf As Any, ByVal buflen As Long, ByVal flags As Long, ByRef toaddr As sockaddr_in, ByVal tolen As Long) As Long
  52. Private Declare Function api_getsockopt Lib "ws2_32.dll" Alias "getsockopt" (ByVal s As Long, ByVal level As Long, ByVal optname As Long, optval As Any, optlen As Long) As Long
  53. Private Declare Function api_setsockopt Lib "ws2_32.dll" Alias "setsockopt" (ByVal s As Long, ByVal level As Long, ByVal optname As Long, optval As Any, ByVal optlen As Long) As Long
  54. Private Declare Function api_recv Lib "ws2_32.dll" Alias "recv" (ByVal s As Long, ByRef buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long
  55. Private Declare Function api_recvfrom Lib "ws2_32.dll" Alias "recvfrom" (ByVal s As Long, ByRef buf As Any, ByVal buflen As Long, ByVal flags As Long, ByRef from As sockaddr_in, ByRef fromlen As Long) As Long
  56. Private Declare Function api_WSACancelAsyncRequest Lib "ws2_32.dll" Alias "WSACancelAsyncRequest" (ByVal hAsyncTaskHandle As Long) As Long
  57. Private Declare Function api_listen Lib "ws2_32.dll" Alias "listen" (ByVal s As Long, ByVal backlog As Long) As Long
  58. Private Declare Function api_accept Lib "ws2_32.dll" Alias "accept" (ByVal s As Long, ByRef addr As sockaddr_in, ByRef addrlen As Long) As Long
  59. Private Declare Function api_inet_ntoa Lib "ws2_32.dll" Alias "inet_ntoa" (ByVal inn As Long) As Long
  60. Private Declare Function api_gethostbyaddr Lib "ws2_32.dll" Alias "gethostbyaddr" (addr As Long, ByVal addr_len As Long, ByVal addr_type As Long) As Long
  61. Private Declare Function api_ioctlsocket Lib "ws2_32.dll" Alias "ioctlsocket" (ByVal s As Long, ByVal cmd As Long, ByRef argp As Long) As Long
  62. Private Declare Function api_closesocket Lib "ws2_32.dll" Alias "closesocket" (ByVal s As Long) As Long
  63.  
  64. '==============================================================================
  65. 'CONSTANTS
  66. '==============================================================================
  67. Public Enum SockState
  68.     sckClosed = 0
  69.     sckOpen
  70.     sckListening
  71.     sckConnectionPending
  72.     sckResolvingHost
  73.     sckHostResolved
  74.     sckConnecting
  75.     sckConnected
  76.     sckClosing
  77.     sckError
  78. End Enum
  79.  
  80. Public Enum DestResolucion 'asynchronic host resolution destination
  81.     destConnect = 0
  82.     'destSendUDP = 1
  83. End Enum
  84.  
  85. Private Const SOMAXCONN As Long = 5
  86.  
  87. Public Enum ProtocolConstants
  88.     sckTCPProtocol = 0
  89.     sckUDPProtocol = 1
  90. End Enum
  91.  
  92. Private Const MSG_PEEK  As Long = &H2
  93.  
  94. '==============================================================================
  95. 'EVENTS
  96. '==============================================================================
  97.  
  98. Public Event CloseSck()
  99. Public Event Connect()
  100. Public Event ConnectionRequest(ByVal requestID As Long)
  101. Public Event DataArrival(ByVal bytesTotal As Long)
  102. Public Event Error(ByVal Number As Integer, Description As String, ByVal sCode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)
  103. Public Event SendComplete()
  104. Public Event SendProgress(ByVal bytesSent As Long, ByVal bytesRemaining As Long)
  105.  
  106. '==============================================================================
  107. 'MEMBER VARIABLES
  108. '==============================================================================
  109. Private m_lngSocketHandle       As Long                 'socket handle
  110. Private m_enmState              As SockState            'socket state
  111. Private m_strTag                As String               'tag
  112. Private m_strRemoteHost         As String               'remote host
  113. Private m_lngRemotePort         As Long                 'remote port
  114. Private m_strRemoteHostIP       As String               'remote host ip
  115. Private m_lngLocalPort          As Long                 'local port
  116. Private m_lngLocalPortBind      As Long                 'temporary local port
  117. Private m_strLocalIP            As String               'local IP
  118. Private m_enmProtocol           As ProtocolConstants    'protocol used (TCP / UDP)
  119.  
  120. Private m_lngMemoryPointer  As Long 'memory pointer used as buffer when resolving host
  121. Private m_lngMemoryHandle   As Long 'buffer memory handle
  122.  
  123. Private m_lngSendBufferLen  As Long 'winsock buffer size for sends
  124. Private m_lngRecvBufferLen  As Long 'winsock buffer size for receives
  125.  
  126. Private m_strSendBuffer As String   'local incoming buffer
  127. Private m_strRecvBuffer As String   'local outgoing buffer
  128.  
  129. Private m_blnAcceptClass As Boolean 'if True then this is a Accept socket class
  130. Private m_colWaitingResolutions As Collection   'hosts waiting to be resolved by the system
  131.  
  132. '  ****  WARNING WARNING WARNING WARNING ******
  133. 'This sub MUST be the first on the class. DO NOT attempt
  134. 'to change it's location or the code will CRASH.
  135. 'This sub receives system messages from our WndProc.
  136. Public Sub WndProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long)
  137. Select Case uMsg
  138.  
  139. Case RESOLVE_MESSAGE
  140.     
  141.     PostResolution wParam, HiWord(lParam)
  142.     
  143. Case SOCKET_MESSAGE
  144.     
  145.     PostSocket LoWord(lParam), HiWord(lParam)
  146.     
  147. End Select
  148. End Sub
  149.  
  150. Private Sub Class_Initialize()
  151. 'socket's handle default value
  152. m_lngSocketHandle = INVALID_SOCKET
  153.  
  154. 'initiate resolution collection
  155. Set m_colWaitingResolutions = New Collection
  156.  
  157. 'initiate processes and winsock service
  158. modSocketMaster.InitiateProcesses
  159. End Sub
  160.  
  161. Private Sub Class_Terminate()
  162. 'clean hostname resolution system
  163. CleanResolutionSystem
  164.  
  165. 'destroy socket if it exists
  166. If Not m_blnAcceptClass Then DestroySocket
  167.  
  168. 'clean processes and finish winsock service
  169. modSocketMaster.FinalizeProcesses
  170.  
  171. 'clean resolution collection
  172. Set m_colWaitingResolutions = Nothing
  173. End Sub
  174.  
  175. '==============================================================================
  176. 'PROPERTIES
  177. '==============================================================================
  178.  
  179. Public Property Get RemotePort() As Long
  180. RemotePort = m_lngRemotePort
  181. End Property
  182.  
  183. Public Property Let RemotePort(ByVal lngPort As Long)
  184. If m_enmProtocol = sckTCPProtocol And m_enmState <> sckClosed Then
  185.     Err.Raise sckInvalidOp, "CSocketMaster.RemotePort", "Invalid operation at current state"
  186. End If
  187.  
  188. If lngPort < 0 Or lngPort > 65535 Then
  189.     Err.Raise sckInvalidArg, "CSocketMaster.RemotePort", "The argument passed to a function was not in the correct format or in the specified range."
  190. Else
  191.     m_lngRemotePort = lngPort
  192. End If
  193. End Property
  194.  
  195. Public Property Get RemoteHost() As String
  196. RemoteHost = m_strRemoteHost
  197. End Property
  198.  
  199. Public Property Let RemoteHost(ByVal strHost As String)
  200. If m_enmProtocol = sckTCPProtocol And m_enmState <> sckClosed Then
  201.     Err.Raise sckInvalidOp, "CSocketMaster.RemoteHost", "Invalid operation at current state"
  202. End If
  203.  
  204. m_strRemoteHost = strHost
  205. End Property
  206.  
  207. Public Property Get RemoteHostIP() As String
  208. RemoteHostIP = m_strRemoteHostIP
  209. End Property
  210.  
  211. Public Property Get LocalPort() As Long
  212. If m_lngLocalPortBind = 0 Then
  213.     LocalPort = m_lngLocalPort
  214. Else
  215.     LocalPort = m_lngLocalPortBind
  216. End If
  217. End Property
  218.  
  219. Public Property Let LocalPort(ByVal lngPort As Long)
  220. If m_enmState <> sckClosed Then
  221.     Err.Raise sckInvalidOp, "CSocketMaster.LocalPort", "Invalid operation at current state"
  222. End If
  223. If lngPort < 0 Or lngPort > 65535 Then
  224.     Err.Raise sckInvalidArg, "CSocketMaster.LocalPort", "The argument passed to a function was not in the correct format or in the specified range."
  225. Else
  226.     m_lngLocalPort = lngPort
  227. End If
  228. End Property
  229.  
  230. Public Property Get State() As SockState
  231. State = m_enmState
  232. End Property
  233.  
  234. Public Property Get LocalHostName() As String
  235. LocalHostName = GetLocalHostName
  236. End Property
  237.  
  238. Public Property Get LocalIP() As String
  239. If m_enmState = sckOpen Or m_enmState = sckListening Then
  240.     LocalIP = m_strLocalIP
  241. Else
  242.     LocalIP = GetLocalIP
  243. End If
  244. End Property
  245.  
  246. Public Property Get BytesReceived() As Long
  247. If m_enmProtocol = sckTCPProtocol Then
  248.     BytesReceived = Len(m_strRecvBuffer)
  249. Else
  250.     BytesReceived = GetBufferLenUDP
  251. End If
  252. End Property
  253.  
  254. Public Property Get SocketHandle() As Long
  255. SocketHandle = m_lngSocketHandle
  256. End Property
  257.  
  258. Public Property Get Tag() As String
  259. Tag = m_strTag
  260. End Property
  261.  
  262. Public Property Let Tag(ByVal strTag As String)
  263. m_strTag = strTag
  264. End Property
  265.  
  266. Public Property Get Protocol() As ProtocolConstants
  267. Protocol = m_enmProtocol
  268. End Property
  269.  
  270. Public Property Let Protocol(ByVal enmProtocol As ProtocolConstants)
  271. If m_enmState <> sckClosed Then
  272.     Err.Raise sckInvalidOp, "CSocketMaster.Protocol", "Invalid operation at current state"
  273. Else
  274.     m_enmProtocol = enmProtocol
  275. End If
  276. End Property
  277.  
  278. 'Destroys the socket if it exists and unregisters it
  279. 'from control list.
  280. Private Sub DestroySocket()
  281. If Not m_lngSocketHandle = INVALID_SOCKET Then
  282.  
  283.     Dim lngResult As Long
  284.     
  285.     lngResult = api_closesocket(m_lngSocketHandle)
  286.     
  287.     If lngResult = SOCKET_ERROR Then
  288.         
  289.         m_enmState = sckError: Debug.Print "STATE: sckError"
  290.         Dim lngErrorCode As Long
  291.         lngErrorCode = Err.LastDllError
  292.         Err.Raise lngErrorCode, "CSocketMaster.DestroySocket", GetErrorDescription(lngErrorCode)
  293.     
  294.     Else
  295.         
  296.         Debug.Print "OK Destroyed socket " & m_lngSocketHandle
  297.         modSocketMaster.UnregisterSocket m_lngSocketHandle
  298.         m_lngSocketHandle = INVALID_SOCKET
  299.     
  300.     End If
  301.     
  302. End If
  303. End Sub
  304.  
  305. Public Sub CloseSck()
  306. If m_lngSocketHandle = INVALID_SOCKET Then Exit Sub
  307.  
  308. m_enmState = sckClosing: Debug.Print "STATE: sckClosing"
  309. CleanResolutionSystem
  310. DestroySocket
  311.      
  312. m_lngLocalPortBind = 0
  313. m_strRemoteHostIP = ""
  314. m_strRecvBuffer = ""
  315. m_strSendBuffer = ""
  316. m_lngSendBufferLen = 0
  317. m_lngRecvBufferLen = 0
  318.  
  319. m_enmState = sckClosed: Debug.Print "STATE: sckClosed"
  320.  
  321. End Sub
  322.  
  323. 'Tries to create a socket if there isn't one yet and registers
  324. 'it to the control list.
  325. 'Returns TRUE if it has success
  326. Private Function SocketExists() As Boolean
  327. SocketExists = True
  328. Dim lngResult As Long
  329. Dim lngErrorCode As Long
  330.  
  331. 'check if there is a socket already
  332. If m_lngSocketHandle = INVALID_SOCKET Then
  333.     
  334.     'decide what kind of socket we are creating, TCP or UDP
  335.     If m_enmProtocol = sckTCPProtocol Then
  336.         lngResult = api_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
  337.     Else
  338.         lngResult = api_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
  339.     End If
  340.     
  341.     If lngResult = INVALID_SOCKET Then
  342.         
  343.         m_enmState = sckError: Debug.Print "STATE: sckError"
  344.         Debug.Print "ERROR trying to create socket"
  345.         SocketExists = False
  346.         lngErrorCode = Err.LastDllError
  347.         Dim blnCancelDisplay As Boolean
  348.         blnCancelDisplay = True
  349.         RaiseEvent Error(lngErrorCode, GetErrorDescription(lngErrorCode), 0, "CSocketMaster.SocketExists", "", 0, blnCancelDisplay)
  350.         If blnCancelDisplay = False Then MsgBox GetErrorDescription(lngErrorCode), vbOKOnly, "CSocketMaster.SocketExists"
  351.     Else
  352.         
  353.         Debug.Print "OK Created socket: " & lngResult
  354.         m_lngSocketHandle = lngResult
  355.         'set and get some socket options
  356.         ProcessOptions
  357.         SocketExists = modSocketMaster.RegisterSocket(m_lngSocketHandle, ObjPtr(Me), True)
  358.     
  359.     End If
  360. End If
  361. End Function
  362.  
  363. 'Tries to connect to RemoteHost if it was passed, or uses
  364. 'm_strRemoteHost instead. If it is a hostname tries to
  365. 'resolve it first.
  366. Public Sub Connect(Optional RemoteHost As Variant, Optional RemotePort As Variant)
  367. If m_enmState <> sckClosed Then
  368.     Err.Raise sckInvalidOp, "CSocketMaster.Connect", "Invalid operation at current state"
  369. End If
  370.  
  371. If Not IsMissing(RemoteHost) Then
  372.         m_strRemoteHost = CStr(RemoteHost)
  373. End If
  374.  
  375. 'for some reason we get a GPF if we try to
  376. 'resolve a null string, so we replace it with
  377. 'an empty string
  378. If m_strRemoteHost = vbNullString Then
  379.     m_strRemoteHost = ""
  380. End If
  381.  
  382. 'check if RemotePort is a number between 1 and 65535
  383. If Not IsMissing(RemotePort) Then
  384.     If IsNumeric(RemotePort) Then
  385.         If CLng(RemotePort) > 65535 Or CLng(RemotePort) < 1 Then
  386.             Err.Raise sckInvalidArg, "CSocketMaster.Connect", "The argument passed to a function was not in the correct format or in the specified range."
  387.         Else
  388.             m_lngRemotePort = CLng(RemotePort)
  389.         End If
  390.     Else
  391.         Err.Raise sckUnsupported, "CSocketMaster.Connect", "Unsupported variant type."
  392.     End If
  393. End If
  394.  
  395. 'create a socket if there isn't one yet
  396. If Not SocketExists Then Exit Sub
  397.  
  398. 'If we are using UDP we just bind the socket and exit
  399. 'silently. Remember UDP is a connectionless protocol.
  400. If m_enmProtocol = sckUDPProtocol Then
  401.     If BindInternal Then
  402.         m_enmState = sckOpen: Debug.Print "STATE: sckOpen"
  403.     End If
  404.     Exit Sub
  405. End If
  406.  
  407. 'try to get a 32 bits long that is used to identify a host
  408. Dim lngAddress As Long
  409. lngAddress = ResolveIfHostname(m_strRemoteHost, destConnect)
  410.  
  411. 'We've got two options here:
  412. '1) m_strRemoteHost was an IP, so a resolution wasn't
  413. '   necessary, and now lngAddress is a 32 bits long and
  414. '   we proceed to connect.
  415. '2) m_strRemoteHost was a hostname, so a resolution was
  416. '   necessary and it's taking place right now. We leave
  417. '   silently.
  418.  
  419. If lngAddress <> vbNull Then
  420.     ConnectToIP lngAddress, 0
  421. End If
  422.  
  423. End Sub
  424.  
  425. 'When the system resolves a hostname in asynchronous way we
  426. 'call this function to decide what to do with the result.
  427. Private Sub PostResolution(ByVal lngAsynHandle As Long, ByVal lngErrorCode As Long)
  428. If m_enmState <> sckResolvingHost Then Exit Sub
  429.  
  430. Dim enmDestination As DestResolucion
  431.  
  432. 'find out what the resolution destination was
  433. enmDestination = m_colWaitingResolutions.Item("R" & lngAsynHandle)
  434. 'erase that record from the collection since we won't need it any longer
  435. m_colWaitingResolutions.Remove "R" & lngAsynHandle
  436.  
  437. If lngErrorCode = 0 Then 'if there weren't errors trying to resolve the hostname
  438.     
  439.     m_enmState = sckHostResolved: Debug.Print "STATE: sckHostResolved"
  440.     
  441.     Dim udtHostent As HOSTENT
  442.     Dim lngPtrToIP As Long
  443.     Dim arrIpAddress(1 To 4) As Byte
  444.     Dim lngRemoteHostAddress As Long
  445.     Dim Count As Integer
  446.     Dim strIpAddress As String
  447.     
  448.     api_CopyMemory udtHostent, ByVal m_lngMemoryPointer, LenB(udtHostent)
  449.     api_CopyMemory lngPtrToIP, ByVal udtHostent.hAddrList, 4
  450.     api_CopyMemory arrIpAddress(1), ByVal lngPtrToIP, 4
  451.     api_CopyMemory lngRemoteHostAddress, ByVal lngPtrToIP, 4
  452.     
  453.     'free memmory, won't need it any longer
  454.     FreeMemory
  455.     
  456.     'We turn the 32 bits long into a readable string.
  457.     'Note: we don't need this string. I put this here just
  458.     'in case you need it.
  459.     For Count = 1 To 4
  460.         strIpAddress = strIpAddress & arrIpAddress(Count) & "."
  461.     Next
  462.         
  463.     strIpAddress = Left$(strIpAddress, Len(strIpAddress) - 1)
  464.     
  465.     'Decide what to do with the result according to the destination
  466.     Select Case enmDestination
  467.     
  468.     Case destConnect
  469.         ConnectToIP lngRemoteHostAddress, 0
  470.     
  471.     End Select
  472.  
  473. Else 'there were errors trying to resolve the hostname
  474.  
  475.     'free buffer memory
  476.     FreeMemory
  477.     
  478.     Select Case enmDestination
  479.         
  480.     Case destConnect
  481.         ConnectToIP vbNull, lngErrorCode
  482.         
  483.     End Select
  484.  
  485. End If
  486. End Sub
  487.  
  488. 'This procedure is called by the WindowProc callback function
  489. 'from the modSocketMaster module. The lngEventID argument is an
  490. 'ID of the network event occurred for the socket. The lngErrorCode
  491. 'argument contains an error code only if an error was occurred
  492. 'during an asynchronous execution.
  493. Private Sub PostSocket(ByVal lngEventID As Long, ByVal lngErrorCode As Long)
  494.  
  495. 'handle any possible error
  496. If lngErrorCode <> 0 Then
  497.     m_enmState = sckError: Debug.Print "STATE: sckError"
  498.     Dim blnCancelDisplay As Boolean
  499.     blnCancelDisplay = True
  500.     RaiseEvent Error(lngErrorCode, GetErrorDescription(lngErrorCode), 0, "CSocketMaster.PostSocket", "", 0, blnCancelDisplay)
  501.     If blnCancelDisplay = False Then MsgBox GetErrorDescription(lngErrorCode), vbOKOnly, "CSocketMaster.PostSocket"
  502.     Exit Sub
  503. End If
  504.  
  505. Dim udtSockAddr As sockaddr_in
  506. Dim lngResult As Long
  507. Dim lngBytesReceived As Long
  508.  
  509. Select Case lngEventID
  510.  
  511. '======================================================================
  512.  
  513. Case FD_CONNECT
  514.  
  515.     'Arrival of this message means that the connection initiated by the call
  516.     'of the connect Winsock API function was successfully established.
  517.  
  518.     Debug.Print "FD_CONNECT " & m_lngSocketHandle
  519.     
  520.     If m_enmState <> sckConnecting Then
  521.         Debug.Print "WARNING: Omitting FD_CONNECT"
  522.         Exit Sub
  523.     End If
  524.     
  525.     'Get the connection local end-point parameters
  526.     lngResult = api_getpeername(m_lngSocketHandle, udtSockAddr, LenB(udtSockAddr))
  527.         
  528.     If lngResult = 0 Then
  529.         m_lngRemotePort = modSocketMaster.IntegerToUnsigned(api_ntohs(udtSockAddr.sin_port))
  530.         m_strRemoteHostIP = StringFromPointer(api_inet_ntoa(udtSockAddr.sin_addr))
  531.     End If
  532.     
  533.     m_enmState = sckConnected: Debug.Print "STATE: sckConnected"
  534.     RaiseEvent Connect
  535.  
  536. '======================================================================
  537.  
  538. Case FD_WRITE
  539.  
  540.     'This message means that the socket in a write-able
  541.     'state, that is, buffer for outgoing data of the transport
  542.     'service is empty and ready to receive data to send through
  543.     'the network.
  544.     
  545.     Debug.Print "FD_WRITE " & m_lngSocketHandle
  546.     
  547.     If m_enmState <> sckConnected Then
  548.         Debug.Print "WARNING: Omitting FD_WRITE"
  549.         Exit Sub
  550.     End If
  551.     
  552.     If Len(m_strSendBuffer) > 0 Then
  553.         SendBufferedData
  554.     End If
  555.     
  556. '======================================================================
  557.  
  558. Case FD_READ
  559.  
  560.     'Some data has arrived for this socket.
  561.  
  562.     Debug.Print "FD_READ " & m_lngSocketHandle
  563.     
  564.     If m_enmProtocol = sckTCPProtocol Then
  565.         
  566.         If m_enmState <> sckConnected Then
  567.             Debug.Print "WARNING: Omitting FD_READ"
  568.             Exit Sub
  569.         End If
  570.         
  571.         'Call the RecvDataToBuffer function that move arrived data
  572.         'from the Winsock buffer to the local one and returns number
  573.         'of bytes received.
  574.     
  575.         lngBytesReceived = RecvDataToBuffer
  576.     
  577.         If lngBytesReceived > 0 Then
  578.             RaiseEvent DataArrival(Len(m_strRecvBuffer))
  579.         End If
  580.     
  581.     Else 'UDP protocol
  582.         
  583.         If m_enmState <> sckOpen Then
  584.             Debug.Print "WARNING: Omitting FD_READ"
  585.             Exit Sub
  586.         End If
  587.         
  588.         'If we use UDP we don't remove data from winsock buffer.
  589.         'We just let the user know the amount received so
  590.         'he/she can decide what to do.
  591.         
  592.         lngBytesReceived = GetBufferLenUDP
  593.         
  594.         If lngBytesReceived > 0 Then
  595.             RaiseEvent DataArrival(lngBytesReceived)
  596.         End If
  597.         
  598.         
  599.         'Now the buffer is emptied no matter what the user
  600.         'dicided to do with the received data
  601.         EmptyBuffer
  602.     End If
  603.     
  604.     
  605. '======================================================================
  606.  
  607. Case FD_ACCEPT
  608.  
  609.     'When the socket is in a listening state, arrival of this message
  610.     'means that a connection request was received. Call the accept
  611.     'Winsock API function in oreder to create a new socket for the
  612.     'requested connection.
  613.   
  614.     Debug.Print "FD_ACCEPT " & m_lngSocketHandle
  615.     If m_enmState <> sckListening Then
  616.         Debug.Print "WARNING: Omitting FD_ACCEPT"
  617.         Exit Sub
  618.     End If
  619.     
  620.     lngResult = api_accept(m_lngSocketHandle, udtSockAddr, LenB(udtSockAddr))
  621.     
  622.     If lngResult = INVALID_SOCKET Then
  623.         lngErrorCode = Err.LastDllError
  624.         Err.Raise lngErrorCode, "CSocketMaster.PostSocket", GetErrorDescription(lngErrorCode)
  625.     Else
  626.         'We assign a temporal instance of CSocketMaster to
  627.         'handle this new socket until user accepts (or not)
  628.         'the new connection
  629.         modSocketMaster.RegisterAccept lngResult
  630.         
  631.         'We change remote info before firing ConnectionRequest
  632.         'event so the user can see which host is trying to
  633.         'connect.
  634.         
  635.         Dim lngTempRP As Long
  636.         Dim strTempRHIP As String
  637.         Dim strTempRH As String
  638.         lngTempRP = m_lngRemotePort
  639.         strTempRHIP = m_strRemoteHostIP
  640.         strTempRH = m_strRemoteHost
  641.         
  642.         GetRemoteInfo lngResult, m_lngRemotePort, m_strRemoteHostIP, m_strRemoteHost
  643.         
  644.         Debug.Print "OK Accepted socket: " & lngResult
  645.         RaiseEvent ConnectionRequest(lngResult)
  646.         
  647.         'we return original info
  648.         If m_enmState = sckListening Then
  649.              m_lngRemotePort = lngTempRP
  650.              m_strRemoteHostIP = strTempRHIP
  651.              m_strRemoteHost = strTempRH
  652.         End If
  653.         
  654.         'This is very important. If the connection wasn't accepted
  655.         'we must close the socket.
  656.         If IsAcceptRegistered(lngResult) Then
  657.             api_closesocket lngResult
  658.             modSocketMaster.UnregisterSocket lngResult
  659.             modSocketMaster.UnregisterAccept lngResult
  660.             Debug.Print "OK Closed accepted socket: " & lngResult
  661.         End If
  662.     End If
  663.     
  664. '======================================================================
  665.     
  666. Case FD_CLOSE
  667.     
  668.     'This message means that the remote host is closing the conection
  669.     
  670.     Debug.Print "FD_CLOSE " & m_lngSocketHandle
  671.     
  672.     If m_enmState <> sckConnected Then
  673.         Debug.Print "WARNING: Omitting FD_CLOSE"
  674.         Exit Sub
  675.     End If
  676.     
  677.     m_enmState = sckClosing: Debug.Print "STATE: sckClosing"
  678.     RaiseEvent CloseSck
  679.     
  680. End Select
  681. End Sub
  682.  
  683. 'Connect to a given 32 bits long ip
  684. Private Sub ConnectToIP(ByVal lngRemoteHostAddress As Long, ByVal lngErrorCode As Long)
  685.  
  686. Dim blnCancelDisplay As Boolean
  687.  
  688. 'Check and handle errors
  689. If lngErrorCode <> 0 Then
  690.     m_enmState = sckError: Debug.Print "STATE: sckError"
  691.     blnCancelDisplay = True
  692.     RaiseEvent Error(lngErrorCode, GetErrorDescription(lngErrorCode), 0, "CSocketMaster.ConnectToIP", "", 0, blnCancelDisplay)
  693.     If blnCancelDisplay = False Then MsgBox GetErrorDescription(lngErrorCode), vbOKOnly, "CSocketMaster.ConnectToIP"
  694.     Exit Sub
  695. End If
  696.  
  697. 'Here we bind the socket
  698. If Not BindInternal Then Exit Sub
  699.  
  700. Debug.Print "OK Connecting to: " + m_strRemoteHost + " " + m_strRemoteHostIP
  701. m_enmState = sckConnecting: Debug.Print "STATE: sckConnecting"
  702.  
  703. Dim udtSockAddr As sockaddr_in
  704. Dim lngResult As Long
  705.  
  706. 'Build the sockaddr_in structure to pass it to the connect
  707. 'Winsock API function as an address of the remote host.
  708. With udtSockAddr
  709.     .sin_addr = lngRemoteHostAddress
  710.     .sin_family = AF_INET
  711.     .sin_port = api_htons(modSocketMaster.UnsignedToInteger(m_lngRemotePort))
  712. End With
  713.  
  714. 'Call the connect Winsock API function in order to establish connection.
  715. lngResult = api_connect(m_lngSocketHandle, udtSockAddr, LenB(udtSockAddr))
  716.  
  717. 'Check and handle errors
  718. If lngResult = SOCKET_ERROR Then
  719.     lngErrorCode = Err.LastDllError
  720.     If lngErrorCode <> WSAEWOULDBLOCK Then
  721.         If lngErrorCode = WSAEADDRNOTAVAIL Then
  722.             Err.Raise WSAEADDRNOTAVAIL, "CSocketMaster.ConnectToIP", GetErrorDescription(WSAEADDRNOTAVAIL)
  723.         Else
  724.             m_enmState = sckError: Debug.Print "STATE: sckError"
  725.             blnCancelDisplay = True
  726.             RaiseEvent Error(lngErrorCode, GetErrorDescription(lngErrorCode), 0, "CSocketMaster.ConnectToIP", "", 0, blnCancelDisplay)
  727.             If blnCancelDisplay = False Then MsgBox GetErrorDescription(lngErrorCode), vbOKOnly, "CSocketMaster.ConnectToIP"
  728.         End If
  729.     End If
  730. End If
  731.  
  732. End Sub
  733.  
  734. Public Sub Bind(Optional LocalPort As Variant, Optional LocalIP As Variant)
  735. If m_enmState <> sckClosed Then
  736.     Err.Raise sckInvalidOp, "CSocketMaster.Bind", "Invalid operation at current state"
  737. End If
  738.  
  739. If BindInternal(LocalPort, LocalIP) Then
  740.     m_enmState = sckOpen: Debug.Print "STATE: sckOpen"
  741. End If
  742. End Sub
  743.  
  744. 'This function binds a socket to a local port and IP.
  745. 'Retunrs TRUE if it has success.
  746. Private Function BindInternal(Optional ByVal varLocalPort As Variant, Optional ByVal varLocalIP As Variant) As Boolean
  747. If m_enmState = sckOpen Then
  748.     BindInternal = True
  749.     Exit Function
  750. End If
  751.  
  752. Dim lngLocalPortInternal As Long
  753. Dim strLocalHostInternal As String
  754. Dim strIP As String
  755. Dim lngAddressInternal As Long
  756. Dim lngResult As Long
  757. Dim lngErrorCode As Long
  758.  
  759. BindInternal = False
  760.  
  761. 'Check if varLocalPort is a number between 0 and 65535
  762. If Not IsMissing(varLocalPort) Then
  763.     
  764.     If IsNumeric(varLocalPort) Then
  765.         If varLocalPort < 0 Or varLocalPort > 65535 Then
  766.             BindInternal = False
  767.             Err.Raise sckInvalidArg, "CSocketMaster.BindInternal", "The argument passed to a function was not in the correct format or in the specified range."
  768.         Else
  769.             lngLocalPortInternal = CLng(varLocalPort)
  770.         End If
  771.     Else
  772.         BindInternal = False
  773.         Err.Raise sckUnsupported, "CSocketMaster.BindInternal", "Unsupported variant type."
  774.     End If
  775.     
  776. Else
  777.     
  778.     lngLocalPortInternal = m_lngLocalPort
  779.     
  780. End If
  781.  
  782. If Not IsMissing(varLocalIP) Then
  783.     If varLocalIP <> vbNullString Then
  784.         strLocalHostInternal = CStr(varLocalIP)
  785.     Else
  786.         strLocalHostInternal = GetLocalIP
  787.     End If
  788. Else
  789.     strLocalHostInternal = GetLocalIP
  790. End If
  791.  
  792. 'get a 32 bits long IP
  793. lngAddressInternal = ResolveIfHostnameSync(strLocalHostInternal, strIP, lngResult)
  794.  
  795. If lngResult <> 0 Then
  796.     Err.Raise sckInvalidArg, "CSocketMaster.BindInternal", "Invalid argument"
  797. End If
  798.  
  799. 'create a socket if there isn't one yet
  800. If Not SocketExists Then Exit Function
  801.  
  802. Dim udtSockAddr As sockaddr_in
  803.  
  804. With udtSockAddr
  805.     .sin_addr = lngAddressInternal
  806.     .sin_family = AF_INET
  807.     .sin_port = api_htons(modSocketMaster.UnsignedToInteger(lngLocalPortInternal))
  808. End With
  809.  
  810. 'bind the socket
  811. lngResult = api_bind(m_lngSocketHandle, udtSockAddr, LenB(udtSockAddr))
  812.  
  813. If lngResult = SOCKET_ERROR Then
  814.  
  815.     lngErrorCode = Err.LastDllError
  816.     Err.Raise lngErrorCode, "CSocketMaster.BindInternal", GetErrorDescription(lngErrorCode)
  817.     
  818. Else
  819.  
  820.     m_strLocalIP = strIP
  821.     
  822.     If lngLocalPortInternal <> 0 Then
  823.     
  824.         Debug.Print "OK Bind HOST: " & strLocalHostInternal & " PORT: " & lngLocalPortInternal
  825.         m_lngLocalPort = lngLocalPortInternal
  826.         
  827.     Else
  828.         lngResult = GetLocalPort(m_lngSocketHandle)
  829.         
  830.         If lngResult = SOCKET_ERROR Then
  831.             lngErrorCode = Err.LastDllError
  832.             Err.Raise lngErrorCode, "CSocketMaster.BindInternal", GetErrorDescription(lngErrorCode)
  833.         Else
  834.             Debug.Print "OK Bind HOST: " & strLocalHostInternal & " PORT: " & lngResult
  835.             m_lngLocalPortBind = lngResult
  836.         End If
  837.         
  838.     End If
  839.     
  840.     BindInternal = True
  841. End If
  842. End Function
  843.  
  844. 'Allocate some memory for HOSTEN structure and returns
  845. 'a pointer to this buffer if no error occurs.
  846. 'Returns 0 if it fails.
  847. Private Function AllocateMemory() As Long
  848. m_lngMemoryHandle = api_GlobalAlloc(GMEM_FIXED, MAXGETHOSTSTRUCT)
  849.  
  850. If m_lngMemoryHandle <> 0 Then
  851.     m_lngMemoryPointer = api_GlobalLock(m_lngMemoryHandle)
  852.     
  853.     If m_lngMemoryPointer <> 0 Then
  854.         api_GlobalUnlock (m_lngMemoryHandle)
  855.         AllocateMemory = m_lngMemoryPointer
  856.     Else
  857.         api_GlobalFree (m_lngMemoryHandle)
  858.         AllocateMemory = m_lngMemoryPointer '0
  859.     End If
  860.  
  861. Else
  862.     AllocateMemory = m_lngMemoryHandle '0
  863. End If
  864. End Function
  865.  
  866. 'Free memory allocated by AllocateMemory
  867. Private Sub FreeMemory()
  868. If m_lngMemoryHandle <> 0 Then
  869.     m_lngMemoryHandle = 0
  870.     m_lngMemoryPointer = 0
  871.     api_GlobalFree m_lngMemoryHandle
  872. End If
  873. End Sub
  874.  
  875. Private Function GetLocalHostName() As String
  876. Dim strHostNameBuf As String * LOCAL_HOST_BUFF
  877. Dim lngResult As Long
  878.  
  879. lngResult = api_gethostname(strHostNameBuf, LOCAL_HOST_BUFF)
  880.  
  881. If lngResult = SOCKET_ERROR Then
  882.     GetLocalHostName = vbNullString
  883.     Dim lngErrorCode As Long
  884.     lngErrorCode = Err.LastDllError
  885.     Err.Raise lngErrorCode, "CSocketMaster.GetLocalHostName", GetErrorDescription(lngErrorCode)
  886. Else
  887.     GetLocalHostName = Left(strHostNameBuf, InStr(1, strHostNameBuf, Chr(0)) - 1)
  888. End If
  889. End Function
  890.  
  891. Private Function GetLocalIP() As String
  892. Dim lngResult As Long
  893. Dim lngPtrToIP As Long
  894. Dim strLocalHost As String
  895. Dim arrIpAddress(1 To 4) As Byte
  896. Dim Count As Integer
  897. Dim udtHostent As HOSTENT
  898. Dim strIpAddress As String
  899.  
  900. strLocalHost = GetLocalHostName
  901.  
  902. lngResult = api_gethostbyname(strLocalHost)
  903.  
  904. If lngResult = 0 Then
  905.     GetLocalIP = vbNullString
  906.     Dim lngErrorCode As Long
  907.     lngErrorCode = Err.LastDllError
  908.     Err.Raise lngErrorCode, "CSocketMaster.GetLocalIP", GetErrorDescription(lngErrorCode)
  909. Else
  910.     api_CopyMemory udtHostent, ByVal lngResult, LenB(udtHostent)
  911.     api_CopyMemory lngPtrToIP, ByVal udtHostent.hAddrList, 4
  912.     api_CopyMemory arrIpAddress(1), ByVal lngPtrToIP, 4
  913.    
  914.     For Count = 1 To 4
  915.         strIpAddress = strIpAddress & arrIpAddress(Count) & "."
  916.     Next
  917.    
  918.     strIpAddress = Left$(strIpAddress, Len(strIpAddress) - 1)
  919.     GetLocalIP = strIpAddress
  920. End If
  921. End Function
  922.  
  923. 'If Host is an IP doesn't resolve anything and returns a
  924. 'a 32 bits long IP.
  925. 'If Host isn't an IP then returns vbNull, tries to resolve it
  926. 'in asynchronous way and acts according to enmDestination.
  927. Private Function ResolveIfHostname(ByVal Host As String, ByVal enmDestination As DestResolucion) As Long
  928. Dim lngAddress As Long
  929. lngAddress = api_inet_addr(Host)
  930.  
  931. If lngAddress = INADDR_NONE Then 'if Host isn't an IP
  932.     
  933.     ResolveIfHostname = vbNull
  934.     m_enmState = sckResolvingHost: Debug.Print "STATE: sckResolvingHost"
  935.     
  936.     If AllocateMemory Then
  937.         
  938.         Dim lngAsynHandle As Long
  939.         lngAsynHandle = modSocketMaster.ResolveHost(Host, m_lngMemoryPointer, ObjPtr(Me))
  940.         
  941.         If lngAsynHandle = 0 Then
  942.             FreeMemory
  943.             m_enmState = sckError: Debug.Print "STATE: sckError"
  944.             Dim lngErrorCode As Long
  945.             lngErrorCode = Err.LastDllError
  946.             Dim blnCancelDisplay As Boolean
  947.             blnCancelDisplay = True
  948.             RaiseEvent Error(lngErrorCode, GetErrorDescription(lngErrorCode), 0, "CSocketMaster.ResolveIfHostname", "", 0, blnCancelDisplay)
  949.             If blnCancelDisplay = False Then MsgBox GetErrorDescription(lngErrorCode), vbOKOnly, "CSocketMaster.ResolveIfHostname"
  950.         Else
  951.             m_colWaitingResolutions.Add enmDestination, "R" & lngAsynHandle
  952.             Debug.Print "Resolving host " & Host; " with handle " & lngAsynHandle
  953.         End If
  954.         
  955.     Else
  956.         
  957.         m_enmState = sckError: Debug.Print "STATE: sckError"
  958.         Debug.Print "Error trying to allocate memory"
  959.         Err.Raise sckOutOfMemory, "CSocketMaster.ResolveIfHostname", "Out of memory"
  960.         
  961.     End If
  962.     
  963. Else 'if Host is an IP doen't need to resolve anything
  964.     ResolveIfHostname = lngAddress
  965. End If
  966. End Function
  967.  
  968. 'Resolves a hots (if necessary) in synchronous way
  969. 'If succeeds returns a 32 bits long IP,
  970. 'strHostIP = readable IP string and lngErrorCode = 0
  971. 'If fails returns vbNull,
  972. 'strHostIP = vbNullString and lngErrorCode <> 0
  973. Private Function ResolveIfHostnameSync(ByVal Host As String, ByRef strHostIP As String, ByRef lngErrorCode As Long) As Long
  974. Dim lngPtrToHOSTENT As Long
  975. Dim udtHostent As HOSTENT
  976. Dim lngAddress As Long
  977. Dim lngPtrToIP As Long
  978. Dim arrIpAddress(1 To 4) As Byte
  979. Dim Count As Integer
  980.  
  981. If Host = vbNullString Then
  982.     strHostIP = vbNullString
  983.     lngErrorCode = WSAEAFNOSUPPORT
  984.     ResolveIfHostnameSync = vbNull
  985.     Exit Function
  986. End If
  987.  
  988. lngAddress = api_inet_addr(Host)
  989.  
  990. If lngAddress = INADDR_NONE Then 'if Host isn't an IP
  991.     
  992.     lngPtrToHOSTENT = api_gethostbyname(Host)
  993.     
  994.     If lngPtrToHOSTENT = 0 Then
  995.         lngErrorCode = Err.LastDllError
  996.         strHostIP = vbNullString
  997.         ResolveIfHostnameSync = vbNull
  998.     Else
  999.         api_CopyMemory udtHostent, ByVal lngPtrToHOSTENT, LenB(udtHostent)
  1000.         api_CopyMemory lngPtrToIP, ByVal udtHostent.hAddrList, 4
  1001.         api_CopyMemory arrIpAddress(1), ByVal lngPtrToIP, 4
  1002.         api_CopyMemory lngAddress, ByVal lngPtrToIP, 4
  1003.         
  1004.         For Count = 1 To 4
  1005.             strHostIP = strHostIP & arrIpAddress(Count) & "."
  1006.         Next
  1007.         
  1008.         strHostIP = Left$(strHostIP, Len(strHostIP) - 1)
  1009.         
  1010.         lngErrorCode = 0
  1011.         ResolveIfHostnameSync = lngAddress
  1012.     End If
  1013.     
  1014. Else 'if Host is an IP doen't need to resolve anything
  1015.     
  1016.     lngErrorCode = 0
  1017.     strHostIP = Host
  1018.     ResolveIfHostnameSync = lngAddress
  1019.     
  1020. End If
  1021. End Function
  1022.  
  1023. 'Returns local port from a connected or bound socket.
  1024. 'Returns SOCKET_ERROR if fails.
  1025. Private Function GetLocalPort(ByVal lngSocket As Long) As Long
  1026. Dim udtSockAddr As sockaddr_in
  1027. Dim lngResult As Long
  1028.  
  1029. lngResult = api_getsockname(lngSocket, udtSockAddr, LenB(udtSockAddr))
  1030.  
  1031. If lngResult = SOCKET_ERROR Then
  1032.     GetLocalPort = SOCKET_ERROR
  1033. Else
  1034.     GetLocalPort = modSocketMaster.IntegerToUnsigned(api_ntohs(udtSockAddr.sin_port))
  1035. End If
  1036. End Function
  1037.  
  1038. Public Sub SendData(data As Variant)
  1039.  
  1040. On Error GoTo error1
  1041. Dim arrData() As Byte 'We store the data here before send it
  1042.  
  1043. If m_enmProtocol = sckTCPProtocol Then
  1044.     If m_enmState <> sckConnected Then
  1045.         Err.Raise sckBadState, "CSocketMaster.SendData", "Wrong protocol or connection state for the requested transaction or request"
  1046.         Exit Sub
  1047.     End If
  1048. Else 'If we use UDP we create a socket if there isn't one yet
  1049.     If Not SocketExists Then Exit Sub
  1050.     If Not BindInternal Then Exit Sub
  1051.     m_enmState = sckOpen
  1052. End If
  1053.  
  1054. 'We need to convert data variant into a byte array
  1055. Select Case varType(data)
  1056.         Case vbString
  1057.             Dim strdata As String
  1058.             strdata = CStr(data)
  1059.             If Len(strdata) = 0 Then Exit Sub
  1060.             ReDim arrData(Len(strdata) - 1)
  1061.             arrData() = StrConv(strdata, vbFromUnicode)
  1062.         Case vbArray + vbByte
  1063.             Dim strArray As String
  1064.             strArray = StrConv(data, vbUnicode)
  1065.             If Len(strArray) = 0 Then Exit Sub
  1066.             arrData() = StrConv(strArray, vbFromUnicode)
  1067.         Case vbBoolean
  1068.             Dim blnData As Boolean
  1069.             blnData = CBool(data)
  1070.             ReDim arrData(LenB(blnData) - 1)
  1071.             api_CopyMemory arrData(0), blnData, LenB(blnData)
  1072.         Case vbByte
  1073.             Dim bytData As Byte
  1074.             bytData = CByte(data)
  1075.             ReDim arrData(LenB(bytData) - 1)
  1076.             api_CopyMemory arrData(0), bytData, LenB(bytData)
  1077.         Case vbCurrency
  1078.             Dim curData As Currency
  1079.             curData = CCur(data)
  1080.             ReDim arrData(LenB(curData) - 1)
  1081.             api_CopyMemory arrData(0), curData, LenB(curData)
  1082.         Case vbDate
  1083.             Dim datData As Date
  1084.             datData = CDate(data)
  1085.             ReDim arrData(LenB(datData) - 1)
  1086.             api_CopyMemory arrData(0), datData, LenB(datData)
  1087.         Case vbDouble
  1088.             Dim dblData As Double
  1089.             dblData = CDbl(data)
  1090.             ReDim arrData(LenB(dblData) - 1)
  1091.             api_CopyMemory arrData(0), dblData, LenB(dblData)
  1092.         Case vbInteger
  1093.             Dim intData As Integer
  1094.             intData = CInt(data)
  1095.             ReDim arrData(LenB(intData) - 1)
  1096.             api_CopyMemory arrData(0), intData, LenB(intData)
  1097.         Case vbLong
  1098.             Dim lngData As Long
  1099.             lngData = CLng(data)
  1100.             ReDim arrData(LenB(lngData) - 1)
  1101.             api_CopyMemory arrData(0), lngData, LenB(lngData)
  1102.         Case vbSingle
  1103.             Dim sngData As Single
  1104.             sngData = CSng(data)
  1105.             ReDim arrData(LenB(sngData) - 1)
  1106.             api_CopyMemory arrData(0), sngData, LenB(sngData)
  1107.         Case Else
  1108.             Err.Raise sckUnsupported, "CSocketMaster.SendData", "Unsupported variant type."
  1109.     End Select
  1110.  
  1111. 'if there's already something in the buffer that means we are
  1112. 'already sending data, so we put the new data in the buffer
  1113. 'and exit silently
  1114. If Len(m_strSendBuffer) > 0 Then
  1115.     m_strSendBuffer = m_strSendBuffer + StrConv(arrData(), vbUnicode)
  1116.     Exit Sub
  1117. Else
  1118.     m_strSendBuffer = m_strSendBuffer + StrConv(arrData(), vbUnicode)
  1119. End If
  1120.  
  1121. 'send the data
  1122. SendBufferedData
  1123. Exit Sub
  1124. error1:
  1125.     MsgBox "Winsock Error", vbCritical, "Aleart"
  1126.     Exit Sub
  1127. End Sub
  1128.  
  1129. 'Check which protocol we are using to decide which
  1130. 'function should handle the data sending.
  1131. Private Sub SendBufferedData()
  1132. If m_enmProtocol = sckTCPProtocol Then
  1133.     SendBufferedDataTCP
  1134. Else
  1135.     SendBufferedDataUDP
  1136. End If
  1137. End Sub
  1138.  
  1139. 'Send buffered data if we are using UDP protocol.
  1140. Private Sub SendBufferedDataUDP()
  1141. Dim lngAddress As Long
  1142. Dim udtSockAddr As sockaddr_in
  1143. Dim arrData() As Byte
  1144. Dim lngBufferLength As Long
  1145. Dim lngResult As Long
  1146. Dim lngErrorCode As Long
  1147.  
  1148.    
  1149. Dim strTemp As String
  1150. lngAddress = ResolveIfHostnameSync(m_strRemoteHost, strTemp, lngErrorCode)
  1151.     
  1152. If lngErrorCode <> 0 Then
  1153.     m_strSendBuffer = ""
  1154.     
  1155.     If lngErrorCode = WSAEAFNOSUPPORT Then
  1156.         Err.Raise lngErrorCode, "CSocketMaster.SendBufferedDataUDP", GetErrorDescription(lngErrorCode)
  1157.     Else
  1158.         Err.Raise sckInvalidArg, "CSocketMaster.SendBufferedDataUDP", "Invalid argument"
  1159.     End If
  1160. End If
  1161.  
  1162. With udtSockAddr
  1163.     .sin_addr = lngAddress
  1164.     .sin_family = AF_INET
  1165.     .sin_port = api_htons(modSocketMaster.UnsignedToInteger(m_lngRemotePort))
  1166. End With
  1167.     
  1168. lngBufferLength = Len(m_strSendBuffer)
  1169.     
  1170. arrData() = StrConv(m_strSendBuffer, vbFromUnicode)
  1171.     
  1172. m_strSendBuffer = ""
  1173.  
  1174. lngResult = api_sendto(m_lngSocketHandle, arrData(0), lngBufferLength, 0&, udtSockAddr, LenB(udtSockAddr))
  1175.     
  1176. If lngResult = SOCKET_ERROR Then
  1177.     lngErrorCode = Err.LastDllError
  1178.     m_enmState = sckError: Debug.Print "STATE: sckError"
  1179.     Dim blnCancelDisplay As Boolean
  1180.     blnCancelDisplay = True
  1181.     RaiseEvent Error(lngErrorCode, GetErrorDescription(lngErrorCode), 0, "CSocketMaster.SendBufferedDataUDP", "", 0, blnCancelDisplay)
  1182.     If blnCancelDisplay = False Then MsgBox GetErrorDescription(lngErrorCode), vbOKOnly, "CSocketMaster.SendBufferedDataUDP"
  1183. End If
  1184.     
  1185. End Sub
  1186.  
  1187. 'Send buffered data if we are using TCP protocol.
  1188. Private Sub SendBufferedDataTCP()
  1189.  
  1190. Dim arrData()       As Byte
  1191. Dim lngBufferLength As Long
  1192. Dim lngResult    As Long
  1193. Dim lngTotalSent As Long
  1194.  
  1195. Do Until lngResult = SOCKET_ERROR Or Len(m_strSendBuffer) = 0
  1196.  
  1197.     lngBufferLength = Len(m_strSendBuffer)
  1198.  
  1199.     If lngBufferLength > m_lngSendBufferLen Then
  1200.         lngBufferLength = m_lngSendBufferLen
  1201.         arrData() = StrConv(Left$(m_strSendBuffer, m_lngSendBufferLen), vbFromUnicode)
  1202.     Else
  1203.         arrData() = StrConv(m_strSendBuffer, vbFromUnicode)
  1204.     End If
  1205.  
  1206.     lngResult = api_send(m_lngSocketHandle, arrData(0), lngBufferLength, 0&)
  1207.  
  1208.     If lngResult = SOCKET_ERROR Then
  1209.         Dim lngErrorCode As Long
  1210.         lngErrorCode = Err.LastDllError
  1211.     
  1212.         If lngErrorCode = WSAEWOULDBLOCK Then
  1213.             Debug.Print "WARNING: Send buffer full, waiting..."
  1214.             If lngTotalSent > 0 Then RaiseEvent SendProgress(lngTotalSent, Len(m_strSendBuffer))
  1215.         Else
  1216.             m_enmState = sckError: Debug.Print "STATE: sckError"
  1217.             Dim blnCancelDisplay As Boolean
  1218.             blnCancelDisplay = True
  1219.             RaiseEvent Error(lngErrorCode, GetErrorDescription(lngErrorCode), 0, "CSocketMaster.SendBufferedData", "", 0, blnCancelDisplay)
  1220.             If blnCancelDisplay = False Then MsgBox GetErrorDescription(lngErrorCode), vbOKOnly, "CSocketMaster.SendBufferedData"
  1221.         End If
  1222.  
  1223.     Else
  1224.         Debug.Print "OK Bytes sent: " & lngResult
  1225.         lngTotalSent = lngTotalSent + lngResult
  1226.         If Len(m_strSendBuffer) > lngResult Then
  1227.             m_strSendBuffer = Mid$(m_strSendBuffer, lngResult + 1)
  1228.         Else
  1229.             Debug.Print "OK Finished SENDING"
  1230.             m_strSendBuffer = ""
  1231.             Dim lngTemp As Long
  1232.             lngTemp = lngTotalSent
  1233.             lngTotalSent = 0
  1234.             RaiseEvent SendProgress(lngTemp, 0)
  1235.             RaiseEvent SendComplete
  1236.         End If
  1237.     End If
  1238.  
  1239. Loop
  1240.  
  1241. End Sub
  1242.  
  1243. 'This function retrieves data from the Winsock buffer
  1244. 'into the class local buffer. The function returns number
  1245. 'of bytes retrieved (received).
  1246. Private Function RecvDataToBuffer() As Long
  1247. Dim arrBuffer() As Byte
  1248. Dim lngBytesReceived As Long
  1249. Dim strBuffTemporal As String
  1250.  
  1251. ReDim arrBuffer(m_lngRecvBufferLen - 1)
  1252.  
  1253. lngBytesReceived = api_recv(m_lngSocketHandle, arrBuffer(0), m_lngRecvBufferLen, 0&)
  1254.  
  1255. If lngBytesReceived = SOCKET_ERROR Then
  1256.     
  1257.     m_enmState = sckError: Debug.Print "STATE: sckError"
  1258.     Dim lngErrorCode As Long
  1259.     lngErrorCode = Err.LastDllError
  1260.     Err.Raise lngErrorCode, "CSocketMaster.RecvDataToBuffer", GetErrorDescription(lngErrorCode)
  1261.     
  1262. ElseIf lngBytesReceived > 0 Then
  1263.     
  1264.     strBuffTemporal = StrConv(arrBuffer(), vbUnicode)
  1265.     m_strRecvBuffer = m_strRecvBuffer & Left$(strBuffTemporal, lngBytesReceived)
  1266.     RecvDataToBuffer = lngBytesReceived
  1267.     
  1268. End If
  1269.  
  1270. End Function
  1271.  
  1272. 'Retrieves some socket options.
  1273. 'If it is an UDP socket also sets SO_BROADCAST option.
  1274. Private Sub ProcessOptions()
  1275. Dim lngResult As Long
  1276. Dim lngBuffer As Long
  1277. Dim lngErrorCode As Long
  1278.  
  1279. If m_enmProtocol = sckTCPProtocol Then
  1280.     lngResult = api_getsockopt(m_lngSocketHandle, SOL_SOCKET, SO_RCVBUF, lngBuffer, LenB(lngBuffer))
  1281.     
  1282.     If lngResult = SOCKET_ERROR Then
  1283.         lngErrorCode = Err.LastDllError
  1284.         Err.Raise lngErrorCode, "CSocketMaster.ProcessOptions", GetErrorDescription(lngErrorCode)
  1285.     Else
  1286.         m_lngRecvBufferLen = lngBuffer
  1287.     End If
  1288.  
  1289.     lngResult = api_getsockopt(m_lngSocketHandle, SOL_SOCKET, SO_SNDBUF, lngBuffer, LenB(lngBuffer))
  1290.  
  1291.     If lngResult = SOCKET_ERROR Then
  1292.         lngErrorCode = Err.LastDllError
  1293.         Err.Raise lngErrorCode, "CSocketMaster.ProcessOptions", GetErrorDescription(lngErrorCode)
  1294.     Else
  1295.         m_lngSendBufferLen = lngBuffer
  1296.     End If
  1297.  
  1298. Else
  1299.     lngBuffer = 1
  1300.     lngResult = api_setsockopt(m_lngSocketHandle, SOL_SOCKET, SO_BROADCAST, lngBuffer, LenB(lngBuffer))
  1301.     
  1302.     lngResult = api_getsockopt(m_lngSocketHandle, SOL_SOCKET, SO_MAX_MSG_SIZE, lngBuffer, LenB(lngBuffer))
  1303.  
  1304.     If lngResult = SOCKET_ERROR Then
  1305.         lngErrorCode = Err.LastDllError
  1306.         Err.Raise lngErrorCode, "CSocketMaster.ProcessOptions", GetErrorDescription(lngErrorCode)
  1307.     Else
  1308.         m_lngRecvBufferLen = lngBuffer
  1309.         m_lngSendBufferLen = lngBuffer
  1310.     End If
  1311. End If
  1312.  
  1313.  
  1314. Debug.Print "Winsock buffer size for sends: " & m_lngRecvBufferLen
  1315. Debug.Print "Winsock buffer size for receives: " & m_lngSendBufferLen
  1316. End Sub
  1317.  
  1318. Public Sub GetData(ByRef data As Variant, Optional varType As Variant, Optional maxLen As Variant)
  1319.  
  1320. If m_enmProtocol = sckTCPProtocol Then
  1321.     If m_enmState <> sckConnected And Not m_blnAcceptClass Then
  1322.         Err.Raise sckBadState, "CSocketMaster.GetData", "Wrong protocol or connection state for the requested transaction or request"
  1323.         Exit Sub
  1324.     End If
  1325. Else
  1326.     If m_enmState <> sckOpen Then
  1327.         Err.Raise sckBadState, "CSocketMaster.GetData", "Wrong protocol or connection state for the requested transaction or request"
  1328.         Exit Sub
  1329.     End If
  1330.     If GetBufferLenUDP = 0 Then Exit Sub
  1331. End If
  1332.  
  1333. If Not IsMissing(maxLen) Then
  1334.     If IsNumeric(maxLen) Then
  1335.         If CLng(maxLen) < 0 Then
  1336.             Err.Raise sckInvalidArg, "CSocketMaster.GetData", "The argument passed to a function was not in the correct format or in the specified range."
  1337.         End If
  1338.     Else
  1339.         If m_enmProtocol = sckTCPProtocol Then
  1340.             maxLen = Len(m_strRecvBuffer)
  1341.         Else
  1342.             maxLen = GetBufferLenUDP
  1343.         End If
  1344.     End If
  1345. End If
  1346.  
  1347. Dim lngBytesRecibidos  As Long
  1348.  
  1349. lngBytesRecibidos = RecvData(data, False, varType, maxLen)
  1350. Debug.Print "OK Bytes obtained from buffer: " & lngBytesRecibidos
  1351.  
  1352. End Sub
  1353.  
  1354. Public Sub PeekData(ByRef data As Variant, Optional varType As Variant, Optional maxLen As Variant)
  1355.  
  1356. If m_enmProtocol = sckTCPProtocol Then
  1357.     If m_enmState <> sckConnected Then
  1358.         Err.Raise sckBadState, "CSocketMaster.PeekData", "Wrong protocol or connection state for the requested transaction or request"
  1359.         Exit Sub
  1360.     End If
  1361. Else
  1362.     If m_enmState <> sckOpen Then
  1363.         Err.Raise sckBadState, "CSocketMaster.PeekData", "Wrong protocol or connection state for the requested transaction or request"
  1364.         Exit Sub
  1365.     End If
  1366.     If GetBufferLenUDP = 0 Then Exit Sub
  1367. End If
  1368.  
  1369. If Not IsMissing(maxLen) Then
  1370.     If IsNumeric(maxLen) Then
  1371.         If CLng(maxLen) < 0 Then
  1372.             Err.Raise sckInvalidArg, "CSocketMaster.PeekData", "The argument passed to a function was not in the correct format or in the specified range."
  1373.         End If
  1374.     Else
  1375.         If m_enmProtocol = sckTCPProtocol Then
  1376.             maxLen = Len(m_strRecvBuffer)
  1377.         Else
  1378.             maxLen = GetBufferLenUDP
  1379.         End If
  1380.     End If
  1381. End If
  1382.  
  1383. Dim lngBytesRecibidos  As Long
  1384.  
  1385. lngBytesRecibidos = RecvData(data, True, varType, maxLen)
  1386. Debug.Print "OK Bytes obtained from buffer: " & lngBytesRecibidos
  1387. End Sub
  1388.  
  1389.  
  1390. 'This function is to retrieve data from the buffer. If we are using TCP
  1391. 'then the data is retrieved from a local buffer (m_strRecvBuffer). If we
  1392. 'are using UDP the data is retrieved from winsock buffer.
  1393. 'It can be called by two public methods of the class - GetData and PeekData.
  1394. 'Behavior of the function is defined by the blnPeek argument. If a value of
  1395. 'that argument is TRUE, the function returns number of bytes in the
  1396. 'buffer, and copy data from that buffer into the data argument.
  1397. 'If a value of the blnPeek is FALSE, then this function returns number of
  1398. 'bytes received, and move data from the buffer into the data
  1399. 'argument. MOVE means that data will be removed from the buffer.
  1400. Private Function RecvData(ByRef data As Variant, ByVal blnPeek As Boolean, Optional varClass As Variant, Optional maxLen As Variant) As Long
  1401.  
  1402. Dim blnMaxLenMiss   As Boolean
  1403. Dim blnClassMiss As Boolean
  1404. Dim strRecvData     As String
  1405. Dim lngBufferLen    As Long
  1406. Dim arrBuffer()     As Byte
  1407. Dim lngErrorCode    As Long
  1408.  
  1409. If m_enmProtocol = sckTCPProtocol Then
  1410.     lngBufferLen = Len(m_strRecvBuffer)
  1411. Else
  1412.     lngBufferLen = GetBufferLenUDP
  1413. End If
  1414.  
  1415. blnMaxLenMiss = IsMissing(maxLen)
  1416. blnClassMiss = IsMissing(varClass)
  1417.  
  1418. 'Select type of data
  1419. If varType(data) = vbEmpty Then
  1420.     If blnClassMiss Then varClass = vbArray + vbByte
  1421. Else
  1422.     varClass = varType(data)
  1423. End If
  1424.  
  1425. 'As stated on Winsock control documentation if the
  1426. 'data type passed is string or byte array type then
  1427. 'we must take into account maxLen argument.
  1428. 'If it is another type maxLen is ignored.
  1429. If varClass = vbString Or varClass = vbArray + vbByte Then
  1430.  
  1431.     If blnMaxLenMiss Then 'if maxLen argument is missing
  1432.     
  1433.         If lngBufferLen = 0 Then
  1434.         
  1435.             RecvData = 0
  1436.         
  1437.             arrBuffer = StrConv("", vbFromUnicode)
  1438.             data = arrBuffer
  1439.  
  1440.             Exit Function
  1441.     
  1442.         Else
  1443.             
  1444.             RecvData = lngBufferLen
  1445.             arrBuffer = BuildArray(lngBufferLen, blnPeek, lngErrorCode)
  1446.  
  1447.         End If
  1448.  
  1449.     Else 'if maxLen argument is not missing
  1450.  
  1451.         If maxLen = 0 Or lngBufferLen = 0 Then
  1452.  
  1453.             RecvData = 0
  1454.         
  1455.             arrBuffer = StrConv("", vbFromUnicode)
  1456.             data = arrBuffer
  1457.             
  1458.             If m_enmProtocol = sckUDPProtocol Then
  1459.                 EmptyBuffer
  1460.                 Err.Raise WSAEMSGSIZE, "CSocketMaster.RecvData", GetErrorDescription(WSAEMSGSIZE)
  1461.             End If
  1462.             
  1463.             Exit Function
  1464.         
  1465.         ElseIf maxLen > lngBufferLen Then
  1466.             
  1467.             RecvData = lngBufferLen
  1468.             arrBuffer = BuildArray(lngBufferLen, blnPeek, lngErrorCode)
  1469.  
  1470.         Else
  1471.             
  1472.             RecvData = CLng(maxLen)
  1473.             arrBuffer() = BuildArray(CLng(maxLen), blnPeek, lngErrorCode)
  1474.  
  1475.         End If
  1476.     
  1477.     End If
  1478.         
  1479. End If
  1480.  
  1481.     Select Case varClass
  1482.     
  1483.     Case vbString
  1484.         Dim strdata As String
  1485.         strdata = StrConv(arrBuffer(), vbUnicode)
  1486.         data = strdata
  1487.     Case vbArray + vbByte
  1488.         data = arrBuffer
  1489.     Case vbBoolean
  1490.         Dim blnData As Boolean
  1491.         If LenB(blnData) > lngBufferLen Then Exit Function
  1492.         arrBuffer = BuildArray(LenB(blnData), blnPeek, lngErrorCode)
  1493.         RecvData = LenB(blnData)
  1494.         api_CopyMemory blnData, arrBuffer(0), LenB(blnData)
  1495.         data = blnData
  1496.     Case vbByte
  1497.         Dim bytData As Byte
  1498.         If LenB(bytData) > lngBufferLen Then Exit Function
  1499.         arrBuffer = BuildArray(LenB(bytData), blnPeek, lngErrorCode)
  1500.         RecvData = LenB(bytData)
  1501.         api_CopyMemory bytData, arrBuffer(0), LenB(bytData)
  1502.         data = bytData
  1503.     Case vbCurrency
  1504.         Dim curData As Currency
  1505.         If LenB(curData) > lngBufferLen Then Exit Function
  1506.         arrBuffer = BuildArray(LenB(curData), blnPeek, lngErrorCode)
  1507.         RecvData = LenB(curData)
  1508.         api_CopyMemory curData, arrBuffer(0), LenB(curData)
  1509.         data = curData
  1510.     Case vbDate
  1511.         Dim datData As Date
  1512.         If LenB(datData) > lngBufferLen Then Exit Function
  1513.         arrBuffer = BuildArray(LenB(datData), blnPeek, lngErrorCode)
  1514.         RecvData = LenB(datData)
  1515.         api_CopyMemory datData, arrBuffer(0), LenB(datData)
  1516.         data = datData
  1517.     Case vbDouble
  1518.         Dim dblData As Double
  1519.         If LenB(dblData) > lngBufferLen Then Exit Function
  1520.         arrBuffer = BuildArray(LenB(dblData), blnPeek, lngErrorCode)
  1521.         RecvData = LenB(dblData)
  1522.         api_CopyMemory dblData, arrBuffer(0), LenB(dblData)
  1523.         data = dblData
  1524.     Case vbInteger
  1525.         Dim intData As Integer
  1526.         If LenB(intData) > lngBufferLen Then Exit Function
  1527.         arrBuffer = BuildArray(LenB(intData), blnPeek, lngErrorCode)
  1528.         RecvData = LenB(intData)
  1529.         api_CopyMemory intData, arrBuffer(0), LenB(intData)
  1530.         data = intData
  1531.     Case vbLong
  1532.         Dim lngData As Long
  1533.         If LenB(lngData) > lngBufferLen Then Exit Function
  1534.         arrBuffer = BuildArray(LenB(lngData), blnPeek, lngErrorCode)
  1535.         RecvData = LenB(lngData)
  1536.         api_CopyMemory lngData, arrBuffer(0), LenB(lngData)
  1537.         data = lngData
  1538.     Case vbSingle
  1539.         Dim sngData As Single
  1540.         If LenB(sngData) > lngBufferLen Then Exit Function
  1541.         arrBuffer = BuildArray(LenB(sngData), blnPeek, lngErrorCode)
  1542.         RecvData = LenB(sngData)
  1543.         api_CopyMemory sngData, arrBuffer(0), LenB(sngData)
  1544.         data = sngData
  1545.     Case Else
  1546.         Err.Raise sckUnsupported, "CSocketMaster.RecvData", "Unsupported variant type."
  1547.        
  1548.     End Select
  1549.  
  1550. 'if BuildArray returns an error is handled here
  1551. If lngErrorCode <> 0 Then
  1552.     Err.Raise lngErrorCode, "CSocketMaster.RecvData", GetErrorDescription(lngErrorCode)
  1553. End If
  1554.  
  1555. End Function
  1556.  
  1557. 'Returns a byte array of Size bytes filled with incoming buffer data.
  1558. Private Function BuildArray(ByVal Size As Long, ByVal blnPeek As Boolean, ByRef lngErrorCode As Long) As Byte()
  1559. Dim strdata As String
  1560.  
  1561. If m_enmProtocol = sckTCPProtocol Then
  1562.         
  1563.     strdata = Left$(m_strRecvBuffer, CLng(Size))
  1564.     BuildArray = StrConv(strdata, vbFromUnicode)
  1565.                 
  1566.     If Not blnPeek Then
  1567.         m_strRecvBuffer = Mid$(m_strRecvBuffer, Size + 1)
  1568.     End If
  1569.  
  1570. Else 'UDP protocol
  1571.     Dim arrBuffer() As Byte
  1572.     Dim lngResult As Long
  1573.     Dim udtSockAddr As sockaddr_in
  1574.     Dim lngFlags As Long
  1575.     
  1576.     If blnPeek Then lngFlags = MSG_PEEK
  1577.     
  1578.     ReDim arrBuffer(Size - 1)
  1579.     
  1580.     lngResult = api_recvfrom(m_lngSocketHandle, arrBuffer(0), Size, lngFlags, udtSockAddr, LenB(udtSockAddr))
  1581.     
  1582.     If lngResult = SOCKET_ERROR Then
  1583.         lngErrorCode = Err.LastDllError
  1584.     End If
  1585.     
  1586.     BuildArray = arrBuffer
  1587.     GetRemoteInfoFromSI udtSockAddr, m_lngRemotePort, m_strRemoteHostIP, m_strRemoteHost
  1588.     
  1589. End If
  1590. End Function
  1591.  
  1592. 'Clean resolution system that is in charge of
  1593. 'asynchronous hostname resolutions.
  1594. Private Sub CleanResolutionSystem()
  1595. Dim varAsynHandle As Variant
  1596.  
  1597. 'cancel async resolutions if they're still running
  1598. For Each varAsynHandle In m_colWaitingResolutions
  1599.     api_WSACancelAsyncRequest varAsynHandle
  1600.     modSocketMaster.UnregisterResolution varAsynHandle
  1601. Next
  1602.  
  1603. 'free memory buffer where resolution results are stored
  1604. FreeMemory
  1605. End Sub
  1606.  
  1607. Public Sub Listen()
  1608. If m_enmState <> sckClosed And m_enmState <> sckOpen Then
  1609.     Err.Raise sckInvalidOp, "CSocketMaster.Listen", "Invalid operation at current state"
  1610. End If
  1611.  
  1612. If Not SocketExists Then Exit Sub
  1613. If Not BindInternal Then Exit Sub
  1614.  
  1615. Dim lngResult As Long
  1616.  
  1617. lngResult = api_listen(m_lngSocketHandle, SOMAXCONN)
  1618.  
  1619. If lngResult = SOCKET_ERROR Then
  1620.     Dim lngErrorCode As Long
  1621.     lngErrorCode = Err.LastDllError
  1622.     Err.Raise lngErrorCode, "CSocketMaster.Listen", GetErrorDescription(lngErrorCode)
  1623. Else
  1624.     m_enmState = sckListening: Debug.Print "STATE: sckListening"
  1625. End If
  1626.  
  1627. End Sub
  1628.  
  1629. Public Sub Accept(requestID As Long)
  1630. If m_enmState <> sckClosed Then
  1631.     Err.Raise sckInvalidOp, "CSocketMaster.Accept", "Invalid operation at current state"
  1632. End If
  1633.  
  1634. Dim lngResult As Long
  1635. Dim udtSockAddr As sockaddr_in
  1636. Dim lngErrorCode As Long
  1637.  
  1638. m_lngSocketHandle = requestID
  1639. m_enmProtocol = sckTCPProtocol
  1640. ProcessOptions
  1641.  
  1642. If Not modSocketMaster.IsAcceptRegistered(requestID) Then
  1643.     If IsSocketRegistered(requestID) Then
  1644.         Err.Raise sckBadState, "CSocketMaster.Accept", "Wrong protocol or connection state for the requested transaction or request"
  1645.     Else
  1646.         m_blnAcceptClass = True
  1647.         m_enmState = sckConnected: Debug.Print "STATE: sckConnected"
  1648.         modSocketMaster.RegisterSocket m_lngSocketHandle, ObjPtr(Me), False
  1649.         Exit Sub
  1650.     End If
  1651. End If
  1652.  
  1653. Dim clsSocket As CSocketMaster
  1654. Set clsSocket = GetAcceptClass(requestID)
  1655. modSocketMaster.UnregisterAccept requestID
  1656.  
  1657. lngResult = api_getsockname(m_lngSocketHandle, udtSockAddr, LenB(udtSockAddr))
  1658.  
  1659. If lngResult = SOCKET_ERROR Then
  1660.     
  1661.     lngErrorCode = Err.LastDllError
  1662.     Err.Raise lngErrorCode, "CSocketMaster.Accept", GetErrorDescription(lngErrorCode)
  1663.     
  1664. Else
  1665.  
  1666.     m_lngLocalPortBind = IntegerToUnsigned(api_ntohs(udtSockAddr.sin_port))
  1667.     m_strLocalIP = StringFromPointer(api_inet_ntoa(udtSockAddr.sin_addr))
  1668.     
  1669. End If
  1670.  
  1671. GetRemoteInfo m_lngSocketHandle, m_lngRemotePort, m_strRemoteHostIP, m_strRemoteHost
  1672. m_enmState = sckConnected: Debug.Print "STATE: sckConnected"
  1673.  
  1674. If clsSocket.BytesReceived > 0 Then
  1675.     clsSocket.GetData m_strRecvBuffer
  1676. End If
  1677.  
  1678. modSocketMaster.Subclass_ChangeOwner requestID, ObjPtr(Me)
  1679.  
  1680. If Len(m_strRecvBuffer) > 0 Then RaiseEvent DataArrival(Len(m_strRecvBuffer))
  1681.  
  1682. If clsSocket.State = sckClosing Then
  1683.     m_enmState = sckClosing: Debug.Print "STATE: sckClosing"
  1684.     RaiseEvent CloseSck
  1685. End If
  1686.  
  1687. Set clsSocket = Nothing
  1688. End Sub
  1689.  
  1690. 'Retrieves remote info from a connected socket.
  1691. 'If succeeds returns TRUE and loads the arguments.
  1692. 'If fails returns FALSE and arguments are not loaded.
  1693. Private Function GetRemoteInfo(ByVal lngSocket As Long, ByRef lngRemotePort As Long, ByRef strRemoteHostIP As String, ByRef strRemoteHost As String) As Boolean
  1694. GetRemoteInfo = False
  1695. Dim lngResult As Long
  1696. Dim udtSockAddr As sockaddr_in
  1697.  
  1698. lngResult = api_getpeername(lngSocket, udtSockAddr, LenB(udtSockAddr))
  1699.  
  1700. If lngResult = 0 Then
  1701.     GetRemoteInfo = True
  1702.     GetRemoteInfoFromSI udtSockAddr, lngRemotePort, strRemoteHostIP, strRemoteHost
  1703. Else
  1704.    lngRemotePort = 0
  1705.    strRemoteHostIP = ""
  1706.    strRemoteHost = ""
  1707. End If
  1708. End Function
  1709.  
  1710. 'Gets remote info from a sockaddr_in structure.
  1711. Private Sub GetRemoteInfoFromSI(ByRef udtSockAddr As sockaddr_in, ByRef lngRemotePort As Long, ByRef strRemoteHostIP As String, ByRef strRemoteHost As String)
  1712.  
  1713. 'Dim lngResult As Long
  1714. 'Dim udtHostent As HOSTENT
  1715.  
  1716. lngRemotePort = IntegerToUnsigned(api_ntohs(udtSockAddr.sin_port))
  1717. strRemoteHostIP = StringFromPointer(api_inet_ntoa(udtSockAddr.sin_addr))
  1718. 'lngResult = api_gethostbyaddr(udtSockAddr.sin_addr, 4&, AF_INET)
  1719.  
  1720. 'If lngResult <> 0 Then
  1721. '    api_CopyMemory udtHostent, ByVal lngResult, LenB(udtHostent)
  1722. '    strRemoteHost = StringFromPointer(udtHostent.hName)
  1723. 'Else
  1724.     m_strRemoteHost = ""
  1725. 'End If
  1726.  
  1727. End Sub
  1728.  
  1729. 'Returns winsock incoming buffer length from an UDP socket.
  1730. Private Function GetBufferLenUDP() As Long
  1731. Dim lngResult As Long
  1732. Dim lngBuffer As Long
  1733. lngResult = api_ioctlsocket(m_lngSocketHandle, FIONREAD, lngBuffer)
  1734.  
  1735. If lngResult = SOCKET_ERROR Then
  1736.     GetBufferLenUDP = 0
  1737. Else
  1738.     GetBufferLenUDP = lngBuffer
  1739. End If
  1740. End Function
  1741.  
  1742. 'Empty winsock incoming buffer from an UDP socket.
  1743. Private Sub EmptyBuffer()
  1744. Dim B As Byte
  1745. api_recv m_lngSocketHandle, B, Len(B), 0&
  1746. End Sub
  1747.