- Contents in this wiki are for entertainment purposes only
Robust Winsock VB6 Class
Jump to navigation
Jump to search
Option Explicit ' RobustWinsock Class ' A robust VB6 winsock implementation designed to handle large data transfers, ' connection failures, and error recovery. ' ' Features: ' - Automatic reconnection on failure ' - Chunked data transfer for large files ' - Timeout handling ' - CRC checking for data integrity ' - Session management ' - Event-based architecture ' - Comprehensive error handling ' - Buffer management for large transfers ' Constants for socket states Private Const SOCKET_CLOSED = 0 Private Const SOCKET_OPEN = 1 Private Const SOCKET_LISTENING = 2 Private Const SOCKET_CONNECTION_PENDING = 3 Private Const SOCKET_RESOLVING_HOST = 4 Private Const SOCKET_HOST_RESOLVED = 5 Private Const SOCKET_CONNECTING = 6 Private Const SOCKET_CONNECTED = 7 Private Const SOCKET_CLOSING = 8 Private Const SOCKET_ERROR = 9 ' Constants for CRC calculation Private Const CRC32_POLYNOMIAL = &HEDB88320 ' Constants for error handling Private Const ERR_WINSOCK_BASE = 10000 Private Const ERR_CONNECTION_TIMEOUT = ERR_WINSOCK_BASE + 1 Private Const ERR_CONNECTION_RESET = ERR_WINSOCK_BASE + 2 Private Const ERR_DATA_INTEGRITY = ERR_WINSOCK_BASE + 3 Private Const ERR_BUFFER_OVERFLOW = ERR_WINSOCK_BASE + 4 ' Constants for data transfer Private Const DEFAULT_BUFFER_SIZE = 8192 ' 8KB default buffer Private Const MAX_BUFFER_SIZE = 1048576 ' 1MB max buffer Private Const DEFAULT_TIMEOUT = 30000 ' 30 seconds Private Const DEFAULT_RETRY_COUNT = 3 ' Default number of retries Private Const DEFAULT_HEARTBEAT_INTERVAL = 5000 ' 5 seconds ' Member variables Private m_Winsock As Winsock ' The actual Winsock control Private m_BufferSize As Long ' Size of the buffer Private m_SendBuffer() As Byte ' Buffer for sending data Private m_ReceiveBuffer() As Byte ' Buffer for receiving data Private m_SendBufferLength As Long ' Current length of send buffer Private m_ReceiveBufferLength As Long ' Current length of receive buffer Private m_Timeout As Long ' Timeout in milliseconds Private m_RetryCount As Integer ' Number of retries Private m_CurrentRetry As Integer ' Current retry count Private m_RemoteHost As String ' Remote host name or IP Private m_RemotePort As Long ' Remote port Private m_LocalPort As Long ' Local port Private m_Connected As Boolean ' Connection state Private m_LastError As Long ' Last error code Private m_LastErrorMessage As String ' Last error message Private m_AutoReconnect As Boolean ' Auto reconnect flag Private m_HeartbeatTimer As Timer ' Timer for heartbeats Private m_HeartbeatInterval As Long ' Heartbeat interval Private m_TransferInProgress As Boolean ' Flag for transfer in progress Private m_TotalBytesSent As Long ' Total bytes sent in current transfer Private m_TotalBytesReceived As Long ' Total bytes received in current transfer Private m_ConnectionTimer As Timer ' Timer for connection timeout Private m_SessionID As String ' Unique session identifier Private m_CRCTable(0 To 255) As Long ' Table for CRC calculation Private m_TransferID As Long ' Current transfer ID Private m_ChunkSize As Long ' Size of data chunks Private m_PacketSequence As Long ' Packet sequence number ' Event declarations Public Event Connected() Public Event Disconnected() Public Event DataArrival(ByVal bytesTotal As Long) Public Event SendComplete() Public Event Error(ByVal Number As Long, ByVal Description As String) Public Event ConnectionRequest(ByVal requestID As Long) Public Event ProgressUpdate(ByVal bytesSent As Long, ByVal bytesTotal As Long) Public Event TransferComplete(ByVal transferID As Long, ByVal success As Boolean) Public Event Timeout() Public Event Reconnecting(ByVal attemptNumber As Integer) ' Class initialization Private Sub Class_Initialize() ' Initialize default values m_BufferSize = DEFAULT_BUFFER_SIZE m_Timeout = DEFAULT_TIMEOUT m_RetryCount = DEFAULT_RETRY_COUNT m_CurrentRetry = 0 m_Connected = False m_AutoReconnect = True m_HeartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL m_TransferInProgress = False m_TotalBytesSent = 0 m_TotalBytesReceived = 0 m_ChunkSize = DEFAULT_BUFFER_SIZE m_PacketSequence = 0 ' Initialize buffers ReDim m_SendBuffer(0 To m_BufferSize - 1) ReDim m_ReceiveBuffer(0 To m_BufferSize - 1) ' Create a unique session ID m_SessionID = GenerateSessionID() ' Initialize CRC table InitializeCRCTable ' Create the Winsock control Set m_Winsock = New Winsock ' Set up timers Set m_HeartbeatTimer = New Timer m_HeartbeatTimer.Interval = m_HeartbeatInterval m_HeartbeatTimer.Enabled = False Set m_ConnectionTimer = New Timer m_ConnectionTimer.Interval = m_Timeout m_ConnectionTimer.Enabled = False End Sub ' Class termination Private Sub Class_Terminate() ' Clean up If m_Connected Then DisconnectSocket End If ' Stop timers m_HeartbeatTimer.Enabled = False m_ConnectionTimer.Enabled = False ' Clean up objects Set m_Winsock = Nothing Set m_HeartbeatTimer = Nothing Set m_ConnectionTimer = Nothing End Sub ' Initialize the CRC table for data integrity checks Private Sub InitializeCRCTable() Dim i As Long, j As Long, crc As Long For i = 0 To 255 crc = i For j = 0 To 7 If (crc And 1) Then crc = (crc \ 2) Xor CRC32_POLYNOMIAL Else crc = crc \ 2 End If Next j m_CRCTable(i) = crc Next i End Sub ' Calculate CRC32 for data integrity Private Function CalculateCRC32(data() As Byte, ByVal length As Long) As Long Dim crc As Long, i As Long crc = &HFFFFFFFF For i = 0 To length - 1 crc = ((crc \ 256) And &HFFFFFF) Xor m_CRCTable((crc And &HFF) Xor data(i)) Next i CalculateCRC32 = Not crc End Function ' Generate a unique session ID Private Function GenerateSessionID() As String Dim guid As String guid = Format$(Now, "yyyymmddhhnnss") & Format$(Timer, "000000") & Format$(Rnd() * 1000000, "000000") GenerateSessionID = guid End Function ' Connect to a remote host Public Function Connect(ByVal remoteHost As String, ByVal remotePort As Long) As Boolean On Error GoTo ErrorHandler ' Store connection parameters m_RemoteHost = remoteHost m_RemotePort = remotePort ' Reset counters m_CurrentRetry = 0 m_TotalBytesSent = 0 m_TotalBytesReceived = 0 ' Attempt to connect AttemptConnection ' Start connection timeout timer m_ConnectionTimer.Enabled = True Connect = True Exit Function ErrorHandler: m_LastError = Err.Number m_LastErrorMessage = Err.Description RaiseEvent Error(m_LastError, m_LastErrorMessage) Connect = False End Function ' Internal connection attempt Private Sub AttemptConnection() On Error GoTo ErrorHandler ' Close any existing connection If m_Connected Then DisconnectSocket End If ' Attempt to connect m_Winsock.Connect m_RemoteHost, m_RemotePort Exit Sub ErrorHandler: m_LastError = Err.Number m_LastErrorMessage = Err.Description ' Handle connection failure with retry If m_AutoReconnect And m_CurrentRetry < m_RetryCount Then m_CurrentRetry = m_CurrentRetry + 1 RaiseEvent Reconnecting(m_CurrentRetry) ' Wait a bit before retrying (exponential backoff) Dim waitTime As Long waitTime = 1000 * (2 ^ m_CurrentRetry) ' Use a timer to retry Dim retryTimer As Timer Set retryTimer = New Timer retryTimer.Interval = waitTime retryTimer.Enabled = True ' Retry after wait AttemptConnection Else ' Failed after all retries RaiseEvent Error(m_LastError, m_LastErrorMessage) End If End Sub ' Listen for incoming connections Public Function Listen(ByVal localPort As Long) As Boolean On Error GoTo ErrorHandler ' Store local port m_LocalPort = localPort ' Close any existing connection If m_Connected Then DisconnectSocket End If ' Start listening m_Winsock.LocalPort = localPort m_Winsock.Listen Listen = True Exit Function ErrorHandler: m_LastError = Err.Number m_LastErrorMessage = Err.Description RaiseEvent Error(m_LastError, m_LastErrorMessage) Listen = False End Function ' Accept an incoming connection Public Function Accept(ByVal requestID As Long) As Boolean On Error GoTo ErrorHandler ' Accept the connection m_Winsock.Accept requestID m_Connected = True ' Start heartbeat timer m_HeartbeatTimer.Enabled = True ' Stop connection timeout timer m_ConnectionTimer.Enabled = False ' Raise connected event RaiseEvent Connected Accept = True Exit Function ErrorHandler: m_LastError = Err.Number m_LastErrorMessage = Err.Description RaiseEvent Error(m_LastError, m_LastErrorMessage) Accept = False End Function ' Disconnect from the remote host Public Sub Disconnect() On Error Resume Next DisconnectSocket ' Raise disconnected event RaiseEvent Disconnected End Sub ' Internal disconnect method Private Sub DisconnectSocket() On Error Resume Next ' Close the socket If m_Winsock.State <> SOCKET_CLOSED Then m_Winsock.Close End If ' Reset connection state m_Connected = False ' Stop timers m_HeartbeatTimer.Enabled = False m_ConnectionTimer.Enabled = False ' Reset buffers m_SendBufferLength = 0 m_ReceiveBufferLength = 0 m_TransferInProgress = False End Sub ' Send data over the connection Public Function SendData(data() As Byte, ByVal length As Long) As Boolean On Error GoTo ErrorHandler ' Check if connected If Not m_Connected Then m_LastError = ERR_WINSOCK_BASE m_LastErrorMessage = "Not connected" RaiseEvent Error(m_LastError, m_LastErrorMessage) SendData = False Exit Function End If ' Initialize transfer m_TransferInProgress = True m_TotalBytesSent = 0 m_TransferID = CLng(Timer * 1000) ' Create a unique transfer ID ' Send data in chunks for large transfers Dim remainingBytes As Long, currentChunk As Long, i As Long Dim sendBuffer() As Byte, headerBuffer(0 To 15) As Byte Dim crc As Long remainingBytes = length i = 0 Do While remainingBytes > 0 ' Determine chunk size currentChunk = IIf(remainingBytes > m_ChunkSize, m_ChunkSize, remainingBytes) ' Prepare chunk buffer ReDim sendBuffer(0 To currentChunk - 1) ' Copy data to chunk buffer CopyMemory sendBuffer(0), data(i), currentChunk ' Calculate CRC for this chunk crc = CalculateCRC32(sendBuffer, currentChunk) ' Create packet header ' Format: [SessionID(8)][TransferID(4)][SequenceNumber(2)][Length(2)][CRC(4)] m_PacketSequence = m_PacketSequence + 1 ' Prepare packet header ReDim headerBuffer(0 To 19) CopyMemory headerBuffer(0), CLng(m_TransferID), 4 CopyMemory headerBuffer(4), CInt(m_PacketSequence), 2 CopyMemory headerBuffer(6), CInt(currentChunk), 2 CopyMemory headerBuffer(8), CLng(crc), 4 ' Send header m_Winsock.SendData headerBuffer ' Small delay to ensure header is sent Sleep 10 ' Send data chunk m_Winsock.SendData sendBuffer ' Update counters m_TotalBytesSent = m_TotalBytesSent + currentChunk remainingBytes = remainingBytes - currentChunk i = i + currentChunk ' Raise progress event RaiseEvent ProgressUpdate(m_TotalBytesSent, length) ' Small delay between chunks to prevent buffer overflow If remainingBytes > 0 Then Sleep 50 Loop SendData = True Exit Function ErrorHandler: m_LastError = Err.Number m_LastErrorMessage = Err.Description RaiseEvent Error(m_LastError, m_LastErrorMessage) m_TransferInProgress = False SendData = False End Function ' Send a string over the connection Public Function SendString(ByVal strData As String) As Boolean On Error GoTo ErrorHandler ' Convert string to byte array Dim dataBytes() As Byte Dim length As Long length = Len(strData) ReDim dataBytes(0 To length - 1) ' Copy string to byte array CopyMemory dataBytes(0), ByVal strData, length ' Send the data SendString = SendData(dataBytes, length) Exit Function ErrorHandler: m_LastError = Err.Number m_LastErrorMessage = Err.Description RaiseEvent Error(m_LastError, m_LastErrorMessage) SendString = False End Function ' Send a file over the connection Public Function SendFile(ByVal filePath As String) As Boolean On Error GoTo ErrorHandler ' Check if file exists If Not FileExists(filePath) Then m_LastError = ERR_WINSOCK_BASE m_LastErrorMessage = "File not found: " & filePath RaiseEvent Error(m_LastError, m_LastErrorMessage) SendFile = False Exit Function End If ' Open the file Dim fileNum As Integer Dim fileLength As Long Dim buffer() As Byte Dim bytesRead As Long fileNum = FreeFile ' Get file size fileLength = FileLen(filePath) ' Prepare buffer for file data ReDim buffer(0 To fileLength - 1) ' Read file into buffer Open filePath For Binary Access Read As #fileNum Get #fileNum, , buffer Close #fileNum ' Send the file data SendFile = SendData(buffer, fileLength) Exit Function ErrorHandler: ' Close file if open If fileNum > 0 Then Close #fileNum End If m_LastError = Err.Number m_LastErrorMessage = Err.Description RaiseEvent Error(m_LastError, m_LastErrorMessage) SendFile = False End Function ' Check if a file exists Private Function FileExists(ByVal filePath As String) As Boolean On Error Resume Next FileExists = (Dir(filePath) <> "") End Function ' Get data from the receive buffer Public Function GetData(ByRef data() As Byte, ByVal maxLength As Long) As Long On Error GoTo ErrorHandler ' Check if there's data in the buffer If m_ReceiveBufferLength = 0 Then GetData = 0 Exit Function End If ' Determine how much data to copy Dim copyLength As Long copyLength = IIf(m_ReceiveBufferLength > maxLength, maxLength, m_ReceiveBufferLength) ' Resize the output buffer ReDim data(0 To copyLength - 1) ' Copy data from receive buffer CopyMemory data(0), m_ReceiveBuffer(0), copyLength ' Shift remaining data in buffer If copyLength < m_ReceiveBufferLength Then CopyMemory m_ReceiveBuffer(0), m_ReceiveBuffer(copyLength), m_ReceiveBufferLength - copyLength End If ' Update buffer length m_ReceiveBufferLength = m_ReceiveBufferLength - copyLength GetData = copyLength Exit Function ErrorHandler: m_LastError = Err.Number m_LastErrorMessage = Err.Description RaiseEvent Error(m_LastError, m_LastErrorMessage) GetData = 0 End Function ' Get all available data as a string Public Function GetString() As String On Error GoTo ErrorHandler ' Check if there's data in the buffer If m_ReceiveBufferLength = 0 Then GetString = "" Exit Function End If ' Convert buffer to string Dim strData As String strData = Space(m_ReceiveBufferLength) ' Copy data to string CopyMemory ByVal strData, m_ReceiveBuffer(0), m_ReceiveBufferLength ' Reset buffer m_ReceiveBufferLength = 0 GetString = strData Exit Function ErrorHandler: m_LastError = Err.Number m_LastErrorMessage = Err.Description RaiseEvent Error(m_LastError, m_LastErrorMessage) GetString = "" End Function ' Save received data to a file Public Function SaveToFile(ByVal filePath As String) As Boolean On Error GoTo ErrorHandler ' Check if there's data in the buffer If m_ReceiveBufferLength = 0 Then SaveToFile = False Exit Function End If ' Open the file Dim fileNum As Integer fileNum = FreeFile ' Write buffer to file Open filePath For Binary Access Write As #fileNum Put #fileNum, , m_ReceiveBuffer Close #fileNum ' Reset buffer m_ReceiveBufferLength = 0 SaveToFile = True Exit Function ErrorHandler: ' Close file if open If fileNum > 0 Then Close #fileNum End If m_LastError = Err.Number m_LastErrorMessage = Err.Description RaiseEvent Error(m_LastError, m_LastErrorMessage) SaveToFile = False End Function ' Winsock event handlers Private Sub m_Winsock_Connect() ' Connection established m_Connected = True ' Reset retry counter m_CurrentRetry = 0 ' Stop connection timeout timer m_ConnectionTimer.Enabled = False ' Start heartbeat timer m_HeartbeatTimer.Enabled = True ' Raise connected event RaiseEvent Connected End Sub Private Sub m_Winsock_Close() ' Connection closed m_Connected = False ' Stop timers m_HeartbeatTimer.Enabled = False m_ConnectionTimer.Enabled = False ' Reset transfer state m_TransferInProgress = False ' Raise disconnected event RaiseEvent Disconnected ' Auto reconnect if enabled If m_AutoReconnect Then AttemptConnection End If End Sub Private Sub m_Winsock_DataArrival(ByVal bytesTotal As Long) On Error GoTo ErrorHandler ' Resize buffer if needed If m_ReceiveBufferLength + bytesTotal > UBound(m_ReceiveBuffer) + 1 Then ' Buffer overflow, resize ReDim Preserve m_ReceiveBuffer(0 To m_ReceiveBufferLength + bytesTotal - 1) End If ' Get data from winsock Dim tempBuffer() As Byte ReDim tempBuffer(0 To bytesTotal - 1) m_Winsock.GetData tempBuffer ' Process the packet ' Check for header If bytesTotal >= 12 Then ' Extract header information Dim transferID As Long, sequence As Integer, length As Integer, crc As Long CopyMemory transferID, tempBuffer(0), 4 CopyMemory sequence, tempBuffer(4), 2 CopyMemory length, tempBuffer(6), 2 CopyMemory crc, tempBuffer(8), 4 ' Verify this is a data packet with a valid header If bytesTotal >= 12 + length Then ' Calculate CRC of the data Dim dataCRC As Long ' Create temp buffer for CRC calculation Dim crcBuffer() As Byte ReDim crcBuffer(0 To length - 1) ' Copy data portion CopyMemory crcBuffer(0), tempBuffer(12), length ' Calculate CRC dataCRC = CalculateCRC32(crcBuffer, length) ' Verify CRC If dataCRC = crc Then ' Valid packet, copy to receive buffer CopyMemory m_ReceiveBuffer(m_ReceiveBufferLength), crcBuffer(0), length m_ReceiveBufferLength = m_ReceiveBufferLength + length ' Update total received m_TotalBytesReceived = m_TotalBytesReceived + length ' Raise data arrival event RaiseEvent DataArrival(length) Else ' CRC mismatch, data corruption m_LastError = ERR_DATA_INTEGRITY m_LastErrorMessage = "Data integrity check failed" RaiseEvent Error(m_LastError, m_LastErrorMessage) End If Else ' Not a valid packet, just append to buffer CopyMemory m_ReceiveBuffer(m_ReceiveBufferLength), tempBuffer(0), bytesTotal m_ReceiveBufferLength = m_ReceiveBufferLength + bytesTotal ' Update total received m_TotalBytesReceived = m_TotalBytesReceived + bytesTotal ' Raise data arrival event RaiseEvent DataArrival(bytesTotal) End If Else ' Not enough data for header, just append to buffer CopyMemory m_ReceiveBuffer(m_ReceiveBufferLength), tempBuffer(0), bytesTotal m_ReceiveBufferLength = m_ReceiveBufferLength + bytesTotal ' Update total received m_TotalBytesReceived = m_TotalBytesReceived + bytesTotal ' Raise data arrival event RaiseEvent DataArrival(bytesTotal) End If Exit Sub ErrorHandler: m_LastError = Err.Number m_LastErrorMessage = Err.Description RaiseEvent Error(m_LastError, m_LastErrorMessage) End Sub Private Sub m_Winsock_Error(ByVal Number As Integer, ByVal Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, ByVal CancelDisplay As Boolean) ' Store error information m_LastError = Number m_LastErrorMessage = Description ' Handle connection reset errors with retry If Number = 10054 Then ' Connection reset by peer m_Connected = False ' Stop timers m_HeartbeatTimer.Enabled = False m_ConnectionTimer.Enabled = False ' Reset transfer state m_TransferInProgress = False ' Auto reconnect if enabled If m_AutoReconnect Then AttemptConnection End If End If ' Raise error event RaiseEvent Error(Number, Description) End Sub Private Sub m_Winsock_ConnectionRequest(ByVal requestID As Long) ' Forward connection request event RaiseEvent ConnectionRequest(requestID) End Sub Private Sub m_Winsock_SendComplete() ' Forward send complete event RaiseEvent SendComplete ' Check if transfer is complete If m_TransferInProgress And m_TotalBytesSent > 0 Then m_TransferInProgress = False RaiseEvent TransferComplete(m_TransferID, True) End If End Sub ' Timer event handlers Private Sub m_HeartbeatTimer_Timer() On Error Resume Next ' Send heartbeat packet to keep connection alive If m_Connected Then ' Create a small heartbeat packet Dim heartbeat(0 To 3) As Byte heartbeat(0) = &HFF heartbeat(1) = &HFF heartbeat(2) = &HFF heartbeat(3) = &HFF ' Send heartbeat m_Winsock.SendData heartbeat End If End Sub Private Sub m_ConnectionTimer_Timer() ' Connection timeout m_ConnectionTimer.Enabled = False ' Check if still not connected If Not m_Connected Then ' Stop connection attempt On Error Resume Next m_Winsock.Close ' Set error m_LastError = ERR_CONNECTION_TIMEOUT m_LastErrorMessage = "Connection timeout" ' Raise timeout event RaiseEvent Timeout ' Try to reconnect if auto reconnect is enabled If m_AutoReconnect And m_CurrentRetry < m_RetryCount Then AttemptConnection Else ' Raise error event RaiseEvent Error(m_LastError, m_LastErrorMessage) End If End If End Sub ' Property procedures Public Property Get BufferSize() As Long BufferSize = m_BufferSize End Property Public Property Let BufferSize(ByVal value As Long) ' Ensure buffer size is within limits If value > 0 And value <= MAX_BUFFER_SIZE Then m_BufferSize = value ' Resize buffers ReDim Preserve m_SendBuffer(0 To m_BufferSize - 1) ReDim Preserve m_ReceiveBuffer(0 To m_BufferSize - 1) End If End Property Public Property Get ChunkSize() As Long ChunkSize = m_ChunkSize End Property Public Property Let ChunkSize(ByVal value As Long) ' Ensure chunk size is reasonable If value > 0 And value <= MAX_BUFFER_SIZE Then m_ChunkSize = value End If End Property Public Property Get Timeout() As Long Timeout = m_Timeout End Property Public Property Let Timeout(ByVal value As Long) If value > 0 Then m_Timeout = value m_ConnectionTimer.Interval = value End If End Property Public Property Get RetryCount() As Integer RetryCount = m_RetryCount End Property Public Property Let RetryCount(ByVal value As Integer) If value >= 0 Then m_RetryCount = value End If End Property Public Property Get AutoReconnect() As Boolean AutoReconnect = m_AutoReconnect End Property Public Property Let AutoReconnect(ByVal value As Boolean) m_AutoReconnect = value End Property Public Property Get HeartbeatInterval() As Long HeartbeatInterval = m_HeartbeatInterval End Property Public Property Let HeartbeatInterval(ByVal value As Long) If value > 0 Then m_HeartbeatInterval = value m_HeartbeatTimer.Interval = value End If End Property Public Property Get SessionID() As String SessionID = m_SessionID End Property Public Property Get RemoteHost() As String RemoteHost = m_RemoteHost End Property Public Property Get RemotePort() As Long RemotePort = m_RemotePort End Property Public Property Get LocalPort() As Long LocalPort = m_LocalPort End Property Public Property Let LocalPort(ByVal value As Long) If value > 0 Then m_LocalPort = value End If End Property Public Property Get IsConnected() As Boolean IsConnected = m_Connected End Property Public Property Get LastError() As Long LastError = m_LastError End Property Public Property Get LastErrorMessage() As String LastErrorMessage = m_LastErrorMessage End Property Public Property Get BytesSent() As Long BytesSent = m_TotalBytesSent End Property Public Property Get BytesReceived() As Long BytesReceived = m_TotalBytesReceived End Property Public Property Get State() As Integer If Not m_Winsock Is Nothing Then State = m_Winsock.State Else State = SOCKET_CLOSED End If End Property ' Utility function to copy memory (needs a declare statement in a module) ' Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) ' Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)