unit KOLSocket;

interface

uses
  KOL, Windows, Messages, Winsock;

const
  WM_SOCKET                 = WM_USER + $7000;
  WM_SOCKETERROR            = WM_USER + $7001;
  WM_SOCKETCLOSE            = WM_USER + $7002;
  WM_SOCKETREAD             = WM_USER + $7003;
  WM_SOCKETCONNECT          = WM_USER + $7004;
  WM_SOCKETACCEPT           = WM_USER + $7005;
  WM_SOCKETWRITE            = WM_USER + $7006;
  WM_SOCKETOOB              = WM_USER + $7007;
  WM_SOCKETLISTEN           = WM_USER + $7008;
  WM_SOCKETLOOKUP           = WM_USER + $7009;

  EVENTS_DOLISTEN           = FD_CLOSE OR FD_ACCEPT;
  EVENTS_DOCONNECT          = FD_CONNECT OR FD_CLOSE OR FD_READ;
  EVENTS_SETSOCKETHANDLE    = FD_READ OR FD_CLOSE OR FD_CONNECT;

  MaxWord                   = 65535;
  MinWord                   = 0;

  c_FIRST                   = 1;

  INVALID_SOCKET            = winsock.INVALID_SOCKET;

type

  TWndMethod = procedure(var Message: TMessage) of object;

  PhWnd =^ThWnd;
  ThWnd = object( TObj )
  protected
    m_hWnd: hWnd;
    destructor Destroy; virtual;
  public
    property Handle: hWnd read m_hWnd;
  end;

  PAsyncSocket =^TAsyncSocket;
  TKOLSocket   = PAsyncSocket;

  TWMSocket = record
    Msg: Word;
    case Integer of
    0: (
      SocketWParam: Word;
      SocketDataSize: LongInt;
      SocketNumber: Longint;
      SocketAddress: PAsyncSocket);
    1: (
      WParamLo: Byte;
      WParamHi: Byte;
      SocketEvent: Word;
      SocketError: Word;
      ResultLo: Word;
      ResultHi: Word);
    2: (
      WParam: Word;
      TaskHandle: Word;
      WordHolder: Word;
      pHostStruct: Pointer);
  end;

  TBArray = array[0..65534] of byte;

  TBufRecord = record
     i: integer;
     p:^TBArray;
  end;

  TSocketMessageEvent = procedure (SocketMessage: TWMSocket) of object;

  TAsyncSocket = object( TObj )
    m_SockAddr:         TSockAddr;
    m_Handle:           TSocket;
    m_hWnd:             PhWnd;
    fConnected:         boolean;
    fDNSResult:         string;
    fDNSHandle:         integer;
    FDnsBuffer:         array [0..MAXGETHOSTSTRUCT] of char;
    FList:              PList;
    FOnError:           TSocketMessageEvent;
    FOnLookup:          TSocketMessageEvent;
    FOnAccept:          TSocketMessageEvent;
    FOnClose:           TSocketMessageEvent;
    FOnConnect:         TSocketMessageEvent;
    FOnRead:            TSocketMessageEvent;
    FOnWrite:           TSocketMessageEvent;
    FOnListen:          TSocketMessageEvent;
    FOnOOB:             TSocketMessageEvent;

  protected
    destructor Destroy; virtual;

  private
    function GetCount: LongInt;
    function GetPortNumber: LongInt;
    function GetIPAddress: String;
    function ErrorTest(Evaluation: LongInt): LongInt;

    procedure AllocateSocket;
    procedure KillWinsockBug;
    procedure SetPortNumber(NewPortNumber: LongInt);
    procedure SetIPAddress(NewIPAddress: String);
    procedure SetSocketHandle(NewSocketHandle: TSocket);
    function  GetConnected: boolean;

    // Message Handlers

    procedure HWndProcedure(var Message: TMessage);

    procedure Message_Error(var Message: TWMSocket);
    procedure Message_Lookup(var Message: TWMSocket);
    procedure Message_Close(var Message: TWMSocket);
    procedure Message_Accept(var Message: TWMSocket);
    procedure Message_Read(var Message: TWMSocket);
    procedure Message_Connect(var Message: TWMSocket);
    procedure Message_Write(var Message: TWMSocket);
    procedure Message_OOB(var Message: TWMSocket);
    procedure Message_Listen(var Message: TWMSocket);
    procedure DoReceive(Buffer: Pointer; var ReceiveLen: LongInt);
    procedure DoFinal(Abort: boolean);

  public
    procedure ProcessMessages;
    function  DoGetHostByAddr(IPAddr: PChar): String;
    function  DoGetHostByName(Name: PChar): String;

    procedure DoLookup(host: string);
    procedure DoClose;
    procedure DoSend(Buffer: Pointer; var SendLen: LongInt);
    procedure DoListen;
    procedure DoConnect;
    procedure DoAccept(var AcceptSocket: PAsyncSocket);

    procedure SendString(fString: String);

    function  ReadData(b: pointer; c: integer): integer;
    function  ReadLine(c: char): string; overload;
    function  ReadLine(c: char; t: integer): string; overload;
    function  ErrToStr(Err: LongInt): String;
    function  LocalIP: String;
    function  LocalPort: integer;

    property  SocketHandle: TSocket read m_Handle write SetSocketHandle;
    property  IPAddress: String read GetIPAddress write SetIPAddress;
    property  PortNumber: LongInt read GetPortNumber write SetPortNumber;
    property  Count: LongInt read GetCount;
    property  Connected: boolean read GetConnected;
    property  DNSResult: string read fDNSResult write fDNSResult;

    property  OnError: TSocketMessageEvent read FOnError write FOnError;
    property  OnLookup: TSocketMessageEvent read FOnLookup write FOnLookup;
    property  OnAccept: TSocketMessageEvent read FOnAccept write FOnAccept;
    property  OnClose: TSocketMessageEvent read FOnClose write FOnClose;
    property  OnConnect: TSocketMessageEvent read FOnConnect write FOnConnect;
    property  OnRead: TSocketMessageEvent read FOnRead write FOnRead;
    property  OnWrite: TSocketMessageEvent read FOnWrite write FOnWrite;
    property  OnOOB: TSocketMessageEvent read FOnOOB write FOnOOB;
    property  OnListen: TSocketMessageEvent read FOnListen write FOnListen;
  end;

  function NewThWnd(WndMethod: TWndMethod): PhWnd;
  function NewAsyncSocket: PAsyncSocket;

