VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "CSocket"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'********************************************************************************
'CSocket class
'Copyright © 2002 by Oleg Gdalevich
'Visual Basic Internet Programming website (http://www.vbip.com)
'********************************************************************************
'To use this class module you need:
'   MSocketSupport code module
'********************************************************************************
'Version: 1.0.12     Modified: 17-OCT-2002
'********************************************************************************
'To get latest version of this code please visit the following web page:
'http://www.vbip.com/winsock-api/csocket-class/csocket-class-01.asp
'********************************************************************************
Option Explicit
'
'Added: 23-AUG-2002
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'
'The CSocket protocol's constants as for
'the MS Winsock Control interface
Public Enum ProtocolConstants
    sckTCPProtocol = 0
    sckUDPProtocol = 1
End Enum
'
'The CSocket error's constants as for
'the MS Winsock Control interface
Public Enum ErrorConstants
    sckAddressInUse = 10048
    sckAddressNotAvailable = 10049
    sckAlreadyComplete = 10037
    sckAlreadyConnected = 10056
    sckBadState = 40006
    sckConnectAborted = 10053
    sckConnectionRefused = 10061
    sckConnectionReset = 10054
    sckGetNotSupported = 394
    sckHostNotFound = 11001
    sckHostNotFoundTryAgain = 11002
    sckInProgress = 10036
    sckInvalidArg = 40014
    sckInvalidArgument = 10014
    sckInvalidOp = 40020
    sckInvalidPropertyValue = 380
    sckMsgTooBig = 10040
    sckNetReset = 10052
    sckNetworkSubsystemFailed = 10050
    sckNetworkUnreachable = 10051
    sckNoBufferSpace = 10055
    sckNoData = 11004
    sckNonRecoverableError = 11003
    sckNotConnected = 10057
    sckNotInitialized = 10093
    sckNotSocket = 10038
    sckOpCanceled = 10004
    sckOutOfMemory = 7
    sckOutOfRange = 40021
    sckPortNotSupported = 10043
    sckSetNotSupported = 383
    sckSocketShutdown = 10058
    sckSuccess = 40017
    sckTimedout = 10060
    sckUnsupported = 40018
    sckWouldBlock = 10035
    sckWrongProtocol = 40026
End Enum
'
'The CSocket state's constants as for
'the MS Winsock Control interface
Public Enum StateConstants
    sckClosed = 0
    sckOpen
    sckListening
    sckConnectionPending
    sckResolvingHost
    sckHostResolved
    sckConnecting
    sckConnected
    sckClosing
    sckError
End Enum
'
'In order to resolve a host name the MSocketSupport.ResolveHost
'function can be called from the Connect and SendData methods
'of this class. The callback acceptor for that routine is the
'PostGetHostEvent procedure. This procedure determines what to
'do next with the received host's address checking a value of
'the m_varInternalState variable.
Private Enum InternalStateConstants
    istConnecting
    istSendingDatagram
End Enum
'
Private m_varInternalState As InternalStateConstants
'
'Local (module level) variables to hold values of the
'properties of this (CSocket) class.
Private mvarProtocol        As ProtocolConstants
Private mvarState           As StateConstants
Private m_lngBytesReceived  As Long
Private m_strLocalHostName  As String
Private m_strLocalIP        As String
Private m_lngLocalPort      As Long
Private m_strRemoteHost     As String
Private m_strRemoteHostIP   As String
Private m_lngRemotePort     As Long
Private m_lngSocketHandle   As Long
'
'Resolving host names is performed in an asynchronous mode,
'the m_lngRequestID variable just holds the value returned
'by the ResolveHost function from the MSocketSupport module.
Private m_lngRequestID      As Long
'
'Internal (for this class) buffers. They are the VB Strings.
'Don't trust that guy who told that the VB String data type
'cannot properly deal with binary data. Actually, it can, and
'moreover you have a lot of means to deal with that data -
'the VB string functions (such as Left, Mid, InStr and so on).
'If you need to get a byte array from a string, just call the
'StrConv function:
'
'byteArray() = StrConv(strBuffer, vbFromUnicode)
'
Private m_strSendBuffer     As String 'The internal buffer for outgoing data
Private m_strRecvBuffer     As String 'The internal buffer for incoming data
'
'Lenght of the Winsock buffers. By default = 8192 bytes for TCP sockets.
'These values are initialized in the SocketExists function.
'Now, I really don't know why I was in need to get these values.
Private m_lngSendBufferLen  As Long
Private m_lngRecvBufferLen  As Long
'
'Maximum size of a datagram that can be sent through
'a message-oriented (UDP) socket. This value is returned
'by the InitWinsock function from the MSocketSupport module.
Private m_lngMaxMsgSize     As Long
'
'This flag variable indicates that the socket is bound to
'some local socket address
Private m_blnSocketIsBound  As Boolean  'Added: 10-MAR-2002
'
Private m_blnSendFlag As Boolean        'Added: 12-SEP-2002
'
'This flag variable indicates that the SO_BROADCAST option
'is set on the socket
Private m_blnBroadcast      As Boolean  'Added: 09-JULY-2002
'
'These are those MS Winsock's events.
'Pay attention that the "On" prefix is added.
Public Event OnClose()
Attribute OnClose.VB_Description = "Occurs when the connection has been closed"
Public Event OnConnect()
Attribute OnConnect.VB_Description = "Occurs connect operation is completed"
Public Event OnConnectionRequest(ByVal requestID As Long)
Public Event OnDataArrival(ByVal bytesTotal As Long)
Public Event OnError(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)
Public Event OnSendComplete()
Public Event OnSendProgress(ByVal bytesSent As Long, ByVal bytesRemaining As Long)

Public Sub SendData(varData As Variant)
Attribute SendData.VB_Description = "Send data to remote computer"
    '
    'data to send - will be built from the varData argument
    Dim arrData()       As Byte
    'value returned by the send(sendto) Winsock API function
    Dim lngRetValue     As Long
    'length of the data to send - needed to call the send(sendto) Winsock API function
    Dim lngBufferLength As Long
    'this strucure just contains address of the remote socket to send data to;
    'only for UDP sockets when the sendto Winsock API function is used
    Dim udtSockAddr     As sockaddr_in
    '
    On Error GoTo SendData_Err_Handler
    '
    'If a connection-oriented (TCP) socket was not created or connected to the
    'remote host before calling the SendData method, the MS Winsock Control
    'raises the sckBadState error.
    If mvarProtocol = sckTCPProtocol Then
        '
        If m_lngSocketHandle = INVALID_SOCKET Then
            Err.Raise sckBadState, "CSocket.SendData", _
            "Wrong protocol or connection state for the requested transaction or request."
            Exit Sub
        End If
        '
    Else
        '
        'If the socket is a message-oriented one (UDP), this is OK to create
        'it with the call of the SendData method. The SocketExists function
        'creates a new socket.
        If Not SocketExists Then Exit Sub
        '
    End If
    '
    Select Case varType(varData)
        Case vbArray + vbByte
            'Modified 28-MAY-2002. Thanks to Michael Freidgeim
            '--------------------------------
            'Dim strArray As String
            'strArray = CStr(varData)
            arrData() = varData
            '--------------------------------
        Case vbBoolean
            Dim blnData As Boolean
            blnData = CBool(varData)
            ReDim arrData(LenB(blnData) - 1)
            CopyMemory arrData(0), blnData, LenB(blnData)
        Case vbByte
            Dim bytData As Byte
            bytData = CByte(varData)
            ReDim arrData(LenB(bytData) - 1)
            CopyMemory arrData(0), bytData, LenB(bytData)
        Case vbCurrency
            Dim curData As Currency
            curData = CCur(varData)
            ReDim arrData(LenB(curData) - 1)
            CopyMemory arrData(0), curData, LenB(curData)
        Case vbDate
            Dim datData As Date
            datData = CDate(varData)
            ReDim arrData(LenB(datData) - 1)
            CopyMemory arrData(0), datData, LenB(datData)
        Case vbDouble
            Dim dblData As Double
            dblData = CDbl(varData)
            ReDim arrData(LenB(dblData) - 1)
            CopyMemory arrData(0), dblData, LenB(dblData)
        Case vbInteger
            Dim intData As Integer
            intData = CInt(varData)
            ReDim arrData(LenB(intData) - 1)
            CopyMemory arrData(0), intData, LenB(intData)
        Case vbLong
            Dim lngData As Long
            lngData = CLng(varData)
            ReDim arrData(LenB(lngData) - 1)
            CopyMemory arrData(0), lngData, LenB(lngData)
        Case vbSingle
            Dim sngData As Single
            sngData = CSng(varData)
            ReDim arrData(LenB(sngData) - 1)
            CopyMemory arrData(0), sngData, LenB(sngData)
        Case vbString
            Dim strData As String
            strData = CStr(varData)
            ReDim arrData(Len(strData) - 1)
            arrData() = StrConv(strData, vbFromUnicode)
        Case Else
            '
            'Unknown data type
            '
    End Select
    '
    'Store all the data to send in the module level
    'variable m_strSendBuffer.
    m_strSendBuffer = StrConv(arrData(), vbUnicode)
    '
    'Call the SendBufferedData subroutine in order to send the data.
    'The SendBufferedData sub is just a common procedure that is
    'called from different places in this class.
    'Nothing special - just the code reuse.
    m_blnSendFlag = True
    Call SendBufferedData
    '
EXIT_LABEL:
    '
    Exit Sub
    '
SendData_Err_Handler:
    '
    If Err.LastDllError = WSAENOTSOCK Then
        Err.Raise sckBadState, "CSocket.SendData", "Wrong protocol or connection state for the requested transaction or request."
    Else
        Err.Raise Err.Number, "CSocket.SendData", Err.Description
    End If
    '
    GoTo EXIT_LABEL
    '
End Sub


Public Sub PeekData(varData As Variant, Optional varType As Variant, Optional maxLen As Variant)
Attribute PeekData.VB_Description = "Look at incoming data without removing it from the buffer"
    '
    Dim lngBytesReceived As Long    'value returned by the RecvData function
    '
    On Error GoTo PeekData_Err_Handler
    '
    'The RecvData is a universal subroutine that can either to retrieve or peek
    'data from the Winsock buffer. If a value of the second argument (blnPeek As Boolean)
    'of the RecvData subroutine is True, it will be just peeking.
    lngBytesReceived = RecvData(varData, True, IIf(IsMissing(varType), Empty, varType), _
                                IIf(IsMissing(maxLen), Empty, maxLen))
    '
EXIT_LABEL:
    '
    Exit Sub
    '
PeekData_Err_Handler:
    '
    Err.Raise Err.Number, "CSocket.PeekData", Err.Description
    '
    GoTo EXIT_LABEL
    '
End Sub


Public Sub Listen()
Attribute Listen.VB_Description = "Listen for incoming connection requests"
    '
    Dim lngRetValue As Long 'value returned by the listen Winsock API function
    '
    On Error GoTo Listen_Err_Handler
    '
    'SocketExists is not a variable. It is a function that can
    'create a socket, if the class has no one.
    If Not SocketExists Then Exit Sub
    '
    'The listen Winsock API function cannot be called
    'without the call of the bind one.
    If Not m_blnSocketIsBound Then  'Added: 10-MAR-2002
        Call Bind
    End If                          'Added: 10-MAR-2002
    '
    'Turn the socket into a listening state
    lngRetValue = api_listen(m_lngSocketHandle, 5&)
    '
    If lngRetValue = SOCKET_ERROR Then
        mvarState = sckError
        'Debug.Print "mvarState = sckError"
        Err.Raise Err.LastDllError, "CSocket.Listen", GetErrorDescription(Err.LastDllError)
    Else
        mvarState = sckListening
        'Debug.Print "Listen: mvarState = sckListening"
    End If
    '
EXIT_LABEL:
    '
    Exit Sub
    '
Listen_Err_Handler:
    '
    Err.Raise Err.Number, "CSocket.Listen", Err.Description
    '
    GoTo EXIT_LABEL
    '
End Sub


Public Sub GetData(varData As Variant, Optional varType As Variant, Optional maxLen As Variant)
Attribute GetData.VB_Description = "Retrieve data sent by the remote computer"
    '
    Dim lngBytesReceived As Long    'value returned by the RecvData function
    '
    On Error GoTo GetData_Err_Handler
    '
    'A value of the second argument of the RecvData subroutine is False, so in this way
    'this procedure will retrieve incoming data from the buffer.
    lngBytesReceived = RecvData(varData, False, IIf(IsMissing(varType), Empty, varType), _
                                IIf(IsMissing(maxLen), Empty, maxLen))
    '
EXIT_LABEL:
    '
    Exit Sub
    '
GetData_Err_Handler:
    '
    Err.Raise Err.Number, "CSocket.GetData", Err.Description
    '
    GoTo EXIT_LABEL
    '
End Sub