var
  InstanceCount: LongInt = 0;

implementation

uses objects;

function NewThWnd;
begin
  New(Result, Create);
  Result.m_hWnd := AllocateHWnd(WndMethod);
end;  // constructor ThWnd.Create(WndMethod: TWndMethod)

destructor ThWnd.Destroy;
begin
  DeallocateHWnd(m_hWnd);
  inherited;
end;

function NewAsyncSocket;
var
  TempWSAData: TWSAData;
begin
  InstanceCount := InstanceCount + 1;
  New(Result, Create);
  if (InstanceCount = c_FIRST) then
    Result.ErrorTest(WSAStartup($101, TempWSAData));
  Result.KillWinsockBug;
  Result.m_Handle := INVALID_SOCKET;
  Result.m_SockAddr.sin_family := AF_INET;
  Result.m_SockAddr.sin_addr.s_addr := INet_Addr('0.0.0.0');
  Result.PortNumber := 0;
  Result.FList := NewList;
  Result.m_hWnd := NewThWnd(Result.HWndProcedure);
end;  // constructor TAsyncSocket.Create

function TAsyncSocket.GetCount;
var i: integer;
    t:^TBufRecord;
begin
   result := 0;
   for i := 0 to FList.Count - 1 do begin
      t := FList.Items[i];
      result := result + t^.i;
   end;
end;

function TAsyncSocket.ReadData;
var n,
    r: integer;
    t:^TBufRecord;
    u:^TBufRecord;
    a:^TBArray;