Public Sub Connect(Optional strRemoteHost As Variant, Optional lngRemotePort As Variant)
Attribute Connect.VB_Description = "Connect to the remote computer"
    '
    Dim lngHostAddress  As Long         '32 bit host address
    Dim udtAddress      As sockaddr_in  'socket address - used by the connect Winsock API function
    Dim lngRetValue     As Long         'value returned by the connect Winsock API function
    '
    On Error GoTo Connect_Err_Handler
    '
    'If no socket has been created before, try to create a new one
    If Not SocketExists Then Exit Sub
    '
    'If the arguments of this function are not missing, they
    'overwrite values of the RemoteHost and RemotePort properties.
    '
    If Not IsMissing(strRemoteHost) Then    'Added: 04-MAR-2002
        If Len(strRemoteHost) > 0 Then
            m_strRemoteHost = CStr(strRemoteHost)
        End If
    End If                                  'Added: 04-MAR-2002
    '
    If Not IsMissing(lngRemotePort) Then    'Added: 04-MAR-2002
        If IsNumeric(lngRemotePort) Then    'Added: 04-MAR-2002
            m_lngRemotePort = CLng(lngRemotePort)
        End If                              'Added: 04-MAR-2002
    End If                                  'Added: 04-MAR-2002
    '
    '----------------------------------------------------------
    'Added: 31-JUL-2002
    '----------------------------------------------------------
    If Len(m_strRemoteHost) = 0 Then
        Err.Raise sckAddressNotAvailable, "CSocket.Connect", GetErrorDescription(sckAddressNotAvailable)
        Exit Sub
    End If
    '----------------------------------------------------------
    '
    m_varInternalState = istConnecting
    '
    '------------------------------------------------------------------
    'Modified: 08-JULY-2002
    '------------------------------------------------------------------
    'Here is a major change. Since version 1.0.6 (08-JULY-2002) the
    'SCocket class doesn't try to resolve the IP address into a
    'domain name while connecting.
    '------------------------------------------------------------------
    '
    'Try to get 32 bit host address from the RemoteHost property value
    lngHostAddress = inet_addr(m_strRemoteHost)
    '
    If lngHostAddress = INADDR_NONE Then
        '
        'The RemoteHost property doesn't contain a valid IP address string,
        'so that is perhaps a domain name string that we need to resolve
        'into IP address
        '
        'The ResolveHost function, that can be found in the MSocketSupport
        'module, will call the WSAAsyncGetHostByName Winsock API function.
        'That function is an asynchronous one, so code in this class will be executing
        'after the call to the PostGetHostEvent procedure from the WindowProc function
        'in the MSupportSocket.
        '
        'Also, as you can see, the second argument is a pointer to the object, that is
        'this instance of the CSocket class. We need this because the callback function
        'has to know to which object send the received host infromation. See the code
        'in the MSocketSupport module for more information.
        '
        'Change the State property value
        mvarState = sckResolvingHost
        'Debug.Print "mvarState = sckResolvingHost"
        '
        m_lngRequestID = 0
        m_lngRequestID = MSocketSupport.ResolveHost(m_strRemoteHost, ObjPtr(Me))
        '
        '-------------------------------------------------------
        'Added: 04-JUNE-2002
        '-------------------------------------------------------
        If m_lngRequestID = 0 Then
            Call DestroySocket
            Err.Raise Err.Number, Err.Source, Err.Description
        End If
        '-------------------------------------------------------
        '
    Else
        '
        'The RemoteHost property contains a valid IP address string,
        'so we can go on connecting to the remote host.
        '
        'Build the sockaddr_in structure to pass it to the connect
        'Winsock API function as an address of the remote host.
        With udtAddress
            '
            .sin_addr = lngHostAddress
            .sin_family = AF_INET
            .sin_port = htons(UnsignedToInteger(CLng(m_lngRemotePort)))
            '
        End With
        '
        'Call the connect Winsock API function in order to establish connection.
        lngRetValue = api_connect(m_lngSocketHandle, udtAddress, Len(udtAddress))
        '
        'Since the socket we use is a non-blocking one, the connect Winsock API
        'function should return a value of SOCKET_ERROR anyway.
        '
        If lngRetValue = SOCKET_ERROR Then
            '
            'The WSAEWOULDBLOCK error is OK for such a socket
            '
            If Not Err.LastDllError = WSAEWOULDBLOCK Then
                Err.Raise Err.LastDllError, "CSocket.Connect", GetErrorDescription(Err.LastDllError)
            Else
                'Change the State property value
                mvarState = sckConnecting
                'Debug.Print "mvarState = sckConnecting"
            End If
            '
        End If
        '
    End If
    '
EXIT_LABEL:
    '
    Exit Sub
    '
Connect_Err_Handler:
    '
    Err.Raise Err.Number, "CSocket.CSocket.Connect", Err.Description
    '
    GoTo EXIT_LABEL
    '
End Sub

Public Sub CloseSocket()
Attribute CloseSocket.VB_Description = "Close current connection"
    '
    Dim lngRetValue As Long 'value returned by the shutdown Winsock API function
    '
    On Error GoTo Close_Err_Handler
    '
    'Why do we need to run the code that should not be running?
    If m_lngSocketHandle = INVALID_SOCKET Then Exit Sub
    '
    If Not mvarState = sckConnected Then
        '
        'If the socket is not connected we can just close it
        Call DestroySocket
        mvarState = sckClosed
        'Debug.Print "mvarState = sckClosed"
        '
    Else
        '
        'If the socket is connected, it's another story.
        'In order to be sure that no data will be lost the
        'graceful shutdown of the socket should be performed.
        '
        mvarState = sckClosing
        'Debug.Print "mvarState = sckClosing"
        '
        'Call the shutdown Winsock API function in order to
        'close the connection. That doesn't mean that the
        'connection will be closed after the call of the
        'shutdown function. Connection will be closed from
        'the PostSocketEvent subroutine when the FD_CLOSE
        'message will be received.
        '
        'For people who know what the FIN segment in the
        'TCP header is - this function sends an empty packet
        'with the FIN bit turned on.
        '
        lngRetValue = shutdown(m_lngSocketHandle, SD_SEND)
        '
        'Debug.Print m_lngSocketHandle & ": shutdown"
        '
        If lngRetValue = SOCKET_ERROR Then
            Err.Raise Err.LastDllError, "CSocket.CloseSocket", GetErrorDescription(Err.LastDllError)
        End If
        '
    End If

EXIT_LABEL:
    '
    Exit Sub
    '
Close_Err_Handler:
    '
    If Err.Number <> 10038 Then
        'Err.Raise Err.Number, "CSocket.Close", Err.Description
    End If
    '
    GoTo EXIT_LABEL
    '
End Sub

Public Sub Bind(Optional lngLocalPort As Long, Optional strLocalIP As String)
Attribute Bind.VB_Description = "Binds socket to specific port and adapter"
    '
    Dim lngRetValue     As Long         'value returned by the bind Winsock API function
    Dim udtLocalAddr    As sockaddr_in  'local socket address to bind to - used by the
    '                                    bind Winsock API function
    Dim lngAddress      As Long         '32-bit host address - value returned by
    '                                    the inet_addr Winsock API function
    '
    On Error GoTo Bind_Err_Handler
    '
    'If no socket has been created before, try to create a new one
    If Not SocketExists Then Exit Sub
    '
    'If the arguments of this function are not missing, they
    'overwrites values of the RemoteHost and RemotePort properties.
    '
    If Len(strLocalIP) > 0 Then
        m_strLocalIP = strLocalIP
    End If
    '
    If lngLocalPort > 0 Then
        m_lngLocalPort = lngLocalPort
    End If
    '
    If Len(m_strLocalIP) > 0 Then
        '
        'If the local IP is known, get the address
        'from it with the inet_addr Winsock API function.
        lngAddress = inet_addr(m_strLocalIP)
        '
    Else
        '
        'If the IP is unknown, assign the default interface's IP.
        'Actually, this line is useless in Visual Basic code,
        'as INADDR_ANY = 0 (IP = 0.0.0.0).
        lngAddress = INADDR_ANY
        '
    End If
    '
    If lngAddress = SOCKET_ERROR Then
        '
        'Bad address - go away
        Err.Raise Err.LastDllError, "CSocket.Bind", GetErrorDescription(Err.LastDllError)
        Exit Sub
        '
    End If
    '
    'Prepare the udtLocalAddr UDT that is a socket address structure.
    With udtLocalAddr
        '
        'host address (32-bits value)
        .sin_addr = lngAddress
        'address family
        .sin_family = AF_INET
        'port number in the network byte order
        .sin_port = htons(UnsignedToInteger(m_lngLocalPort))    'Modified: 04-JUNE-2002
        '
    End With
    '
    'Call the bind Winsock API function in order to assign local address for the socket
    lngRetValue = api_bind(m_lngSocketHandle, udtLocalAddr, Len(udtLocalAddr))
    '
    If lngRetValue = SOCKET_ERROR Then
        '
        Err.Raise Err.LastDllError, "CSocket.Bind", GetErrorDescription(Err.LastDllError)
        '
    Else
        '
        m_blnSocketIsBound = True   'Added: 10-MAR-2002
        '
    End If
    '
EXIT_LABEL:
    '
    Exit Sub
    '
Bind_Err_Handler:
    '
    Err.Raise Err.Number, "CSocket.Bind", Err.Description
    '
    GoTo EXIT_LABEL
    '
End Sub