begin
   if FList.count = 0 then begin
      result := 0;
      exit;
   end;
   n := 0;
   a := b;
   while (n < c) and (n < count) do begin
      r := c - n;
      t := FList.Items[0];
      if r > t^.i then r := t^.i;
      move(t^.p^, a^[n], r);
      n := n + r;
      if r = t^.i then begin
         FreeMem(t^.p, t^.i);
         FreeMem(t, SizeOf(TBufRecord));
         FList.Delete(0);
      end else begin
         GetMem(u, SizeOf(TBufRecord));
         u^.i := t^.i - r;
         GetMem(u^.p, u^.i);
         move(t^.p^[r], u^.p^, u^.i);
         FreeMem(t^.p, t^.i);
         FreeMem(t, SizeOf(TBufRecord));
         FList.Items[0] := u;
      end;
   end;
   result := n;
end;

function TAsyncSocket.ReadLine(c: char): string;
var i,
    n,
    j: integer;
    t:^TBufRecord;
    s: string;
begin
   result := '';
   n := 0;
   if count = 0 then exit;
   for i := 0 to FList.Count - 1 do begin
      t := FList.Items[i];
      for j := 0 to t^.i - 1 do begin
         inc(n);
         if chr(t^.p^[j]) = c then begin
            if n > 1 then begin
               setlength(s, n - 1);
               ReadData(@s[1], n - 1);
               ReadData(@n   ,     1);
               result := s;
            end else begin
               ReadData(@n   ,     1);
               result := '';
            end;
            exit;
         end;
      end;
   end;
end;

function TAsyncSocket.ReadLine(c: char; t: integer): string;
var tt: longint;
   Msg: tagMSG;
begin
   result := '';
   tt := gettickcount;
   while (result = '') and (longint(gettickcount) < tt + t * 1000) do begin
      if PeekMessage(Msg, m_hWnd.m_hWnd, 0, 0, PM_REMOVE) then begin
         DispatchMessage(Msg);
      end;
      result := ReadLine(c);
      if m_Handle = INVALID_SOCKET then exit;
   end;
end;

function TAsyncSocket.GetIPAddress: String;
begin
  Result := INet_NToA(m_SockAddr.sin_addr);
end;  // function TAsyncSocket.GetIPAddress: String

function TAsyncSocket.GetPortNumber: LongInt;
begin
  Result := NToHS(m_SockAddr.sin_port);
end;  // function TAsyncSocket.GetPortNumber: Word

procedure TAsyncSocket.AllocateSocket;
begin
  if (m_Handle = INVALID_SOCKET) then
    begin
    m_Handle := ErrorTest(socket(AF_INET, SOCK_STREAM, 0));
    end;  // if (m_Handle = INVALID_SOCKET) then
end;  // procedure TAsyncSocket.AllocateSocket

procedure TAsyncSocket.SetSocketHandle(NewSocketHandle: TSocket);
begin
  DoFinal(True);
  m_Handle := NewSocketHandle;
  ErrorTest(WSAAsyncSelect(m_Handle, m_hWnd.Handle, WM_SOCKET, EVENTS_SETSOCKETHANDLE));
end;  // procedure TAsyncSocket.SetSocketHandle(NewSocketHandle: TSocket)

function TAsyncSocket.GetConnected;
begin
   result := fConnected;
end;

function TAsyncSocket.ErrorTest(Evaluation: LongInt): LongInt;
var
  TempMessage: TWMSocket;
begin
  if ((Evaluation = SOCKET_ERROR) OR (Evaluation = INVALID_SOCKET)) then
    begin
    TempMessage.Msg := WM_SOCKETERROR;
    TempMessage.SocketError := WSAGetLastError;
    TempMessage.SocketNumber := m_Handle;
    TempMessage.SocketAddress := @self;
    Message_Error(TempMessage);
    Result := Evaluation;
    end  // if ((Evaluation = SOCKET_ERROR) OR (Evaluation = INVALID_SOCKET)) then
  else
    Result := Evaluation;
end;  // function ErrorTest(Evaluation: LongInt): LongInt;

procedure TAsyncSocket.KillWinsockBug;
var
  Addr: Integer;
begin
  Addr := 0;
  // For an unknown reason, if a call is made to GetHostByName and it should
  // fail, the following call to GetHostByAddr will not fail, but return '>'
  // in the place of the host name.  This clears the problem up.
  GetHostByName('');
  GetHostByAddr(@Addr, SizeOf(Integer), PF_INET);
  GetHostByName('');
end;

procedure TAsyncSocket.SetIPAddress(NewIPAddress: String);
var
   pTempHostEnt: PHostEnt;
begin
  m_SockAddr.sin_addr.s_addr := INet_Addr(PChar(NewIPAddress));
  if (m_SockAddr.sin_addr.s_addr = u_long(INADDR_NONE))  then
     begin
     pTempHostEnt := GetHostByName(PChar(NewIPAddress));
     if (pTempHostEnt <> Nil) then
        m_SockAddr.sin_addr.s_addr := PInAddr(pTempHostEnt^.h_addr_list^)^.s_addr;
     end;
end;  // procedure TAsyncSocket.SetIPAddress(NewIPAddress: String)

procedure TAsyncSocket.SetPortNumber(NewPortNumber: LongInt);
begin
  if ((NewPortNumber > 0) AND (NewPortNumber <= MaxWord)) then
    m_SockAddr.sin_port := HToNS(NewPortNumber);
end;  // procedure TAsyncSocket.SetPortNumber(NewPortNumber: Word)

procedure TAsyncSocket.DoReceive(Buffer: Pointer; var ReceiveLen: LongInt);
begin
  ReceiveLen := recv(m_Handle, Buffer^, ReceiveLen, 0);
  ErrorTest(ReceiveLen);
end;  // TAsyncSocket.DoReceive(Buffer: Pointer; BufferLen: LongInt)

procedure TAsyncSocket.DoSend(Buffer: Pointer; var SendLen: LongInt);
begin
  SendLen := send(m_Handle, Buffer^, SendLen, 0);
  ErrorTest(SendLen);
end;  // procedure TAsyncSocket.DoSend(Buffer: Pointer; BufferLen: LongInt)

procedure TAsyncSocket.DoLookup;
var
   IPAddr  : TInAddr;
begin
   if Host = '' then begin
      Exit;
   end;

   { Cancel any pending lookup }
   if FDnsHandle <> 0 then
      WSACancelAsyncRequest(FDnsHandle);

    FDnsResult := '';

    IPAddr.S_addr := Inet_addr(PChar(Host));
    if IPAddr.S_addr <> u_long(INADDR_NONE) then begin
        FDnsResult := inet_ntoa(IPAddr);
{        TriggerDnsLookupDone(0);}
        Exit;
    end;

    FDnsHandle := WSAAsyncGetHostByName(m_hWnd.Handle,
                                        WM_SOCKETLOOKUP,
                                        @Host[1],
                                        @FDnsBuffer,
                                        SizeOf(FDnsBuffer));
    if FDnsHandle = 0 then begin
       ErrorTest(WSAGetLastError);
       Exit;
    end;
end;

procedure TAsyncSocket.DoClose;
begin
   DoFinal(True);
end;

procedure TAsyncSocket.DoFinal;
var
  TempMessage: TWMSocket;
begin
  if (m_Handle <> INVALID_SOCKET) then begin
    if not Abort then begin
       ProcessMessages;
    end;
    TempMessage.Msg := WM_SOCKETCLOSE;
    TempMessage.SocketNumber := m_Handle;
    TempMessage.SocketAddress := @self;
    Message_Close(TempMessage);
    ErrorTest(closesocket(m_Handle));
    m_Handle := INVALID_SOCKET;
    end;
end;

procedure TAsyncSocket.DoAccept(var AcceptSocket: PAsyncSocket);
var
  TempSize: Integer;
  TempSock: TSocket;
  TempAddr: TSockAddrIn;