Public Sub Accept(requestID As Long)
Attribute Accept.VB_Description = "Accept an incoming connection request"
    '
    'The requestID argument is provided with the ConnectRequest
    'event of another instance of the CSocket class. Actually,
    'this argument is a handle of the socket already created
    'calling the Accept Winsock API function by that (another)
    'instance of the CSocket class.
    '
    Dim lngRetValue As Long         'value returned by the getsockname, getpeername, and
    '                                getsockopt Winsock API functions
    Dim lngBuffer   As Long         'the buffer to pass with the getsockopt Winsock API function
    Dim udtSockAddr As sockaddr_in  'socket address - used by the getsockname and getpeername
    '                                Winsock API functions
    Dim udtHostent  As HOSTENT      'structure to hold the host info - returned by the
    '                                getsockname and getpeername Winsock API functions
    '
    On Error GoTo Accept_Err_Handler
    '
    'What we need to do in the body of this subroutine is to
    'initialize the properties of the class that we can find
    'values for. Also we need to register the socket with
    'the RegisterSocket function from MSocketSupport module.
    '
    'Assign the socket handle
    m_lngSocketHandle = requestID
    '
    'Retrieve the connection end-points to initialize
    'the following properties of the CSocket class:
    'LocalPort, LocalIP, LocalHostName
    'RemotePort, RemoteHostIP, RemoteHost
    '
    'Local end point
    '
    lngRetValue = getsockname(m_lngSocketHandle, udtSockAddr, Len(udtSockAddr))
    '
    If lngRetValue = 0 Then
        '
        'LocalPort property
        m_lngLocalPort = IntegerToUnsigned(ntohs(udtSockAddr.sin_port))
        'LocalIP property
        m_strLocalIP = StringFromPointer(inet_ntoa(udtSockAddr.sin_addr))
        'LocalHostName property
        '----------------------------------------------------------------
        'Modified: 31-JUL-2002
        '----------------------------------------------------------------
        'lngRetValue = gethostbyaddr(udtSockAddr.sin_addr, 4&, AF_INET)
        'CopyMemory udtHostent, ByVal lngRetValue, Len(udtHostent)
        'm_strLocalHostName = StringFromPointer(udtHostent.hName)
        m_strLocalHostName = m_strLocalIP
        '----------------------------------------------------------------
        '
    End If
    '
    'Remote end point
    '
    lngRetValue = getpeername(m_lngSocketHandle, udtSockAddr, Len(udtSockAddr))
    '
    If lngRetValue = 0 Then
        '
        'RemotePort property
        m_lngRemotePort = IntegerToUnsigned(ntohs(udtSockAddr.sin_port))
        'RemoteHostIP property
        m_strRemoteHostIP = StringFromPointer(inet_ntoa(udtSockAddr.sin_addr))
        'RemoteHost property
        '----------------------------------------------------------------
        'Modified: 31-JUL-2002
        '----------------------------------------------------------------
        'lngRetValue = gethostbyaddr(udtSockAddr.sin_addr, 4&, AF_INET)
        'CopyMemory udtHostent, ByVal lngRetValue, Len(udtHostent)
        'm_strRemoteHost = StringFromPointer(udtHostent.hName)
        m_strRemoteHost = m_strRemoteHostIP
        '----------------------------------------------------------------
        '
    End If
    '
    'Retrieve the socket type to initialize the Protocol property
    lngRetValue = getsockopt(m_lngSocketHandle, SOL_SOCKET, SO_TYPE, lngBuffer, LenB(lngBuffer))
    '
    If lngRetValue <> SOCKET_ERROR Then
        '
        If lngBuffer = SOCK_STREAM Then
            mvarProtocol = sckTCPProtocol
        Else
            mvarProtocol = sckUDPProtocol
        End If
        '
    End If
    '
    'Get default size of the Winsock's buffers.
    Call GetWinsockBuffers  'Added: 10-MAR-2002
    '
    If MSocketSupport.RegisterSocket(m_lngSocketHandle, ObjPtr(Me)) Then
        '
        'Change the State property value
        mvarState = sckConnected
        'Debug.Print "Accept: mvarState = sckConnected"
        '
    End If
    '
EXIT_LABEL:
    '
    Exit Sub
    '
Accept_Err_Handler:
    '
    Err.Raise Err.Number, "CSocket.Accept", Err.Description
    '
    GoTo EXIT_LABEL
    '
End Sub

Public Property Get State() As StateConstants
    State = mvarState
End Property

Public Property Get SocketHandle() As Long
Attribute SocketHandle.VB_Description = " Returns the socket handle"
    SocketHandle = m_lngSocketHandle
End Property

Public Property Get RemotePort() As Long
Attribute RemotePort.VB_Description = "Returns/Sets the port to be connected to on the remote computer"
    RemotePort = m_lngRemotePort
End Property

Public Property Let RemotePort(NewValue As Long)
    m_lngRemotePort = NewValue
End Property

Public Property Get RemoteHostIP() As String
Attribute RemoteHostIP.VB_Description = "Returns the remote host IP address"
    RemoteHostIP = m_strRemoteHostIP
End Property

Public Property Get RemoteHost() As String
Attribute RemoteHost.VB_Description = "Returns/Sets the name used to identify the remote computer"
    RemoteHost = m_strRemoteHost
End Property

Public Property Let RemoteHost(NewValue As String)
    '
    Dim lngHostAddress As Long '32 bit host address
    Dim lngRetValue    As Long 'value returned by the setsockopt function
    '
    m_strRemoteHost = NewValue
    '
    If Len(NewValue) > 0 Then
        '
        'Check for a valid IP address string
        '
        lngHostAddress = inet_addr(NewValue)
        '
        If Not lngHostAddress = INADDR_NONE Then
            '
            m_strRemoteHostIP = NewValue
            '
            If Not mvarProtocol = sckUDPProtocol Then Exit Property
            If Not SocketExists Then Exit Property
            '
            'If the IP address is a brodcasting one set the option
            '
            If Right(NewValue, 4) = ".255" And m_blnBroadcast = False Then
                '
                lngRetValue = setsockopt(m_lngSocketHandle, SOL_SOCKET, SO_BROADCAST, 1&, 4&)
                '
                If lngRetValue = SOCKET_ERROR Then
                    '
                    With Err
                        .Raise .LastDllError, "CSocket.RemoteHost", GetErrorDescription(.LastDllError)
                    End With
                    '
                Else
                    '
                    m_blnBroadcast = True
                    '
                End If
                '
            ElseIf (Not (Right(NewValue, 4) = ".255")) And (m_blnBroadcast = True) Then
                '
                lngRetValue = setsockopt(m_lngSocketHandle, SOL_SOCKET, SO_BROADCAST, 0&, 4&)
                '
                If lngRetValue = SOCKET_ERROR Then
                    '
                    With Err
                        .Raise .LastDllError, "CSocket.RemoteHost", GetErrorDescription(.LastDllError)
                    End With
                    '
                Else
                    '
                    m_blnBroadcast = False
                    '
                End If
                '
            End If
            '
        End If
        '
    End If
    '
End Property

Public Property Get Protocol() As ProtocolConstants
Attribute Protocol.VB_Description = "Returns/Sets the socket protocol"
    Protocol = mvarProtocol
End Property

Public Property Let Protocol(NewValue As ProtocolConstants)
    '
    If m_lngSocketHandle = INVALID_SOCKET Then  'Modified: 10-MAR-2002
        mvarProtocol = NewValue
    End If
    '
End Property

Public Property Get LocalPort() As Long
Attribute LocalPort.VB_Description = "Returns/Sets the port used on the local computer"
    LocalPort = m_lngLocalPort
End Property

Public Property Let LocalPort(NewValue As Long)
    m_lngLocalPort = NewValue
End Property