begin
  TempSize := SizeOf(TSockAddr);
  TempSock := accept(m_Handle, @TempAddr, @TempSize);
  AcceptSocket.m_SockAddr := TempAddr;
  if (ErrorTest(TempSock) <> INVALID_SOCKET) then
    AcceptSocket.SocketHandle := TempSock;
end;  // procedure TAsyncSocket.DoAccept(var AcceptSocket: TAsyncSocket)

procedure TAsyncSocket.DoListen;
var
  TempMessage: TWMSocket;
begin
  DoClose;
  AllocateSocket;
  if
    (ErrorTest(WSAAsyncSelect(m_Handle, m_hWnd.Handle, WM_SOCKET, EVENTS_DOLISTEN))
      <> SOCKET_ERROR) AND
    (ErrorTest(bind(m_Handle, m_SockAddr, SizeOf(TSockAddr))) <> SOCKET_ERROR) AND
    (ErrorTest(listen(m_Handle, 5)) <> SOCKET_ERROR) then
    begin
    TempMessage.Msg := WM_SOCKETLISTEN;
    TempMessage.SocketNumber := m_Handle;
    TempMessage.SocketAddress := @self;
    Message_Listen(TempMessage);
    end
  else
    DoClose;
end;  // procedure TAsyncSocket.DoListen

procedure TAsyncSocket.DoConnect;
var
  TempResult: LongInt;
begin
  DoClose;
  AllocateSocket;
  ErrorTest(WSAAsyncSelect(m_Handle, m_hWnd.Handle, WM_SOCKET, EVENTS_DOCONNECT));
  TempResult := connect(m_Handle, m_SockAddr, SizeOf(TSockAddr));
  if ((TempResult = SOCKET_ERROR) AND (WSAGetLastError <> WSAEWOULDBLOCK)) then
    ErrorTest(SOCKET_ERROR);
end;  // procedure TAsyncSocket.DoConnect

procedure TAsyncSocket.SendString;
var
  L: LongInt;
begin
  L := Length(fString);
  DoSend(PChar(fString), L);
end;

function TAsyncSocket.DoGetHostByName(Name: PChar): String;
var
   pTempHostEnt: PHostEnt;
begin
  pTempHostEnt := GetHostByName(Name);
  if (pTempHostEnt <> Nil) then
    Result := inet_ntoa(pInAddr(pTempHostEnt^.h_addr_list^)^)
  else
    Result := '';
end;

procedure TAsyncSocket.ProcessMessages;
var Msg: TMsg;
begin
   while PeekMessage(Msg, m_hWnd.m_hWnd, WM_SOCKET, WM_SOCKETLOOKUP, PM_REMOVE) do begin
      DispatchMessage(Msg);
   end;
end;

function TAsyncSocket.DoGetHostByAddr(IPAddr: PChar): String;
var
   pTempHostEnt: PHostEnt;
   TempAddr: LongInt;
begin
  TempAddr := INet_Addr(IPAddr);
  pTempHostEnt := GetHostByAddr(@TempAddr, SizeOf(TempAddr), PF_INET);
  if (pTempHostEnt <> Nil) then
    Result := pTempHostEnt^.h_name
  else
    Result := '';
end;

procedure TAsyncSocket.HWndProcedure(var Message: TMessage);
var
  TempMessage: TWMSocket;