Public Property Get LocalIP() As String
Attribute LocalIP.VB_Description = "Returns the local machine IP address"
    LocalIP = m_strLocalIP
End Property

Public Property Get LocalHostName() As String
Attribute LocalHostName.VB_Description = "Returns the local machine name"
    LocalHostName = m_strLocalHostName
End Property

Public Property Get BytesReceived() As Long
Attribute BytesReceived.VB_Description = "Returns the number of bytes received on this connection"
    BytesReceived = m_lngBytesReceived
End Property

Private Sub Class_Initialize()
    '
    'Socket's handle default value
    m_lngSocketHandle = INVALID_SOCKET
    'Initialize the Winsock service
    m_lngMaxMsgSize = MSocketSupport.InitWinsockService
    '
End Sub

Public Function vbSocket() As Long
'********************************************************************************
'Author    :Oleg Gdalevich
'Purpose   :Creates a new socket
'Returns   :The socket handle if successful, otherwise - INVALID_SOCKET
'Arguments :
'********************************************************************************
    '
    On Error GoTo vbSocket_Err_Handler
    '
    Dim lngRetValue     As Long 'value returned by the socket API function
    '
    'Call the socket Winsock API function in order to create a new socket
    If mvarProtocol = sckUDPProtocol Then
        lngRetValue = api_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
    Else
        lngRetValue = api_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
    End If
    '
    If lngRetValue = INVALID_SOCKET Then
        '
        Err.Raise Err.LastDllError, "CSocket.vbSocket", GetErrorDescription(Err.LastDllError)
        '
    Else
        '
        'Debug.Print lngRetValue & ": socket created"
        '
        If Not MSocketSupport.RegisterSocket(lngRetValue, ObjPtr(Me)) Then  'Modified: 04-JUNE-2002
            '--------------------------------------------------
            'Added: 04-JUNE-2002
            '--------------------------------------------------
            lngRetValue = INVALID_SOCKET
            Call api_closesocket(lngRetValue)
            Err.Raise Err.Number, Err.Source, Err.Description
            '--------------------------------------------------
            '
        End If
        '
    End If
    '
    'Assign returned value
    vbSocket = lngRetValue
    '
EXIT_LABEL:
    Exit Function

vbSocket_Err_Handler:
    '
    vbSocket = INVALID_SOCKET
    '
End Function

Friend Sub PostSocketEvent(ByVal lngEventID As Long, Optional ByVal lngError As Long)
    '
    'This procedure is called by the WindowProc callback function
    'from the MSocketSupport module. The lngEventID argument is an
    'ID of the network event occurred for the socket. The lngError
    'argument contains an error code only if an error was occurred
    'during an asynchronous execution.
    '
    Dim lngBytesReceived    As Long         'value returned by the RecvDataToBuffer function
    Dim lngRetValue         As Long         'value returned by the getsockname Winsock API function
    Dim lngNewSocket        As Long         'value returned by the accept Winsock API function
    Dim udtSockAddr         As sockaddr_in  'remote socket address for the accept Winscok API function
    Dim udtHostent          As HOSTENT      'structure to hold the host info - returned
    '                                        by the gethostbyaddr Winsock API function
    '
    On Error GoTo ERROR_HANDLER
    '
    If lngError > 0 Then
        '
        'An error was occurred.
        '
        'Change a value of the State property
        mvarState = sckError
        'Debug.Print "mvarState = sckError"
        'Close the socket
        Call DestroySocket
        'The OnError event is just for this case
        RaiseEvent OnError(CInt(lngError), GetErrorDescription(lngError), 0, "", "", 0, False)
        'We have nothing to do here anymore
        Exit Sub
        '
    End If
    '
    Select Case lngEventID
        '
        Case FD_READ
            '
            'Debug.Print "FD_READ"
            '
            'Some data has arrived for this socket.
            'Call the RecvDataToBuffer function that move arrived data
            'from the Winsock buffer to the local one and returns number
            'of bytes received.
            lngBytesReceived = RecvDataToBuffer
            '
            'Debug.Print "Bytes received: " & lngBytesReceived
            '
            'The BytesReceived property contains number of bytes in
            'the local buffer of the class.
            m_lngBytesReceived = m_lngBytesReceived + lngBytesReceived
            '
            'The OnDataArrival event is just for the case when some data
            'was retieved from the Winsock buffer.
            If lngBytesReceived > 0 Then
                RaiseEvent OnDataArrival(Len(m_strRecvBuffer))
            End If
            '
        Case FD_WRITE
            '
            'This message means that the socket in a write-able
            'state, that is, buffer for outgoing data of the transport
            'service is empty and ready to receive data to send through
            'the network.
            '
            'Debug.Print "FD_WRITE"
            '
            'If the local buffer for outgoing data (m_strSendBuffer) is
            'not empty, the previous call of the send/sendto Winsock API
            'function was failed. Call the SendBufferedData procedure in
            'oreder to try to send that data again.
            If Len(m_strSendBuffer) > 0 Then
                '
                Call SendBufferedData
            Else
                '
                If m_blnSendFlag Then           'Added: 12-SEP-2002
                    m_blnSendFlag = False       'Added: 12-SEP-2002
                    RaiseEvent OnSendComplete   'Added: 23-AUG-2002
                End If
                '
            End If
            '
        Case FD_OOB
            '
            'Ignored.
            '
        Case FD_ACCEPT
            '
            'When the socket is in a listening state, arrival of this message
            'means that a connection request was received. Call the accept
            'Winsock API function in oreder to create a new socket for the
            'requested connection.
            lngNewSocket = api_accept(m_lngSocketHandle, udtSockAddr, Len(udtSockAddr))
            '
            'Debug.Print lngNewSocket & ": created"
            '
            'Let the client application know that the request was received
            'and pass with the event argument a handle of the recently created
            'socket. The client application should create a new instance of
            'the CSocket class, and then use the socket handle (lngNewSocket)
            'to initialize its properties. Another way is to do not create
            'one more instance of this class. We may close existing socket,
            'and then accept the new handle:
            '
            '  Private Sub objSocket_OnConnectionRequest(ByVal requestID As Long)
            '      If objSocket.State <> sckClosed Then objSocket.CloseSocket
            '      objSocket.Accept (requestID)
            '  End Sub
            '
            RaiseEvent OnConnectionRequest(lngNewSocket)
            '
        Case FD_CONNECT
            '
            'Arrival of this message means that the connection initiated by the call
            'of the connect Winsock API function was successfully established.
            '
            'Get the connection local end-point parameters
            '
            lngRetValue = getsockname(m_lngSocketHandle, udtSockAddr, LenB(udtSockAddr))
            '
            If lngRetValue = 0 Then
                '
                'LocalPort property
                m_lngLocalPort = IntegerToUnsigned(ntohs(udtSockAddr.sin_port))
                'LocalIP property
                m_strLocalIP = StringFromPointer(inet_ntoa(udtSockAddr.sin_addr))
                'LocalHostName property
                lngRetValue = gethostbyaddr(udtSockAddr.sin_addr, 4&, AF_INET)
                '---------------------------------------------------------------
                'Modified: 31-JUL-2002
                '---------------------------------------------------------------
                If lngRetValue <> 0 Then
                    CopyMemory udtHostent, ByVal lngRetValue, Len(udtHostent)
                    m_strLocalHostName = StringFromPointer(udtHostent.hName)
                Else
                    m_strLocalHostName = m_strLocalIP
                End If
                '---------------------------------------------------------------
                '
            End If
            '
            ' -- Modified: 04-MAR-2002 --
            '
            'Change a value of the State property
            mvarState = sckConnected
            '
            'Let the client app know that the connection was established.
            RaiseEvent OnConnect
            '
            ' -- --------------------- --
            '
            'Debug.Print "mvarState = sckConnected"
            '
        Case FD_CLOSE
            '
            'This message means that the remote host is closing the conection
            '
            '-------------------------------------------------------------------
            'Modified: 20-AUG-2002
            'Thanks to mreggio and other vbip.com Forum members.
            '-------------------------------------------------------------------
            Do
                '
                lngBytesReceived = RecvDataToBuffer
                '
                If lngBytesReceived > 0 Then
                    RaiseEvent OnDataArrival(Len(m_strRecvBuffer))
                End If
                '
            Loop Until lngBytesReceived = 0 Or lngBytesReceived = SOCKET_ERROR
            '
            If mvarState = sckClosing Then
                '
                '---------------
                'Don't even read this as it doesn't work on a very-very
                'fast connection, for example: localhost<->localhost :).
                'The Do..Loop was moved up, now you can see it above the
                '"If mvarState = sckClosing Then" statement
                '-----------------
                '
                'If a value of the State property already is sckClosing,
                'the closing of the connection was initiated by the local
                'end-point (this socket) of the connection. In other words,
                'the shutdown Winsock API function has been called before
                '(the FIN segment is already sent by the local end-point).
                '
                'In this case we need wait until all the data sent by the
                'remote end-point of the connection will be received.
                '
                'Do
                '    '
                '    lngBytesReceived = RecvDataToBuffer
                '    '
                '    If lngBytesReceived > 0 Then
                '        RaiseEvent OnDataArrival(Len(m_strRecvBuffer))
                '    End If
                '    '
                'Loop Until lngBytesReceived = 0 Or lngBytesReceived = SOCKET_ERROR
                '
            '-------------------------------------------------------------------
            Else
                '
                mvarState = sckClosing
                'Debug.Print "mvarState = sckClosing"
                '
                'If a value of the State property is not sckClosing, the
                'connectoin is closing by the remote end-point of the
                'connection (the FIN segment is sent by the remote host).
                'In this case we need send all the remained data from the
                'local buffer before to close the socket.
                If Len(m_strSendBuffer) > 0 Then
                    '
                    Call SendBufferedData
                    '
                End If
                '
            End If
            '
            'Close the socket
            Call DestroySocket
            '
            'Change a value of the State property
            mvarState = sckClosed
            'Debug.Print "mvarState = sckClosed"
            '
            'Let the client app know that the connection is closed
            RaiseEvent OnClose
            '
    End Select
    '
    Exit Sub
    '