begin
  case Message.Msg of
    WM_SOCKETLOOKUP:
      begin
         TempMessage.Msg := WM_SOCKETLOOKUP;
         TempMessage.SocketNumber := m_Handle;
         TempMessage.SocketAddress := @self;
         Message_Lookup(TempMessage);
      end;
    WM_SOCKET:
      begin
      if (Message.LParamHi > WSABASEERR) then
        begin
        WSASetLastError(Message.LParamHi);
        ErrorTest(SOCKET_ERROR);
        end  // if (Message.LParamHi > WSABASEERR) then
      else
        begin
        case Message.LParamLo of
          FD_READ:
            begin
            TempMessage.SocketDataSize := 0;
            ErrorTest(IOCtlSocket(m_Handle, FIONREAD, TempMessage.SocketDataSize));
            TempMessage.Msg := WM_SOCKETREAD;
            TempMessage.SocketNumber := m_Handle;
            TempMessage.SocketAddress := @self;
            Message_Read(TempMessage);
            end;  // FD_READ
          FD_CLOSE:
            begin
            DoFinal(False);
            end; // FD_CLOSE
          FD_CONNECT:
            begin
            TempMessage.Msg := WM_SOCKETCONNECT;
            TempMessage.SocketNumber := m_Handle;
            TempMessage.SocketAddress := @self;
            Message_Connect(TempMessage);
            end;  // FD_CONNECT
          FD_ACCEPT:
            begin
            TempMessage.Msg := WM_SOCKETACCEPT;
            TempMessage.SocketNumber := m_Handle;
            TempMessage.SocketAddress := @self;
            Message_Accept(TempMessage);
            end;  // FD_ACCEPT
          FD_WRITE:
            begin
            TempMessage.Msg := WM_SOCKETWRITE;
            TempMessage.SocketNumber := m_Handle;
            TempMessage.SocketAddress := @self;
            Message_Write(TempMessage);
            end;  // FD_WRITE
          FD_OOB:
            begin
            TempMessage.Msg := WM_SOCKETOOB;
            TempMessage.SocketNumber := m_Handle;
            TempMessage.SocketAddress := @self;
            Message_OOB(TempMessage);
            end;  // FD_OOB
          end;  // case Message.LParamLo of
        end  // else (if (Message.LParamHi > WSABASEERR) then)
      end;  // WM_SOCKET:
      else
      Message.Result := DefWindowProc(m_hWnd.m_hWnd, Message.Msg, Message.WParam, Message.LParam);
    end;  // case Message.Msg of
end;  // procedure TAsyncSocket.HWndProcedure(var Message: TMessage)

procedure TAsyncSocket.Message_Error(var Message: TWMSocket);
begin
  if Assigned(FOnError) then FOnError(Message)
  else
  MessageBox(HWND_DESKTOP, PChar(ErrToStr(Message.SocketError) + ' on socket ' +
    Int2Str(Message.SocketNumber)), 'Message_Error', MB_OK);
end;  // procedure TAsyncSocket.Message_Error(var Message: TWMSocket)

procedure TAsyncSocket.Message_Lookup(var Message: TWMSocket);
var p: PHostEnt;
begin
  p := @fDNSBuffer;
  fDNSResult := p.h_name;
  if Assigned(FOnLookup) then FOnLookup(Message)
  else
  MessageBox(HWND_DESKTOP, PChar('WM_SOCKETLOOKUP on socket ' + Int2Str(Message.SocketNumber)),
    'Message_Lookup', MB_OK);
end;  // procedure TAsyncSocket.Message_LookUp(var Message: TWMSocket)

procedure TAsyncSocket.Message_Close(var Message: TWMSocket);
begin
  fConnected := False;
  if Assigned(FOnClose) then FOnClose(Message)
  else
  MessageBox(HWND_DESKTOP, PChar('WM_SOCKETCLOSE on socket ' + Int2Str(Message.SocketNumber)),
    'Message_Close', MB_OK);
end;  // procedure TAsyncSocket.Message_Close(var Message: TWMSocket)

procedure TAsyncSocket.Message_Accept(var Message: TWMSocket);
begin
  fConnected := True;
  if Assigned(FOnAccept) then FOnAccept(Message)
  else
  MessageBox(HWND_DESKTOP, PChar('WM_SOCKETACCEPT on socket ' + Int2Str(Message.SocketNumber)),
    'Message_Accept', MB_OK);
end;  // procedure TAsyncSocket.Message_Accept(var Message: TWMSocket)

procedure TAsyncSocket.Message_Read(var Message: TWMSocket);
var t:^TBufRecord;
begin
  if Message.SocketDataSize > 0 then begin
     fConnected := True;
     GetMem(t, sizeof(TBufRecord));
     t^.i := Message.SocketDataSize;
     GetMem(t^.p, t^.i);
     DoReceive(t^.p, t^.i);
     FList.Add(t);
  end;
  if Assigned(FOnRead) then FOnRead(Message)
  else
  MessageBox(HWND_DESKTOP, PChar('WM_SOCKETREAD on socket ' + Int2Str(Message.SocketNumber)),
    'Message_Read', MB_OK);
end;  // procedure TAsyncSocket.Message_Read(var Message: TWMSocket)

procedure TAsyncSocket.Message_Connect(var Message: TWMSocket);
begin
  fConnected := True;
  if Assigned(FOnConnect) then FOnConnect(Message)
  else
  MessageBox(HWND_DESKTOP, PChar('WM_SOCKETCONNECT on socket ' + Int2Str(Message.SocketNumber)),
    'Message_Connect', MB_OK);
end;  // procedure TAsyncSocket.Message_Connect(var Message: TWMSocket)

procedure TAsyncSocket.Message_Write(var Message: TWMSocket);
begin
  fConnected := True;
  if Assigned(FOnWrite) then FOnWrite(Message)
  else
  MessageBox(HWND_DESKTOP, PChar('WM_SOCKETWRITE on socket ' + Int2Str(Message.SocketNumber)),
    'Message_Write', MB_OK);
end;  // procedure TAsyncSocket.Message_Write(var Message: TWMSocket)

procedure TAsyncSocket.Message_OOB(var Message: TWMSocket);
begin
  if Assigned(FOnOOB) then FOnOOB(Message)
  else
  MessageBox(HWND_DESKTOP, PChar('WM_SOCKETOOB on socket ' + Int2Str(Message.SocketNumber)),
    'Message_OOB', MB_OK);
end;  // procedure TAsyncSocket.Message_OOB(var Message: TWMSocket)

procedure TAsyncSocket.Message_Listen(var Message: TWMSocket);
begin
  if Assigned(FOnListen) then FOnListen(Message)
  else
  MessageBox(HWND_DESKTOP, PChar('WM_SOCKETLISTEN on socket ' + Int2Str(Message.SocketNumber)),
    'Message_Listen', MB_OK);
end;  // procedure TAsyncSocket.Message_Listen(var Message: TWMSocket)

destructor TAsyncSocket.Destroy;
var t:^TBufRecord;
    i: integer;
begin
  DoClose;
  if (InstanceCount = c_FIRST) then
    ErrorTest(WSACleanup);
  m_hWnd.Free;
  for i := 0 to FList.Count - 1 do begin
     t := FList.Items[i];
     FreeMem(t^.p, t^.i);
     FreeMem(t, SizeOf(TBufRecord));
  end;
  FList.Free;
  InstanceCount := InstanceCount - 1;
  inherited;
end;