ERROR_HANDLER:
    '
    Err.Raise Err.Number, "CSocket.PostSocketEvent", Err.Description    'Modified: 15-APR-2002
    '
End Sub

Friend Sub PostGetHostEvent(ByVal lngRequestID As Long, ByVal lngHostAddress As Long, strHostName As String, Optional lngError As Long)
    '
    'This procedure is called by the WindowProc callback function
    'from the MSocketSupport module. Think about it as about result
    'returned by the ResolveHost function called from this class.
    '
    Dim udtAddress      As sockaddr_in  'socket address - used by the connect Winsock API function
    Dim lngRetValue     As Long         'value returned by the connect Winsock API function
    Dim lngPtrToAddress As Long         'pointer to the string that contains IP address - value
                                        'returned by the inet_ntoa Winsock API function
    '
    On Error GoTo ERROR_HANDLER
    '
    If lngError > 0 Then
        '
        'An error was occerred during resolving the host hame.
        'For example: "Host not found"
        '
        '----------------------------------------------------------------
        'Added: 28-APR-2002
        'There is the case when a computer has a valid IP address
        'but its name cannot be resolved. In this case the code should
        'countinue the execution - we just don't need to change the
        'RemoteHost property value.
        '----------------------------------------------------------------
        '
        'Does the strHostName argument contain a valid IP address?
        lngHostAddress = inet_addr(strHostName)
        '
        If lngHostAddress = INADDR_NONE Then    'Added: 28-APR-2002
            '
            'Change a value of the State property
            mvarState = sckError
            'Debug.Print "mvarState = sckError"
            '
            'Let the client app know that an error was occurred.
            RaiseEvent OnError(CInt(lngError), GetErrorDescription(lngError), 0, "", "", 0, False)
            '
            Exit Sub
            '
        Else    'Added: 28-APR-2002
            '
            'Nothing to do here
            'Both properties the RemoteHost and RemoteHostIP
            'have the same value of the IP address string.
            '
        End If  'Added: 28-APR-2002
        '
    End If
    '
    'Check the id value - Do we really need this?
    If lngRequestID = 0 Then Exit Sub
    '
    If lngRequestID = m_lngRequestID Then
        '
        'Change a value of the State property
        mvarState = sckHostResolved
        'Debug.Print "mvarState = sckHostResolved"
        '
        'Initialize the RemoteHost property
        m_strRemoteHost = strHostName
        '
        'Get pointer to the string that contains the IP address
        lngPtrToAddress = inet_ntoa(lngHostAddress)
        '
        'Retrieve that string by the pointer and init the
        'RemoteHostIP property.
        m_strRemoteHostIP = StringFromPointer(lngPtrToAddress)
        '
        'The ResolveHost function may be called from two methods
        'of the class: Connect and SendData. The m_varInternalState
        'variable tells us where the ResolveHost function called
        'from, and thus what to do here.
        '
        If m_varInternalState = istConnecting Then
            '
            'The ResolveHost was called from the Connect method, so
            'we need to continue the process of the connection establishing.
            '
            'Build the sockaddr_in structure to pass it to the connect
            'Winsock API function as an address of the remote host.
            With udtAddress
                '
                .sin_addr = lngHostAddress
                .sin_family = AF_INET
                .sin_port = htons(UnsignedToInteger(CLng(m_lngRemotePort)))
                '
            End With
            '
            'Call the connect Winsock API function in order to establish connection.
            lngRetValue = api_connect(m_lngSocketHandle, udtAddress, Len(udtAddress))
            '
            'Since the socket we use is a non-blocking one, the connect Winsock API
            'function should return a value of SOCKET_ERROR anyway.
            '
            If lngRetValue = SOCKET_ERROR Then
                '
                'The WSAEWOULDBLOCK error is OK for such a socket
                '
                If Not Err.LastDllError = WSAEWOULDBLOCK Then
                    'Modified: 31-JUL-2002
                    RaiseEvent OnError(Err.LastDllError, GetErrorDescription(Err.LastDllError), 0&, "CSocket.PostGetHostEvent", "", 0&, False)
                Else
                    'Change the State property value
                    mvarState = sckConnecting
                    'Debug.Print "mvarState = sckConnecting"
                End If
                '
            End If
            '
        ElseIf m_varInternalState = istSendingDatagram Then
            '
            'The ResolveHost was called from the SendData method in
            'the case when a message-oriented (UDP) socket is used.
            '
            Call SendBufferedData
            '
        End If
        '
    End If
    '
    Exit Sub
    '
ERROR_HANDLER:
    '
    Err.Raise Err.Number, "CSocket.PostGetHostEvent", Err.Description
    '
End Sub

Private Function SocketExists() As Boolean
    '
    If m_lngSocketHandle = INVALID_SOCKET Then
        '
        'If the m_lngSocketHandle is not a valid value, call
        'the vbSocket function in order to create a new socket
        m_lngSocketHandle = vbSocket
        '
        If m_lngSocketHandle = SOCKET_ERROR Then
            '
            'A value of SOCKET_ERROR means that the socket was not created.
            'In this case the SocketExists function must return False
            Exit Function
            '
        Else
            '
            'Get default size of the Winsock's buffers.
            Call GetWinsockBuffers  'Modified: 10-MAR-2002
            '
        End If
        '
    End If
    '
    'The m_lngSocketHandle variable contains a valid socket
    'handle value. In this case the function returns True.
    SocketExists = True
    '
End Function

Private Sub GetWinsockBuffers()
    '
    'This subroutine is to retrieve default size of the Winsock buffers.
    'These values will be stored in the module level variables:
    'm_lngSendBufferLen and m_lngRecvBufferLen.
    'It can be called from the SocketExists and Accept functions.
    '
    'Added: 10-MAR-2002
    '
    Dim lngRetValue     As Long 'value returned by the getsockopt Winsock API function
    Dim lngBuffer       As Long 'buffer to pass with the getsockopt call
    '
    If mvarProtocol = sckTCPProtocol Then
        'Buffer for incoming data
        lngRetValue = getsockopt(m_lngSocketHandle, SOL_SOCKET, SO_RCVBUF, lngBuffer, 4&)
        m_lngRecvBufferLen = lngBuffer
        'Buffer for outgoing data
        lngRetValue = getsockopt(m_lngSocketHandle, SOL_SOCKET, SO_SNDBUF, lngBuffer, 4&)
        m_lngSendBufferLen = lngBuffer
    Else
        'the m_lngMaxMsgSize value is returned by InitWinsockService
        'function from the MSocketSupport module
        m_lngSendBufferLen = m_lngMaxMsgSize
        m_lngRecvBufferLen = m_lngMaxMsgSize
    End If
    '
End Sub

Private Function RecvDataToBuffer() As Long
    '
    'This function is to retrieve data from the Winsock buffer
    'into the class local buffer. The function returns number
    'of bytes retrieved (received).
    '
    Dim lngBytesReceived        As Long     'value returned by recv/recvfrom Winsock API function
    Dim lngRetValue             As Long     'value returned by gethostbyaddr Winsock API function
    Dim strTempBuffer           As String   'just a temporary buffer
    Dim arrBuffer()             As Byte     'buffer to pass to the recv/recvfrom Winsock API function
    Dim udtSockAddr             As sockaddr_in 'socket address of the remote peer
    Dim lngSockAddrLen          As Long     'size of the sockaddr_in structure
    Dim udtHostent              As HOSTENT  'used to get host name with gethostbyaddr function
    '
    'Prepare the buffer to pass it to the recv/recvfrom Winsock API function.
    'The m_lngRecvBufferLen variable was initialized during creating
    'of the socket, see the vbSocket function to find out how.
    ReDim arrBuffer(m_lngRecvBufferLen - 1)
    '
    If mvarProtocol = sckTCPProtocol Then
        '
        'If the socket is a connection-oriented one, just call the recv function
        'to retrieve all the arrived data from the Winsock buffer.
        lngBytesReceived = recv(m_lngSocketHandle, arrBuffer(0), m_lngRecvBufferLen, 0&)
        '
    Else
        '
        'If the socket uses UDP, it's another story. As stated in the MS Winsock Control
        'documentation after receiving data the RemoteHost, RemoteHostIP, and RemotePort
        'properties contains parameters of the machine sending the UDP data. To achive
        'such a behavior we must use the recvfrom Winsock API function.
        '
        lngSockAddrLen = Len(udtSockAddr)
        '
        lngBytesReceived = recvfrom(m_lngSocketHandle, arrBuffer(0), m_lngRecvBufferLen, _
                                    0&, udtSockAddr, lngSockAddrLen)
        '
        If Not lngBytesReceived = SOCKET_ERROR Then
            '
            'Now the udtSockAddr contains a socket address of the remote host.
            'Initialize the RemoteHost, RemoteHostIP, and RemotePort properties.
            '
            With udtSockAddr
                '
                'RemotePort property
                m_lngRemotePort = IntegerToUnsigned(ntohs(.sin_port))
                'RemoteHostIP property
                m_strRemoteHostIP = StringFromPointer(inet_ntoa(.sin_addr))
                'RemoteHost property
                lngRetValue = gethostbyaddr(.sin_addr, 4&, AF_INET)
                CopyMemory udtHostent, ByVal lngRetValue, Len(udtHostent)
                m_strRemoteHost = StringFromPointer(udtHostent.hName)
                '
            End With
            '
        End If
        '
    End If
    '
    If lngBytesReceived > 0 Then
        '
        'Convert a byte array into the VB string
        strTempBuffer = StrConv(arrBuffer(), vbUnicode)
        'Store received data in the local buffer for incoming data - m_strRecvBuffer
        m_strRecvBuffer = m_strRecvBuffer & Left$(strTempBuffer, lngBytesReceived)
        'Return number of received bytes.
        RecvDataToBuffer = lngBytesReceived
        '
    ElseIf lngBytesReceived = SOCKET_ERROR Then
        '
        Err.Raise Err.LastDllError, "CSocket.RecvToBuffer", GetErrorDescription(Err.LastDllError)
        '
    End If
    '
End Function

Private Function RecvData(varData As Variant, blnPeek As Boolean, Optional varType As Variant, Optional maxLen As Variant) As Long
    '
    'This function is to retrieve data from the local buffer (m_strRecvBuffer).
    'It can be called by two public methods of the class - GetData and PeekData.
    'Behavior of the function is defined by the blnPeek argument. If a value of
    'that argument is True, the function returns number of bytes in the
    'local buffer, and copy data from that buffer into the varData argument.
    'If a value of the blnPeek is False, then this function returns number of
    'bytes received, and move data from the local buffer into the varData
    'argument. MOVE means that data will be removed from the local buffer.
    '
    Dim strRecvData As String   'temporary string buffer
    Dim arrBuffer() As Byte     'temporary byte array buffer
    '
    'If the local buffer is empty, go away - we have nothing to do here.
    If Len(m_strRecvBuffer) = 0 Then Exit Function
    '
    If IsEmpty(maxLen) Then
        maxLen = 0
    End If
    '
    If (Not maxLen > Len(m_strRecvBuffer)) And (maxLen > 0) Then
        '
        strRecvData = Left$(m_strRecvBuffer, CLng(maxLen))
        '
        If Not blnPeek Then
            m_strRecvBuffer = Mid$(m_strRecvBuffer, CLng(maxLen + 1))
        End If
        '
        arrBuffer() = StrConv(strRecvData, vbFromUnicode)
        '
    Else
        '
        arrBuffer() = StrConv(m_strRecvBuffer, vbFromUnicode)
        '
        If Not blnPeek Then
            m_strRecvBuffer = ""
        End If
        '
    End If
    '
    If IsEmpty(varType) Then
        varData = CStr(StrConv(arrBuffer(), vbUnicode))
    Else
        '
        Select Case varType
            Case vbArray + vbByte
                'Modified 28-MAY-2002. Thanks to Michael Freidgeim
                '--------------------------------
                'Dim strArray As String
                'strArray = StrConv(arrBuffer(), vbUnicode)
                'varData = StrConv(strArray, vbFromUnicode)
                varData = arrBuffer()
                '--------------------------------
            Case vbBoolean
                Dim blnData As Boolean
                CopyMemory blnData, arrBuffer(0), LenB(blnData)
                varData = blnData
            Case vbByte
                Dim bytData As Byte
                CopyMemory bytData, arrBuffer(0), LenB(bytData)
                varData = bytData
            Case vbCurrency
                Dim curData As Currency
                CopyMemory curData, arrBuffer(0), LenB(curData)
                varData = curData
            Case vbDate
                Dim datData As Date
                CopyMemory datData, arrBuffer(0), LenB(datData)
                varData = datData
            Case vbDouble
                Dim dblData As Double
                CopyMemory dblData, arrBuffer(0), LenB(dblData)
                varData = dblData
            Case vbInteger
                Dim intData As Integer
                CopyMemory intData, arrBuffer(0), LenB(intData)
                varData = intData
            Case vbLong
                Dim lngData As Long
                CopyMemory lngData, arrBuffer(0), LenB(lngData)
                varData = lngData
            Case vbSingle
                Dim sngData As Single
                CopyMemory sngData, arrBuffer(0), LenB(sngData)
                varData = sngData
            Case vbString
                Dim strData As String
                strData = StrConv(arrBuffer(), vbUnicode)
                varData = strData
                '
        End Select
        '
    End If
    '
    'Added 28-MAY-2002. Thanks to Michael Freidgeim
    m_lngBytesReceived = Len(m_strRecvBuffer) 'reset BytesReceived after Getdata
    '