function TAsyncSocket.ErrToStr(Err: LongInt): String;
begin
  case Err of
    WSAEINTR:
      Result := 'WSAEINTR';
    WSAEBADF:
      Result := 'WSAEBADF';
    WSAEACCES:
      Result := 'WSAEACCES';
    WSAEFAULT:
      Result := 'WSAEFAULT';
    WSAEINVAL:
      Result := 'WSAEINVAL';
    WSAEMFILE:
      Result := 'WSAEMFILE';
    WSAEWOULDBLOCK:
      Result := 'WSAEWOULDBLOCK';
    WSAEINPROGRESS:
      Result := 'WSAEINPROGRESS';
    WSAEALREADY:
      Result := 'WSAEALREADY';
    WSAENOTSOCK:
      Result := 'WSAENOTSOCK';
    WSAEDESTADDRREQ:
      Result := 'WSAEDESTADDRREQ';
    WSAEMSGSIZE:
      Result := 'WSAEMSGSIZE';
    WSAEPROTOTYPE:
      Result := 'WSAEPROTOTYPE';
    WSAENOPROTOOPT:
      Result := 'WSAENOPROTOOPT';
    WSAEPROTONOSUPPORT:
      Result := 'WSAEPROTONOSUPPORT';
    WSAESOCKTNOSUPPORT:
      Result := 'WSAESOCKTNOSUPPORT';
    WSAEOPNOTSUPP:
      Result := 'WSAEOPNOTSUPP';
    WSAEPFNOSUPPORT:
      Result := 'WSAEPFNOSUPPORT';
    WSAEAFNOSUPPORT:
      Result := 'WSAEAFNOSUPPORT';
    WSAEADDRINUSE:
      Result := 'WSAEADDRINUSE';
    WSAEADDRNOTAVAIL:
      Result := 'WSAEADDRNOTAVAIL';
    WSAENETDOWN:
      Result := 'WSAENETDOWN';
    WSAENETUNREACH:
      Result := 'WSAENETUNREACH';
    WSAENETRESET:
      Result := 'WSAENETRESET';
    WSAECONNABORTED:
      Result := 'WSAECONNABORTED';
    WSAECONNRESET:
      Result := 'WSAECONNRESET';
    WSAENOBUFS:
      Result := 'WSAENOBUFS';
    WSAEISCONN:
      Result := 'WSAEISCONN';
    WSAENOTCONN:
      Result := 'WSAENOTCONN';
    WSAESHUTDOWN:
      Result := 'WSAESHUTDOWN';
    WSAETOOMANYREFS:
      Result := 'WSAETOOMANYREFS';
    WSAETIMEDOUT:
      Result := 'WSAETIMEDOUT';
    WSAECONNREFUSED:
      Result := 'WSAECONNREFUSED';
    WSAELOOP:
      Result := 'WSAELOOP';
    WSAENAMETOOLONG:
      Result := 'WSAENAMETOOLONG';
    WSAEHOSTDOWN:
      Result := 'WSAEHOSTDOWN';
    WSAEHOSTUNREACH:
      Result := 'WSAEHOSTUNREACH';
    WSAENOTEMPTY:
      Result := 'WSAENOTEMPTY';
    WSAEPROCLIM:
      Result := 'WSAEPROCLIM';
    WSAEUSERS:
      Result := 'WSAEUSERS';
    WSAEDQUOT:
      Result := 'WSAEDQUOT';
    WSAESTALE:
      Result := 'WSAESTALE';
    WSAEREMOTE:
      Result := 'WSAEREMOTE';
    WSASYSNOTREADY:
      Result := 'WSASYSNOTREADY';
    WSAVERNOTSUPPORTED:
      Result := 'WSAVERNOTSUPPORTED';
    WSANOTINITIALISED:
      Result := 'WSANOTINITIALISED';
    WSAHOST_NOT_FOUND:
      Result := 'WSAHOST_NOT_FOUND';
    WSATRY_AGAIN:
      Result := 'WSATRY_AGAIN';
    WSANO_RECOVERY:
      Result := 'WSANO_RECOVERY';
    WSANO_DATA:
      Result := 'WSANO_DATA';
    else Result := 'UNDEFINED WINSOCK ERROR';
    end;  // case Err of
end;  // function TAsyncSocket.ErrToStr(Err: LongInt): String

function TAsyncSocket.LocalIP;
var Name: TSockAddrIn;
     len: integer;
begin
   GetSockName(m_Handle, Name, len);
   Result := int2str(ord(Name.sin_addr.S_un_b.s_b1)) + '.' +
             int2str(ord(Name.sin_addr.S_un_b.s_b2)) + '.' +
             int2str(ord(Name.sin_addr.S_un_b.s_b3)) + '.' +
             int2str(ord(Name.sin_addr.S_un_b.s_b4));
end;

function TAsyncSocket.LocalPort;
var Name: TSockAddrIn;
     len: integer;
     err: integer;
     Tmp: TWMSocket;
begin
   Result := 0;
   err := GetSockName(m_Handle, Name, len);
   if err = 0 then begin
      Result  := NToHS(Name.sin_port);
   end else begin
      Tmp.Msg := WM_SOCKETERROR;
      Tmp.SocketError := WSAGetLastError;
      Tmp.SocketNumber := m_Handle;
      Tmp.SocketAddress := @self;
      Message_Error(Tmp);
   end;
end;

end.