End Function

Private Sub DestroySocket()
    '
    'The purpose of this subroutine is to unregister the socket with
    'UnregisterSocket that can be found in the MSocketSupport module
    'and close the socket with the closesocket Winsock API function.
    '
    Dim lngRetValue As Long 'value returned by the closesocket
                            'Winsock AP function
    '
    m_strRecvBuffer = "" 'Added: 17-OCT-2002
    '
    If Not m_lngSocketHandle = INVALID_SOCKET Then
        '
        'Unregister the socket. For more info on how it works
        'see the code of the function in the MSocketSupport module
        Call MSocketSupport.UnregisterSocket(m_lngSocketHandle)
        '
        'Close the socket with the closesocket Winsock API function.
        lngRetValue = api_closesocket(m_lngSocketHandle)
        '
        'Debug.Print m_lngSocketHandle & ": closed"
        '
        If lngRetValue = SOCKET_ERROR Then
            Err.Raise Err.LastDllError, "CSocket.DestroySocket", GetErrorDescription(Err.LastDllError)
        End If
        '
        'Change the SocketHandle property value
        m_lngSocketHandle = INVALID_SOCKET
        '
        'If the bind Winsock API function has been called on
        'this socket, m_blnSocketIsBound = True. We need to
        'change this value.
        m_blnSocketIsBound = False  'Added: 10-MAR-2002
        '
        m_blnBroadcast = False      'Added: 09-JULY-2002
        '
    End If
    '
End Sub

Private Sub Class_Terminate()
    '
    If Not m_lngSocketHandle = INVALID_SOCKET Then
        Call DestroySocket
    End If
    '
    Call CleanupWinsock
    '
End Sub

Private Sub SendBufferedData()
    '
    'This procedure sends data from the local buffer (m_strSendBuffer).
    'The data from the client application is passed with the SendData
    'method of the class as an argument and is stored in the local
    'buffer until all the data from that buffer will be sent using this
    'subroutine.
    '
    'Why do we need to store data in the local buffer? There are some
    'things happenning in the Winsock's buffer for outgoing data since
    'we're using non-blocking sockets' calls. If that buffer is full,
    'the transport subsystem doesn't take the data and the send/sendto
    'functions return a value of SOCKET_ERROR, Err.LastDllError give
    'us a value of WSAEWOULDBLOCK. This means that if the socket would
    'be a blocking one, such a call would block socket until the buffer
    'will be freed and ready to accept some data to send.
    '
    'So this procedure can be called several (mostly not more than two)
    'times for the same chunk of data. First call is in the body of the
    'SendData method, and other calls (if necessary) will be performed
    'from the PostSocketEvent subroutine, as soon as the FD_WRITE message
    'will be received. The arrival of the FD_WRITE message means that a
    'socket is in a write-able state - its buffer is ready to get data.
    '
    Dim lngRetValue     As Long         'value returned by send/sendto Winsock API function
    Dim arrData()       As Byte         'data to send with the send/sendto function
    Dim lngBufferLength As Long         'size of the data buffer to send
    Dim udtSockAddr     As sockaddr_in  'address of the remote socket - for the sendto function
    '
    'The send/sendto function needs this value for one of its arguments
    lngBufferLength = Len(m_strSendBuffer)
    m_blnSendFlag = True
    '
    'Convert data from a VB string to a byte array
    arrData() = StrConv(m_strSendBuffer, vbFromUnicode)
    '
    If mvarProtocol = sckTCPProtocol Then
        '
        'just call the send function in order to send data via connection
        lngRetValue = send(m_lngSocketHandle, arrData(0), lngBufferLength, 0&)
        '
    Else
        '
        'With UDP socket we are going to use the sendto Winsock API function.
        'This function needs the socket address of the remote host to send
        'message to.
        '
        If Len(m_strRemoteHostIP) = 0 Then
            '
            'If the RemoteHostIP property is empty, we don't know
            'the remote IP so we need to resolve that address.
            '
            m_varInternalState = istSendingDatagram
            m_lngRequestID = MSocketSupport.ResolveHost(m_strRemoteHost, ObjPtr(Me))
            '
            'The ResolveHost is an asynchronous call. This subroutine wiil be called
            'one more time from the PostGetHostEvent procedure when the host will be
            'resolved.
            '
        Else
            '
            'If we are here the host was resolved successfully and the RemoteHostIP
            'property provides us with IP to send a UDP message to.
            '
            'Build the sockaddr_in structure to pass the remote socket address
            'to the sendto function.
            With udtSockAddr
                .sin_addr = inet_addr(m_strRemoteHostIP)
                .sin_port = htons(UnsignedToInteger(m_lngRemotePort))
                .sin_family = AF_INET
            End With
            '
            'Call the sendto function in order to send a UDP message
            lngRetValue = sendto(m_lngSocketHandle, arrData(0), lngBufferLength, 0&, udtSockAddr, Len(udtSockAddr))
            '
        End If
        '
    End If
    '
    If lngRetValue = SOCKET_ERROR Then
        '
        'If a value of Err.LastDllError is WSAEWOULDBLOCK, that means
        'that the Winsock's buffer for outgoing data is full and cannot
        'accept data to send. In this case we ignore this error and do
        'not empty local buffer (m_strSendBuffer).
        '
        If Not Err.LastDllError = WSAEWOULDBLOCK Then
            'Debug.Print "Error occurred: " & Err.LastDllError & " - " & GetErrorDescription(Err.LastDllError)
            Err.Raise Err.LastDllError, "CSocket.SendData", GetErrorDescription(Err.LastDllError)
        Else
            'Debug.Print "WSAEWOULDBLOCK"
        End If
        '
    Else
        '
        'The data were sent successfully. Raise the OnSendProgress or
        'OnSendComplete event to let the client app know.
        '
        'Debug.Print "SendData - Bytes sent: " & lngRetValue
        '
        If Len(m_strSendBuffer) > lngRetValue Then
            '
            m_strSendBuffer = Mid$(m_strSendBuffer, lngRetValue + 1)
            '
        Else
            '
            m_strSendBuffer = ""
            '
            '---------------------------------------------
            'Modified: 23-AUG-2002
            '---------------------------------------------
            'RaiseEvent OnSendComplete
            Call PostMessage(p_lngWindowHandle, p_lngWinsockMessage, m_lngSocketHandle, FD_WRITE)
            '---------------------------------------------
        End If
        '
        RaiseEvent OnSendProgress(lngRetValue, Len(m_strSendBuffer))
        '
    End If
    '
End Sub
