{ **************************************************************
	Package: XWB - Kernel RPCBroker
	Date Created: Sept 18, 1997 (Version 1.1)
	Site Name: Oakland, OI Field Office, Dept of Veteran Affairs
	Developers: Danila Manapsal, Don Craven, Joel Ivey
	Description: manages Winsock connections and creates/parses
	             messages
	Current Release: Version 1.1 Patch 40 (Sept. 22, 2004)
*************************************************************** }

unit Wsockc;
{
  Changes in v1.1.13 (JLI -- 8/23/00) -- XWB*1.1*13
    Made changes to cursor dependent on current cursor being crDefault so
       that the application can set it to a different cursor for long or
       repeated processes without the cursor 'flashing' repeatedly.

  Changes in v1.1.8 (REM -- 6/18/99) -- XWB*1.1*8
    Update version 'BrokerVer'.

  Changes in v1.1.6 (DPC -- 6/7/99) -- XWB*1.1*6
    In tCall function, made changing cursor to hourglass conditional:
    don't do it if XWB IM HERE  RPC is being invoked.

  Changes in V1.1.4 (DCM - 9/18/98)-XWB*1.1*4
  1.  Changed the ff line in NetStart from:
      if inet_addr(PChar(Server)) <> INADDR_NONE then
      to
      if inet_addr(PChar(Server)) <> longint(INADDR_NONE) then
  Reason:  true 64 bit types in Delphi 4
  2.  Changed the ff line in NetStart from:
      $else
      hSocket := accept(hSocketListen, DHCPHost, AddrLen);{ -- returns new socket
      to
      $else
      hSocket := accept(hSocketListen, @DHCPHost, @AddrLen);{ -- returns new socket
  Reason:  Incompatible types when recompiling
  3. In NetStop, if socket <= 0, restore the default cursor.
  Reason:  Gave the impression of a busy process after the Kernel login
      process timesout.

  Changes in V1.1T3  [Feb 5, 1997]
  1. Connect string now includes workstation name. This is used by kernel
     security.
  2. Code is 32bit compliant for Delphi 2.0
  3. A great majority of PChars changed to default string (ansi-string)
  4. Reading is done in 32k chunks during a loop.  Intermediate data is
     buffered into a string.  At the end, a PChar is allocated and
     returned to maintain compatibility with the original broker interface.
     It is expected that shortly this will change once the broker component
     changes its usage of tcall to expect a string return.  Total read
     can now exceed 32K up to workstation OS limits.
  5. Creation of Hostent and Address structures has been streamlined.

  Changes in V1.0T12
  1. Inclusion of hSocket as a parameter on most API calls


  Changes in V1.0T11
  1. Reference parameter type is included. i.e. $J will be evaluated rather
  than sending "$J".
  2. Fully integrated with the TRPCB component interface.
  3. This low level module is now called from an intermediate DLL.

  Changes in V1.0T10
  1. Fixed various memory leaks.

  Changes in V1.0T9
  1. Supports word processing fields.
  2. Added a new exception type EBrokerError.  This is raised when errors occur
  in NetCall, NetworkConnect, and NetworkDisconnect

  Changes in V1.0T8
  1. Fix a problem in BuildPar in the case of a single list parameter with many
     entries.
  2. List parameters (arrays) can be large up to 65520 bytes
  3. Introduction of sCallV and tCallV which use the Delphi Pascal open array
     syntax (sCallFV and tCallV developed by Kevin Meldrum)
  4. A new brokerDataRec type, null has been introduced to represent M calls
     with no parameters, i.e. D FUN^LIB().
  5. If you want to send a null parameter "", i.e. D FUN^LIB(""), Value
  should be set to ''.
  6. Fixed bug where a single ^ passed to sCall would generate error (confused
  as a global reference.
  7. Fixed a bug where a first position dot (.) in a literal parameter would
  cause an error at the server end.
  8. Fixed a bug where null strings (as white space in a memo box for example)
  would not be correctly received at the server.

  Changes in V1.0T7
  1. Procedure NetworkConnect has been changed to Function NetworkConnect
     returning BOOL
  2. global variable IsConnected (BOOL) can be used to determine connection
     state
  3. Function cRight has been fixed to preserve head pointer to input PChar
     string
  4. New message format which includes length calculations for input parameters

   *******************************************************************
  A 32-bit high level interface to the Winsock API in Delphi Pascal.

  This implementation allows communications between Delphi forms and
  DHCP back end servers through the use of the DHCP Request Broker.

  Usage: Put wsock in your Uses clause of your Delphi form.  See additional
  specs for Request Broker message formats, etc.
  Programmer: Enrique Gomez - VA San Francisco ISC - April 1995
}


interface

Uses
SysUtils, winsock, xwbut1, WinProcs, Wintypes,
classes, dialogs, forms, controls,
stdctrls, ClipBrd, Trpcb, RpcbErr;

type
  TXWBWinsock = class(TObject)
  private
    FCountWidth: Integer;
    FIsBackwardsCompatible: Boolean;
    FOldConnectionOnly: Boolean;
  public
    XNetCallPending, xFlush: boolean;
    SocketError, XHookTimeOut: integer;
    XNetTimerStart: TDateTime;
    BROKERSERVER: string;
    SecuritySegment, ApplicationSegment: string;
    IsConnected: Boolean;
//    NetBlockingHookVar: Function(): Bool; export;
    function NetCall(hSocket: integer; imsg: string): PChar;
    function tCall(hSocket: integer; api, apVer: String; Parameters: TParams;
             var Sec, App: PChar; TimeOut: integer): PChar;
    function cRight( z: PChar;  n: longint): PChar;
    function cLeft( z: PChar; n: longint): PChar;
    function BuildApi ( n,p: string; f: longint): string;
    function BuildHdr ( wkid: string; winh: string; prch: string;
             wish: string): string;
    function BuildPar(hSocket: integer; api, RPCVer: string;
             const Parameters: TParams): string;
    function StrPack ( n: string; p: integer): string;
    function VarPack(n: string): string;
    function NetStart(ForegroundM: boolean; Server: string; ListenerPort: integer;
                  var hSocket: integer): integer;
    function NetworkConnect(ForegroundM: boolean; Server: string; ListenerPort, 
        TimeOut: integer): Integer;
    function libSynGetHostIP(s: string): string;
    function libNetCreate (lpWSData : TWSAData) : integer;
    function libNetDestroy: integer;
    function GetServerPacket(hSocket: integer): string;
//    function NetBlockingHook: BOOL; export;

    procedure NetworkDisconnect(hSocket: integer);
    procedure NetStop(hSocket: integer);
    procedure CloseSockSystem(hSocket: integer; s: string);
    constructor Create;

    procedure NetError(Action: string; ErrType: integer);
function NetStart1(ForegroundM: boolean; Server: string; ListenerPort: integer; 
    var hSocket: integer): Integer; virtual;
    function BuildPar1(hSocket: integer; api, RPCVer: string; const Parameters: 
        TParams): String; virtual;
    property CountWidth: Integer read FCountWidth write FCountWidth;
    property IsBackwardsCompatible: Boolean read FIsBackwardsCompatible write 
        FIsBackwardsCompatible;
    property OldConnectionOnly: Boolean read FOldConnectionOnly write
        FOldConnectionOnly;
  end;

function LPack(Str: String; NDigits: Integer): String;

function SPack(Str: String): String;

function NetBlockingHook: BOOL; export;

var
  HookTimeOut: Integer;
  NetCallPending: Boolean;
  NetTimerStart: TDateTime;

Const
 WINSOCK1_1 = $0101;
 DHCP_NAME = 'BROKERSERVER';
 M_DEBUG = True;
 M_NORMAL = False;
 BrokerVer = '1.108';
 Buffer64K = 65520;
 Buffer32K = 32767;
 Buffer24K = 24576;
 Buffer16K = 16384;
 Buffer8K = 8192;
 Buffer4K = 4096;
 DefBuffer = 256;
 DebugOn: boolean = False;
 XWBBASEERR = {WSABASEERR + 1} 20000;

{Broker Application Error Constants}
 XWB_NO_HEAP    = XWBBASEERR + 1;
 XWB_M_REJECT   = XWBBASEERR + 2;
 XWB_BadSignOn  = XWBBASEERR + 4;
 XWB_BadReads   = XWBBASEERR + 8;
 XWB_ExeNoMem   = XWBBASEERR + 100;
 XWB_ExeNoFile  = XWB_ExeNoMem +  2;
 XWB_ExeNoPath  = XWB_ExeNoMem +  3;
 XWB_ExeShare   = XWB_ExeNoMem +  5;
 XWB_ExeSepSeg  = XWB_ExeNoMem +  6;
 XWB_ExeLoMem   = XWB_ExeNoMem +  8;
 XWB_ExeWinVer  = XWB_ExeNoMem + 10;
 XWB_ExeBadExe  = XWB_ExeNoMem + 11;
 XWB_ExeDifOS   = XWB_ExeNoMem + 12;
 XWB_RpcNotReg  = XWBBASEERR + 201;

implementation

 uses fDebugInfo; {P36} //, TRPCB;

var
  Prefix: String;

{
  function LPack
  Prepends the length of the string in NDigits characters to the value of Str

  e.g., LPack('DataValue',4)
  returns   '0009DataValue'
}
function LPack(Str: String; NDigits: Integer): String;
Var
  r: Integer;
  t: String;
  Width: Integer;
  Ex1: Exception;
begin
  r := Length(Str);
  // check for enough space in NDigits characters
  t := IntToStr(r);
  Width := Length(t);
  if NDigits < Width then
  begin
    Ex1 := Exception.Create('In generation of message to server, call to LPack where Length of string of '+IntToStr(Width)+' chars exceeds number of chars for output length ('+IntToStr(NDigits)+')');
    Raise Ex1;
  end;
  t := '000000000' + IntToStr(r);               {eg 11-1-96}
  Result := Copy(t, length(t)-(NDigits-1),length(t)) + Str;
end;

{
  function SPack
  Prepends the length of the string in one byte to the value of Str, thus Str must be less than 256 characters.

  e.g., SPack('DataValue')
  returns   #9 + 'DataValue'
}
function SPack(Str: String): String;
Var
  r: Integer;
  Ex1: Exception;
begin
  r := Length(Str);
  // check for enough space in one byte
  if r > 255 then
  begin
    Ex1 := Exception.Create('In generation of message to server, call to SPack with Length of string of '+IntToStr(r)+' chars which exceeds max of 255 chars');
    Raise Ex1;
  end;
//  t := Byte(r);               
  Result := Char(r) + Str;
end;


function TXWBWinsock.libNetCreate (lpWSData : TWSAData) : integer;
begin
    Result := WSAStartup(WINSOCK1_1, lpWSData); {hard coded for Winsock
           version 1.1}
end;

function TXWBWinsock.libNetDestroy :integer;
begin
  WSAUnhookBlockingHook;      { -- restore the default mechanism};
  WSACleanup;                 { -- shutdown TCP API};
  Result := 1;
end;

function TXWBWinsock.libSynGetHostIP(s: string): string;
var
   HostName: PChar;
   HostAddr: TSockAddr;
   TCPResult: PHostEnt;
   test: longint;
   ChangeCursor: Boolean;
begin
    { -- set up a hook for blocking calls so there is no automatic DoEvents
      in the background }
   xFlush := False;
   NetTimerStart := Now;
   NetCallPending := True;
   HookTimeOut := XHookTimeOut;
   WSASetBlockingHook(@NetBlockingHook);

   if Screen.Cursor = crDefault then
     ChangeCursor := True
   else
     ChangeCursor := False;
   if ChangeCursor then
     Screen.Cursor := crHourGlass;
   HostName := StrNew(PChar(s));
   test := inet_addr(HostName);
   if test > INADDR_ANY then
      begin
           Result := s;
           StrDispose(Hostname);
           if ChangeCursor then
             Screen.Cursor := crDefault;
           exit;
      end;

   try
   begin
      TCPResult := gethostbyname(HostName);
      if TCPResult = nil then
         begin
              if ChangeCursor then
                Screen.Cursor := crDefault;
              WSAUnhookBlockingHook;
              Result := '';
              StrDispose(HostName);
              exit;
         end;

      HostAddr.sin_addr.S_addr := longint(plongint(TCPResult^.h_addr_list^)^);

   end;
   except on EInvalidPointer do
          begin
               Result := '';
               Screen.Cursor := crDefault;
               StrDispose(HostName);
               exit;
          end;
   end;
   if ChangeCursor then
     Screen.Cursor := crDefault;
   WSAUnhookBlockingHook;
   Result := StrPas(inet_ntoa(HostAddr.sin_addr));
   StrDispose(HostName);
end;

function TXWBWinsock.cRight;
var
   i,t: longint;
begin
     t := strlen(z);
     if n < t then
        begin
             for i := 0 to n do
                 z[i] := z[t-n+i];
             z[n] := chr(0);
        end;
     cRight := z;
end;

function TXWBWinsock.cLeft;
var
   t: longint;
begin
     t := strlen(z);
     if n > t then n := t;
     z[n] := chr(0);
     cLeft := z;
end;

function TXWBWinsock.BuildApi ( n,p: string; f: longint): string;
Var
  x,s: string;
begin
     str(f,x);
     s := StrPack(p,5);
     result := StrPack(x + n + '^' + s,5);
end;

function TXWBWinsock.NetworkConnect(ForegroundM: boolean; Server: string; 
    ListenerPort, TimeOut: integer): Integer;
var
   status: integer;
   hSocket: integer;
   BrokerError: EBrokerError;
begin
  Prefix := '[XWB]';
     xFlush := False;
     IsConnected := False;
     XHookTimeOut := TimeOut;
     if not OldConnectionOnly then
     try
       status := NetStart(ForeGroundM, server, ListenerPort, hSocket);
     except
       on E: EBrokerError do
       begin
         if IsBackwardsCompatible then  // remove DSM specific error message, and just go with any error
         begin
           status := NetStart1(ForeGroundM, server, ListenerPort, hSocket);
         end
         else if ((Pos('connection lost',E.Message) > 0) //  DSM
              or ((Pos('recv',E.Message) > 0) and (Pos('WSAECONNRESET',E.Message) > 0))) then  // Cache
         begin
           BrokerError := EBrokerError.Create('Broker requires a UCX or single connection protocol and this port uses the callback protocol.'+'  The application is specified to be non-backwards compatible.  Installing patch XWB*1.1*35 and activating this port number for UCX connections will correct the problem.');
           raise BrokerError;
         end
         else
           raise;
       end;
     end
     else  // OldConnectionOnly
       status := NetStart1(ForeGroundM, server, ListenerPort, hSocket);

     if status = 0 then IsConnected := True;
     Result := hSocket;                  {return the newly established socket}
end;

procedure TXWBWinsock.NetworkDisconnect(hSocket: integer);
begin
     xFlush := False;
     if IsConnected then
     try
        NetStop(hSocket);
     except on EBrokerError do
     begin
          SocketError := WSAUnhookBlockingHook;     { -- rest deflt mechanism}
          SocketError := WSACleanup;                { -- shutdown TCP API}
     end;
     end;

end;

function TXWBWinsock.BuildHdr ( wkid: string; winh: string; prch: string;
         wish: string): string;
Var
  t: string;
begin
   t := wkid + ';' + winh + ';' + prch + ';' + wish + ';';
   Result := StrPack(t,3);
end;

function TXWBWinsock.BuildPar(hSocket: integer; api, RPCVer: string;
         const Parameters: TParams): string;
var
  i,ParamCount: integer;
  param: string;
  tResult: string;
  subscript: string;
  IsSeen: Boolean;
  BrokerError: EBrokerError;
  Str: String;
begin
  param := '5';
  if Parameters = nil then ParamCount := 0
  else ParamCount := Parameters.Count;
  for i := 0 to ParamCount - 1 do
  begin
    if Parameters[i].PType <> undefined then
    begin
       // Make sure that new parameter types are only used with non-callback server.
      if IsBackwardsCompatible and ((Parameters[i].PType = global) or (Parameters[i].PType = empty) or (Parameters[i].PType = stream)) then
      begin
        if Parameters[i].PType = global then
          Str := 'global'
        else if Parameters[i].PType = empty then
          Str := 'empty'
        else
          Str := 'stream';
        BrokerError := EBrokerError.Create('Use of ' + Str + ' parameter type requires setting the TRPCBroker IsBackwardsCompatible property to FALSE');
        raise BrokerError;
      end;
      with Parameters[i] do
      begin
//        if PType= null then
//          param:='';

        if PType = literal then
          param := param + '0'+LPack(Value,CountWidth)+'f';      // 030107 new message protocol

        if PType = reference then
          param := param + '1'+LPack(Value,CountWidth)+'f';     // 030107 new message protocol

        if PType = empty then
          param := param + '4f';

        if (PType = list) or (PType = global) then
        begin
          if PType = list then      // 030107 new message protocol
            param := param + '2'
          else
            param := param + '3';
          IsSeen := False;
          subscript := Mult.First;
          while subscript <> '' do
          begin
            if IsSeen then
              param := param + 't';
            if Mult[subscript] = '' then
              Mult[subscript] := #1;
            param := param + LPack(subscript,CountWidth)+LPack(Mult[subscript],CountWidth);
            IsSeen := True;
            subscript := Mult.Order(subscript,1);
          end;  // while subscript <> ''
          if not IsSeen then         // 040922 added to take care of list/global parameters with no values
            param := param + LPack('',CountWidth);
          param := param + 'f';
        end;
        if PType = stream then
        begin
          param := param + '5' + LPack(Value,CountWidth) + 'f';
        end;
      end;  // with Parameters[i] do
    end;  // if Parameters[i].PType <> undefined
  end; // for i := 0
  if param = '5' then
    param := param + '4f';

  tresult := Prefix + '11' + IntToStr(CountWidth) + '0' + '2' + SPack(RPCVer) + SPack(api) + param + #4;

//  Application.ProcessMessages;  // removed 040716 jli not needed and may impact some programs

  Result := tresult;
end;
{                   // previous message protocol
  sin := TStringList.Create;
  sin.clear;
  x := '';
  param := '';
  arr := 0;
  if Parameters = nil then ParamCount := 0
  else ParamCount := Parameters.Count;
  for i := 0 to ParamCount - 1 do
    if Parameters[i].PType <> undefined then begin
      with Parameters[i] do begin

//        if PType= null then
//          param:='';

        if PType = literal then
          param := param + strpack('0' + Value,3);
        if PType = reference then
          param := param + strpack('1' + Value,3);
        if (PType = list) or (PType = global) then begin
          Value := '.x';
          param := param + strpack('2' + Value,3);
          if Pos('.',Value) >0 then
            x := Copy(Value,2,length(Value));
//            if PType = wordproc then dec(last);
            subscript := Mult.First;
            while subscript <> '' do begin
              if Mult[subscript] = '' then Mult[subscript] := #1;
              sin.Add(StrPack(subscript,3) + StrPack(Mult[subscript],3));
              subscript := Mult.Order(subscript,1);
            end;  // while
            sin.Add('000');
            arr := 1;
        end;  // if
      end;  // with
    end;  // if

param := Copy(param,1,Length(param));
tsize := 0;

tResult := '';
tout := '';

hdr := BuildHdr('XWB','','','');
strout := strpack(hdr + BuildApi(api,param,arr),5);
num :=0;

RPCVersion := '';
RPCVersion := VarPack(RPCVer);

if sin.Count-1 > 0 then num := sin.Count-1;

if num > 0 then
   begin
        for i := 0 to num do
          tsize := tsize + length(sin.strings[i]);
        x := '00000' + IntToStr(tsize + length(strout)+ length(RPCVersion));
   end;
if num = 0 then
   begin
        x := '00000' + IntToStr(length(strout)+ length(RPCVersion));
   end;

psize := x;
psize := Copy(psize,length(psize)-5,5);
tResult := psize;
tResult := ConCat(tResult, RPCVersion);
tout := strout;
tResult := ConCat(tResult, tout);

if num > 0 then
   begin
        for i := 0 to num do
            tResult := ConCat(tResult, sin.strings[i]);
   end;

sin.free;

frmBrokerExample.Edit1.Text := tResult;

Result := tResult;  // return result
end;
}

function TXWBWinsock.StrPack(n: string; p: integer): String;
Var
  s,l: integer;
  t,x,zero: shortstring;
  y: string;
begin

    s := Length(n);
    fillchar(zero,p+1, '0');
    SetLength(zero, p);
    str(s,x);
    t := zero + x;
    l := length(x)+1;
    y := Copy(t, l , p);
    y := y + n;
    Result := y;
end;

function TXWBWinsock.VarPack(n: string): string;
var
   s: integer;
begin
     if n = '' then
        n := '0';
     s := Length(n);
     SetLength(Result, s+2);
     Result := '|' + chr(s) + n;
end;

const
     OneSecond = 0.000011574;

function NetBlockingHook: BOOL;
var
   TimeOut: double;
     //TimeOut = 30 * OneSecond;

begin
  if HookTimeOut > 0 then
     TimeOut := HookTimeOut * OneSecond
  else
      TimeOut := OneSecond / 20;
  Result := False;
  if NetCallPending then
     if Now > (NetTimerStart + TimeOut) then WSACancelBlockingCall;
end;

function TXWBWinsock.NetCall(hSocket: integer; imsg: string): PChar;
var
  BufSend, BufRecv, BufPtr: PChar;
  sBuf: string;
  OldTimeOut: integer;
  BytesRead, BytesLeft, BytesTotal: longint;
  TryNumber: Integer;
  BadXfer: Boolean;
  xString: String;
begin

  { -- clear receive buffer prior to sending rpc }
  if xFlush = True then begin
     OldTimeOut := HookTimeOut;
     HookTimeOut := 0;
     WSASetBlockingHook(@NetBlockingHook);
     NetCallPending := True;
     BufRecv := StrAlloc(Buffer32k);
     NetTimerStart := Now;
     BytesRead := recv(hSocket, BufRecv^, Buffer32k, 0);
     if BytesRead > 0 then
       while BufRecv[BytesRead-1] <> #4 do begin
         BytesRead := recv(hSocket, BufRecv^, Buffer32k, 0);
       end;
     StrDispose(BufRecv);
     xFlush := False;
     //Buf := nil;    //P14
     HookTimeOut := OldTimeOut;
  end;
  { -- provide variables for blocking hook }

  TryNumber := 0;
  BadXfer := True;


  { -- send message length + message to server }

  //BytesTotal := length(Prefix) + length(imsg) + 1 // p14
  //Buf := StrAlloc(BytesTotal);
  //Buf[0] := #0;

   if Prefix = '[XWB]' then
     BufSend := StrNew(PChar({Prefix +} imsg))  //;     //moved in P14
   else
     BufSend := StrNew(PChar({Prefix +} imsg));
  BufRecv := StrAlloc(Buffer32k);
  Result := PChar('');
//  try
  while BadXfer and (TryNumber < 4) do
  begin
    NetCallPending := True;
    NetTimerStart := Now;
    TryNumber := TryNumber + 1;
    BadXfer := False;
   {Clipboard.SetTextBuf(buf);
    ShowMessage('In Clipboard');}
    SocketError := send(hSocket, BufSend^, StrLen(BufSend), 0);
    if SocketError = SOCKET_ERROR then
      NetError('send', 0);
{
  finally
    StrDispose(Buf);
    //Buf := nil;     //P14
  end;
}
    BufRecv[0] := #0;
    try
      BufPtr := BufRecv;
      BytesLeft := Buffer32k;
      BytesTotal := 0;

      {Get Security and Application packets}
      SecuritySegment := GetServerPacket(hSocket);
      ApplicationSegment := GetServerPacket(hSocket);
      sBuf := '';
      { -- loop reading TCP buffer until server is finished sending reply }

      repeat
        BytesRead := recv(hSocket, BufPtr^, BytesLeft, 0);

        if BytesRead > 0 then begin
          if BufPtr[BytesRead-1] = #4 then
          begin
            sBuf := ConCat(sBuf, BufPtr);
          end
          else
          begin
            BufPtr[BytesRead] := #0;
            sBuf := ConCat(sBuf, BufPtr);
          end;
          Inc(BytesTotal, BytesRead);
        end;

        if BytesRead <= 0 then
        begin
          if BytesRead = SOCKET_ERROR then
            NetError('recv', 0)
          else
            NetError('connection lost', 0);
          break;
        end;
      until BufPtr[BytesRead-1] = #4;
      sBuf := Copy(sBuf, 1, BytesTotal - 1);
      StrDispose(BufRecv);
      BufRecv := StrAlloc(BytesTotal+1);   // cause of many memory leaks
      StrCopy(BufRecv, PChar(sBuf));
      Result := BufRecv;
      if ApplicationSegment = 'U411' then
        BadXfer := True;
      NetCallPending := False;
    finally
      sBuf := '';
    end;
  end;

  if BadXfer then
  begin
    StrDispose(BufRecv);
    NetError(StrPas('Repeated Incomplete Reads on the server'), XWB_BadReads);
    Result := StrNew('');
  end;

  { -- if there was on error on the server, display the error code }

  if Result[0] = #24 then
  begin
    xString := StrPas(@Result[1]);
    StrDispose(BufRecv);
    NetError(xString, XWB_M_REJECT);
//    NetCall := #0;
    Result := StrNew('');
  end;
end;

function TXWBWinsock.tCall(hSocket: integer; api, apVer: String; Parameters: TParams;
         var Sec , App: PChar; TimeOut: integer ): PChar;
var
 tmp: string;
 ChangeCursor: Boolean;
begin
     HookTimeOut := TimeOut;
     if (string(Api) <> 'XWB IM HERE') and (Screen.Cursor = crDefault) then
       ChangeCursor  := True
     else
       ChangeCursor := False;
     if ChangeCursor then
       Screen.Cursor := crHourGlass;  //P6

     if Prefix = '[XWB]' then
       tmp := BuildPar(hSocket, api, apVer, Parameters)
     else
       tmp := BuildPar1(hSocket, api, apVer, Parameters);

//     xFlush := True;     // Have it clear input buffers prior to call
     Result := NetCall(hSocket, tmp);
     StrPCopy(Sec, SecuritySegment);
     StrPCopy(App, ApplicationSegment);
     if ChangeCursor then
       Screen.Cursor := crDefault;
end;


function TXWBWinsock.NetStart (ForegroundM: boolean; Server: string;
                   ListenerPort: integer; var hSocket: integer): integer;
Var
  WinSockData: TWSADATA;
  LocalHost, DHCPHost: TSockAddr;
  LocalName, workstation, pDHCPName: string;
  y, tmp, upArrow, rAccept, rLost: string;
  tmpPchar: PChar;
  pLocalname: array [0..255] of char;
  r: integer;
  HostBuf,DHCPBuf: PHostEnt;
  lin: TLinger;
  s_lin: array [0..3] of char absolute lin;
  ChangeCursor: Boolean;
begin
{ ForegroundM is a boolean value, TRUE means the M handling process is
  running interactively a pointer rather than passing address length
  by value) }

    { -- initialize Windows Sockets API for this task }
    if Screen.Cursor = crDefault then
      ChangeCursor := True
    else
      ChangeCursor := False;
    if ChangeCursor then
      Screen.Cursor := crHourGlass;
    upArrow := string('^');
    rAccept := string('accept');
    rLost := string('(connection lost)');

    SocketError := WSAStartup(WINSOCK1_1, WinSockData);
    If SocketError >0 Then
            NetError( 'WSAStartup',0);

    { -- set up a hook for blocking calls so there is no automatic DoEvents
     in the background }
    NetCallPending := False;
    if ForeGroundM = False then if WSASetBlockingHook(@NetBlockingHook) = nil
       then NetError('WSASetBlockingHook',0);

    { -- establish HostEnt and Address structure for local machine}
    SocketError := gethostname(pLocalName, 255); { -- name of local system}
    If SocketError >0 Then
       NetError ('gethostname (local)',0);
    HostBuf := gethostbyname(pLocalName); { -- info for local name}
    If HostBuf = nil Then
       NetError( 'gethostbyname',0);
    LocalHost.sin_addr.S_addr := longint(plongint(HostBuf^.h_addr_list^)^);
    LocalName := inet_ntoa(LocalHost.sin_addr);
    workstation := string(HostBuf.h_name);

    { -- establish HostEnt and Address structure for remote machine }
    if inet_addr(PChar(Server)) <> longint(INADDR_NONE) then
    begin
      DHCPHost.sin_addr.S_addr := inet_addr(PChar(Server));
      DHCPBuf := gethostbyaddr(@DHCPHost.sin_addr.S_addr,sizeof(DHCPHost),PF_INET);
    end
    else
        DHCPBuf := gethostbyname(PChar(Server)); { --  info for DHCP system}

    If DHCPBuf = nil Then
    begin
        { modification to take care of problems with 10-dot addresses that weren't registered - solution found by Shawn Hardenbrook }
//            NetError ('Error Identifying Remote Host ' + Server,0);
//            NetStart := 10001;
//            exit;
      DHCPHost.sin_addr.S_addr := inet_addr(PChar(Server));
      pDHCPName := 'UNKNOWN';
    end
    else
    begin;
      DHCPHost.sin_addr.S_addr := longint(plongint(DHCPBuf^.h_addr_list^)^);
      pDHCPName := inet_ntoa(DHCPHost.sin_addr);
    end;
    DHCPHost.sin_family := PF_INET;                 { -- internet address type}
    DHCPHost.sin_port := htons(ListenerPort);        { -- port to connect to}

    { -- make connection to DHCP }
    hSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    If hSocket = INVALID_SOCKET Then
            NetError( 'socket',0);

    SocketError := connect(hSocket, DHCPHost, SizeOf(DHCPHost));
    If SocketError = SOCKET_ERROR Then
       NetError( 'connect',0);
    HookTimeOut := 30;

       { -- remove setup of hSocketListen

//    establish local IP now that connection is done
    AddrLen := SizeOf(LocalHost);
    SocketError := getsockname(hSocket, LocalHost, AddrLen);
    if SocketError = SOCKET_ERROR then
       NetError ('getsockname',0);
    LocalName := inet_ntoa(LocalHost.sin_addr);

//   -- set up listening socket for DHCP return connect
    hSocketListen := socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // --  new socket
    If hSocketListen = INVALID_SOCKET Then
      NetError ('socket (listening)',0);

    LocalHost.sin_family := PF_INET;            // -- internet address type
    LocalHost.sin_port := 0;                    // -- local listening port
    SocketError := bind(hSocketListen, LocalHost,
                SizeOf(LocalHost)); // -- bind socket to address
    If SocketError = SOCKET_ERROR Then
      NetError( 'bind',0);

    AddrLen := sizeof(LocalHost);
    SocketError := getsockname(hSocketListen, LocalHost,
                AddrLen);  // -- get listening port #
    If SocketError = SOCKET_ERROR Then
       NetError( 'getsockname',0);
    LocalPort := ntohs(LocalHost.sin_port);    // -- put in proper byte order

    SocketError := listen(hSocketListen, 1);   // -- put socket in listen mode
    If SocketError = SOCKET_ERROR Then
            NetError( 'listen',0);
}
    { -- send IP address + port + workstation name and wait for OK : eg 1-30-97}
{
    RPCVersion := VarPack(BrokerVer);              //   eg 11-1-96
    x := string('TCPconnect^');
    x := ConCat(x, LocalName, upArrow);            //   local ip address
    t := IntToStr(LocalPort);                         // callback port
    x := ConCat(x, t, upArrow, workstation, upArrow); // workstation name
    r := length(x) + length(RPCVersion) + 5;
    t := string('00000') + IntToStr(r);               // eg 11-1-96
    y := Copy(t, length(t)-4,length(t));
    y := ConCat(y, RPCVersion, StrPack(x,5));         // rpc version
}
    { new protocol 030107 }

//    y := '[XWB]10' +IntToStr(CountWidth)+ '0' + '4'+#$A+'TCPConnect50'+ LPack(LocalName,CountWidth)+'f0'+LPack(IntToStr(LocalPort),CountWidth)+'f0'+LPack(workstation,CountWidth)+'f'+#4;
    y := Prefix + '10' +IntToStr(CountWidth)+ '0' + '4'+#$A +'TCPConnect50'+ LPack(LocalName,CountWidth)+'f0'+LPack(IntToStr(0),CountWidth)+'f0'+LPack(workstation,CountWidth)+'f'+#4;

{  // need to remove selecting port etc from client, since it will now be handled on the server P36

    if ForeGroundM = True then
    begin
         if ChangeCursor then
           Screen.Cursor := crDefault;
         t := 'Start M job D EN^XWBTCP' + #13 + #10 + 'Addr = ' +
           LocalName + #13 + #10 + 'Port = ' + IntToStr(LocalPort);

         frmDebugInfo := TfrmDebugInfo.Create(Application.MainForm);
         try
           frmDebugInfo.lblDebugInfo.Caption := t;
           ShowApplicationAndFocusOK(Application);
           frmDebugInfo.ShowModal;
         finally
           frmDebugInfo.Free
         end;

//         ShowMessage(t);  //TODO
    end;
}  // remove debug mode from client

    tmpPChar := NetCall(hSocket, PChar(y));                {eg 11-1-96}
    tmp := tmpPchar;
    StrDispose(tmpPchar);
    if CompareStr(tmp, rlost) = 0 then
       begin
            lin.l_onoff := 1;
            lin.l_linger := 0;

            SocketError := setsockopt(hSocket, SOL_SOCKET, SO_LINGER,
                        s_lin, sizeof(lin));
            If SocketError = SOCKET_ERROR Then
               NetError( 'setsockopt (connect)',0);

          closesocket(hSocket);
          WSACleanup;
          Result := 10002;
          exit;
       end;
    r := CompareStr(tmp, rAccept);
    If r <> 0 Then
       NetError ('NetCall',XWB_M_REJECT);
{  // JLI 021217 remove disconnect and reconnect code -- use UCX connection directly.
    lin.l_onoff := 1;
    lin.l_linger := 0;

    SocketError := setsockopt(hSocket, SOL_SOCKET, SO_LINGER,
                s_lin, sizeof(lin));
    If SocketError = SOCKET_ERROR Then
       NetError( 'setsockopt (connect)',0);
    SocketError := closesocket(hSocket);          { -- done with this socket
    If SocketError > 0 Then
            NetError( 'closesocket',0);

    { -- wait for connect from DHCP and accept it - (uses blocking call)
    AddrLen := SizeOf(DHCPHost);
    hSocket := accept(hSocketListen, @DHCPHost, @AddrLen);{ -- returns new socket
    If hSocket = INVALID_SOCKET Then
       begin
            NetError( 'accept',0);
       end;

    lin.l_onoff := 1;
    lin.l_linger := 0;

    SocketError := setsockopt(hSocketListen, SOL_SOCKET, SO_LINGER,
                s_lin, sizeof(lin));
    If SocketError = SOCKET_ERROR Then
       NetError( 'setsockopt (connect)',0);

    SocketError := closesocket(hSocketListen);   // -- done with listen skt

    If SocketError > 0 Then
       begin
            NetError ('closesocket (listening)',0);
       end;
}             // JLI 12/17/02  end of section commented out

    if ChangeCursor then
      Screen.Cursor := crDefault;
    NetStart := 0;
{ -- connection established, socket handle now in:  hSocket
        ifrmWinSock.txtStatus := 'socket obtained' *** }
end;

function TXWBWinsock.NetStart1(ForegroundM: boolean; Server: string;
    ListenerPort: integer; var hSocket: integer): Integer;
Var
  WinSockData: TWSADATA;
  LocalHost, DHCPHost: TSockAddr;
  LocalName, t, workstation, pDHCPName: string;
  x, y, tmp,RPCVersion, upArrow, rAccept, rLost: string;
  tmpPchar: PChar;
  pLocalname: array [0..255] of char;
  LocalPort, AddrLen, hSocketListen,r: integer;
  HostBuf,DHCPBuf: PHostEnt;
  lin: TLinger;
  s_lin: array [0..3] of char absolute lin;
  ChangeCursor: Boolean;
begin
  Prefix := '{XWB}';
{ ForegroundM is a boolean value, TRUE means the M handling process is
  running interactively a pointer rather than passing address length
  by value) }

    { -- initialize Windows Sockets API for this task }
    if Screen.Cursor = crDefault then
      ChangeCursor := True
    else
      ChangeCursor := False;
    if ChangeCursor then
      Screen.Cursor := crHourGlass;
    upArrow := string('^');
    rAccept := string('accept');
    rLost := string('(connection lost)');

    SocketError := WSAStartup(WINSOCK1_1, WinSockData);
    If SocketError >0 Then
            NetError( 'WSAStartup',0);

    { -- set up a hook for blocking calls so there is no automatic DoEvents
     in the background }
    NetCallPending := False;
    if ForeGroundM = False then if WSASetBlockingHook(@NetBlockingHook) = nil
       then NetError('WSASetBlockingHook',0);

    { -- establish HostEnt and Address structure for local machine}
    SocketError := gethostname(pLocalName, 255); { -- name of local system}
    If SocketError >0 Then
       NetError ('gethostname (local)',0);
    HostBuf := gethostbyname(pLocalName); { -- info for local name}
    If HostBuf = nil Then
       NetError( 'gethostbyname',0);
    LocalHost.sin_addr.S_addr := longint(plongint(HostBuf^.h_addr_list^)^);
    LocalName := inet_ntoa(LocalHost.sin_addr);
    workstation := string(HostBuf.h_name);

    { -- establish HostEnt and Address structure for remote machine }
    if inet_addr(PChar(Server)) <> longint(INADDR_NONE) then
    begin
      DHCPHost.sin_addr.S_addr := inet_addr(PChar(Server));
      DHCPBuf := gethostbyaddr(@DHCPHost.sin_addr.S_addr,sizeof(DHCPHost),PF_INET);
    end
    else
        DHCPBuf := gethostbyname(PChar(Server)); { --  info for DHCP system}

    If DHCPBuf = nil Then
    begin
        { modification to take care of problems with 10-dot addresses that weren't registered - solution found by Shawn Hardenbrook }
//            NetError ('Error Identifying Remote Host ' + Server,0);
//            NetStart := 10001;
//            exit;
      DHCPHost.sin_addr.S_addr := inet_addr(PChar(Server));
      pDHCPName := 'UNKNOWN';
    end
    else
    begin;
      DHCPHost.sin_addr.S_addr := longint(plongint(DHCPBuf^.h_addr_list^)^);
      pDHCPName := inet_ntoa(DHCPHost.sin_addr);
    end;
    DHCPHost.sin_family := PF_INET;                 { -- internet address type}
    DHCPHost.sin_port := htons(ListenerPort);        { -- port to connect to}

    { -- make connection to DHCP }
    hSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    If hSocket = INVALID_SOCKET Then
            NetError( 'socket',0);

    SocketError := connect(hSocket, DHCPHost, SizeOf(DHCPHost));
    If SocketError = SOCKET_ERROR Then
       NetError( 'connect',0);

    {establish local IP now that connection is done}
    AddrLen := SizeOf(LocalHost);
    SocketError := getsockname(hSocket, LocalHost, AddrLen);
    if SocketError = SOCKET_ERROR then
       NetError ('getsockname',0);
    LocalName := inet_ntoa(LocalHost.sin_addr);

//    { -- set up listening socket for DHCP return connect }
    hSocketListen := socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // --  new socket
    If hSocketListen = INVALID_SOCKET Then
      NetError ('socket (listening)',0);

    LocalHost.sin_family := PF_INET;            // -- internet address type
    LocalHost.sin_port := 0;                    // -- local listening port
    SocketError := bind(hSocketListen, LocalHost,
                SizeOf(LocalHost)); // -- bind socket to address
    If SocketError = SOCKET_ERROR Then
      NetError( 'bind',0);

    AddrLen := sizeof(LocalHost);
    SocketError := getsockname(hSocketListen, LocalHost,
                AddrLen);  // -- get listening port #
    If SocketError = SOCKET_ERROR Then
       NetError( 'getsockname',0);
    LocalPort := ntohs(LocalHost.sin_port);    // -- put in proper byte order

    SocketError := listen(hSocketListen, 1);   // -- put socket in listen mode
    If SocketError = SOCKET_ERROR Then
            NetError( 'listen',0);

    { -- send IP address + port + workstation name and wait for OK : eg 1-30-97}

    RPCVersion := VarPack(BrokerVer);              //   eg 11-1-96
    x := string('TCPconnect^');
    x := ConCat(x, LocalName, upArrow);            //   local ip address
    t := IntToStr(LocalPort);                         // callback port
    x := ConCat(x, t, upArrow, workstation, upArrow); // workstation name
    r := length(x) + length(RPCVersion) + 5;
    t := string('00000') + IntToStr(r);               // eg 11-1-96
    y := Copy(t, length(t)-4,length(t));
    y := ConCat(y, RPCVersion, StrPack(x,5));         // rpc version
    y := Prefix + y;
    { new protocol 030107 }

//    y := '[XWB]10' +IntToStr(CountWidth)+ '0' + '4'+#$A+'TCPConnect50'+ LPack(LocalName,CountWidth)+'f0'+LPack(IntToStr(LocalPort),CountWidth)+'f0'+LPack(workstation,CountWidth)+'f'+#4;
//    y := '[XWB]10' +IntToStr(CountWidth)+ '0' + '4'+#$A+'TCPConnect50'+ LPack(LocalName,CountWidth)+'f0'+LPack(IntToStr(0),CountWidth)+'f0'+LPack(workstation,CountWidth)+'f'+#4;

  // need to remove selecting port etc from client, since it will now be handled on the server P36

    if ForeGroundM = True then
    begin
         if ChangeCursor then
           Screen.Cursor := crDefault;
         t := 'Start M job D EN^XWBTCP' + #13 + #10 + 'Addr = ' +
           LocalName + #13 + #10 + 'Port = ' + IntToStr(LocalPort);

         frmDebugInfo := TfrmDebugInfo.Create(Application.MainForm);
         try
           frmDebugInfo.lblDebugInfo.Caption := t;
           ShowApplicationAndFocusOK(Application);
           frmDebugInfo.ShowModal;
         finally
           frmDebugInfo.Free
         end;

//         ShowMessage(t);  //TODO
    end;
  // remove debug mode from client

    tmpPChar := NetCall(hSocket, PChar(y));                {eg 11-1-96}
    tmp := tmpPchar;
    StrDispose(tmpPchar);
    if CompareStr(tmp, rlost) = 0 then
       begin
            lin.l_onoff := 1;
            lin.l_linger := 0;

            SocketError := setsockopt(hSocket, SOL_SOCKET, SO_LINGER,
                        s_lin, sizeof(lin));
            If SocketError = SOCKET_ERROR Then
               NetError( 'setsockopt (connect)',0);

          closesocket(hSocket);
          WSACleanup;
          Result := 10002;
          exit;
       end;
    r := CompareStr(tmp, rAccept);
    If r <> 0 Then
       NetError ('NetCall',XWB_M_REJECT);
  // JLI 021217 remove disconnect and reconnect code -- use UCX connection directly.
    lin.l_onoff := 1;
    lin.l_linger := 0;

    SocketError := setsockopt(hSocket, SOL_SOCKET, SO_LINGER,
                s_lin, sizeof(lin));
    If SocketError = SOCKET_ERROR Then
       NetError( 'setsockopt (connect)',0);
    SocketError := closesocket(hSocket);          // -- done with this socket
    If SocketError > 0 Then
            NetError( 'closesocket',0);

    // -- wait for connect from DHCP and accept it - (uses blocking call)
    AddrLen := SizeOf(DHCPHost);
    hSocket := accept(hSocketListen, @DHCPHost, @AddrLen); // -- returns new socket
    If hSocket = INVALID_SOCKET Then
       begin
            NetError( 'accept',0);
       end;

    lin.l_onoff := 1;
    lin.l_linger := 0;

    SocketError := setsockopt(hSocketListen, SOL_SOCKET, SO_LINGER,
                s_lin, sizeof(lin));
    If SocketError = SOCKET_ERROR Then
       NetError( 'setsockopt (connect)',0);

    SocketError := closesocket(hSocketListen);   // -- done with listen skt

    If SocketError > 0 Then
       begin
            NetError ('closesocket (listening)',0);
       end;
             // JLI 12/17/02  end of section commented out

    if ChangeCursor then
      Screen.Cursor := crDefault;
    NetStart1 := 0;
{ -- connection established, socket handle now in:  hSocket
        ifrmWinSock.txtStatus := 'socket obtained' *** }
end;


procedure TXWBWinsock.NetStop(hSocket: integer);
Var
  tmp: string;
  lin: TLinger;
  s_lin: array [0..3] of char absolute lin;
  ChangeCursor: Boolean;
  tmpPChar: PChar;
  Str: String;
  x: array [0..15] of Char;
begin
    if not IsConnected then exit;
    if Screen.Cursor = crDefault then
      ChangeCursor := True
    else
      ChangeCursor := False;
    if ChangeCursor then
      Screen.Cursor := crHourGlass;
    if hSocket <= 0 then
    begin
         if ChangeCursor then
           screen.cursor := crDefault;
         exit;
    end;

    StrPcopy(x, StrPack(StrPack('#BYE#',5),5));

    { convert to new message protocol 030107 }
    if Prefix = '[XWB]' then
      Str := Prefix + '10'+IntToStr(CountWidth)+'0' +'4'+#5+'#BYE#'+#4
    else
      Str := Prefix + x;
    If hSocket <> INVALID_SOCKET Then
    begin
      tmpPChar := NetCall(hSocket,Str);
//   	  tmpPChar := NetCall(hSocket, x);
 	  tmp := tmpPChar;
      StrDispose(tmpPChar);
        lin.l_onoff := 1;                    { -- shut down the M handler};
        lin.l_linger := 0;

        SocketError := setsockopt(hSocket, SOL_SOCKET, SO_LINGER,
                    s_lin, sizeof(lin));
        If SocketError = SOCKET_ERROR Then
           NetError( 'setsockopt (connect)',0);

	SocketError := closesocket(hSocket);  { -- close the socket}
    end;

    SocketError := WSAUnhookBlockingHook;     { -- restore the default mechanism}
    SocketError := WSACleanup;                { -- shutdown TCP API}
    If SocketError > 0 Then
       NetError( 'WSACleanup',0);             { -- check blocking calls, etc.}
    if ChangeCursor then
      Screen.Cursor := crDefault;
    IsConnected := False;
end;


procedure TXWBWinsock.CloseSockSystem(hSocket: integer; s: string);
var
   lin: TLinger;
   s_lin: array [0..3] of char absolute lin;
begin
     lin.l_onoff := 1;
     lin.l_linger := 0;

     SocketError := setsockopt(hSocket, SOL_SOCKET, SO_LINGER,
                 s_lin, sizeof(lin));
     If SocketError = SOCKET_ERROR Then
        NetError( 'setsockopt (connect)',0);

     closesocket(hSocket);
     WSACleanup;
     ShowMessage(s);  //TODO
     halt(1);
end;

function TXWBWinsock.GetServerPacket(hSocket: integer): string;
var
   s,sb: PChar;
   buflen: integer;
begin
     s := StrAlloc(1);
     s[0] := #0;
     buflen := recv(hSocket, s^, 1, 0); {get length of segment}
     if buflen = SOCKET_ERROR Then   // 040720 code added to check for the timing problem if initial attempt to read during connection fails
     begin
       sleep(100);
       buflen := recv(hSocket, s^, 1, 0);
     end;
     if buflen = SOCKET_ERROR then
       NetError( 'recv',0);
     buflen := ord(s[0]);
     sb := StrAlloc(buflen+1);
     sb[0] := #0;
     buflen := recv(hSocket, sb^, buflen, 0); {get security segment}
     if buflen = SOCKET_ERROR Then
        NetError( 'recv',0);
     sb[buflen] := #0;
     Result := StrPas(sb);
     StrDispose(sb);
     StrDispose(s);
end;

constructor TXWBWinsock.Create;
begin
  inherited;
//  NetBlockingHookVar := NetBlockingHook;
  CountWidth := 3;
end;

procedure TXWBWinsock.NetError(Action: string; ErrType: integer);
var
   x,s: string;
   r: integer;
   BrokerError: EBrokerError;
   TimeOut: Double;
begin
   Screen.Cursor := crDefault;
   r := 0;
   if ErrType > 0 then r := ErrType;
   if ErrType = 0 then
  begin
        // P36
        // code added to indicate WSAETIMEDOUT error instead of WSAEINTR
        // when time out period exceeded.  WSAEINTR error is misleading
        // since the server is still active, but took too long
        if NetcallPending then
        begin
          if HookTimeOut > 0 then
          begin
            TimeOut := HookTimeOut * OneSecond;
            if Now > (NetTimerStart + TimeOut) then
              r := WSAETIMEDOUT;
          end;
        end;
        if r = 0 then
          r := WSAGetLastError;
        if (r = WSAEINTR) or (r = WSAETIMEDOUT) then xFlush := True;
        if WSAIsBlocking = True then WSACancelBlockingCall;  // JLI 021210
  end;
  Case r of
        WSAEINTR           : x := 'WSAEINTR';
        WSAEBADF           : x := 'WSAEINTR';
        WSAEFAULT          : x := 'WSAEFAULT';
        WSAEINVAL          : x := 'WSAEINVAL';
        WSAEMFILE          : x := 'WSAEMFILE';
        WSAEWOULDBLOCK     : x := 'WSAEWOULDBLOCK';
        WSAEINPROGRESS     : x := 'WSAEINPROGRESS';
        WSAEALREADY        : x := 'WSAEALREADY';
        WSAENOTSOCK        : x := 'WSAENOTSOCK';
        WSAEDESTADDRREQ    : x := 'WSAEDESTADDRREQ';
        WSAEMSGSIZE        : x := 'WSAEMSGSIZE';
        WSAEPROTOTYPE      : x := 'WSAEPROTOTYPE';
        WSAENOPROTOOPT     : x := 'WSAENOPROTOOPT';
        WSAEPROTONOSUPPORT : x := 'WSAEPROTONOSUPPORT';
        WSAESOCKTNOSUPPORT : x := 'WSAESOCKTNOSUPPORT';
        WSAEOPNOTSUPP      : x := 'WSAEOPNOTSUPP';
        WSAEPFNOSUPPORT    : x := 'WSAEPFNOSUPPORT';
        WSAEAFNOSUPPORT    : x := 'WSAEAFNOSUPPORT';
        WSAEADDRINUSE      : x := 'WSAEADDRINUSE';
        WSAEADDRNOTAVAIL   : x := 'WSAEADDRNOTAVAIL';
        WSAENETDOWN        : x := 'WSAENETDOWN';
        WSAENETUNREACH     : x := 'WSAENETUNREACH';
        WSAENETRESET       : x := 'WSAENETRESET';
        WSAECONNABORTED    : x := 'WSAECONNABORTED';
        WSAECONNRESET      : x := 'WSAECONNRESET';
        WSAENOBUFS         : x := 'WSAENOBUFS';
        WSAEISCONN         : x := 'WSAEISCONN';
        WSAENOTCONN        : x := 'WSAENOTCONN';
        WSAESHUTDOWN       : x := 'WSAESHUTDOWN';
        WSAETOOMANYREFS    : x := 'WSAETOOMANYREFS';
        WSAETIMEDOUT       : x := 'WSAETIMEDOUT';
        WSAECONNREFUSED    : x := 'WSAECONNREFUSED';
        WSAELOOP           : x := 'WSAELOOP';
        WSAENAMETOOLONG    : x := 'WSAENAMETOOLONG';
        WSAEHOSTDOWN       : x := 'WSAEHOSTDOWN';
        WSAEHOSTUNREACH    : x := 'WSAEHOSTUNREACH';
        WSAENOTEMPTY       : x := 'WSAENOTEMPTY';
        WSAEPROCLIM        : x := 'WSAEPROCLIM';
        WSAEUSERS          : x := 'WSAEUSERS';
        WSAEDQUOT          : x := 'WSAEDQUOT';
        WSAESTALE          : x := 'WSAESTALE';
        WSAEREMOTE         : x := 'WSAEREMOTE';
        WSASYSNOTREADY     : x := 'WSASYSNOTREADY';
        WSAVERNOTSUPPORTED : x := 'WSAVERNOTSUPPORTED';
        WSANOTINITIALISED  : x := 'WSANOTINITIALISED';
        WSAHOST_NOT_FOUND  : x := 'WSAHOST_NOT_FOUND';
        WSATRY_AGAIN       : x := 'WSATRY_AGAIN';
        WSANO_RECOVERY     : x := 'WSANO_RECOVERY';
        WSANO_DATA         : x := 'WSANO_DATA';

        XWB_NO_HEAP        : x := 'Insufficient Heap';
        XWB_M_REJECT       : x := 'M Error - Use ^XTER';
        XWB_BadReads       : x := 'Server unable to read input data correctly.';
        XWB_BadSignOn      : x := 'Sign-on was not completed.';
        XWB_ExeNoMem       : x := 'System was out of memory, executable file was corrupt, or relocations were invalid.';
        XWB_ExeNoFile      : x := 'File was not found.';
        XWB_ExeNoPath      : x := 'Path was not found.';
        XWB_ExeShare       : x := 'Attempt was made to dynamically link to a task,' +
                                  ' or there was a sharing or network-protection error.';
        XWB_ExeSepSeg      : x := 'Library required separate data segments for each task.';
        XWB_ExeLoMem       : x := 'There was insufficient memory to start the application.';
        XWB_ExeWinVer      : x := 'Windows version was incorrect.';
        XWB_ExeBadExe      : x := 'Executable file was invalid.' +
                                  ' Either it was not a Windows application or there was an error in the .EXE image.';
        XWB_ExeDifOS       : x := 'Application was designed for a different operating system.';
        XWB_RpcNotReg      : X := 'Remote procedure not registered to application.';
        XWB_BldConnectList : x := 'BrokerConnections list could not be created';
        XWB_NullRpcVer     : x := 'RpcVersion cannot be empty.' + #13 + 'Default is 0 (zero).';
        else x := IntToStr(r);
  end;
  s := 'Error encountered.' + chr(13)+chr(10) + 'Function was: ' + Action + chr(13)+chr(10) + 'Error was: ' + x;
  BrokerError := EBrokerError.Create(s);
  BrokerError.Action := Action;
  BrokerError.Code := r;
  BrokerError.Mnemonic := x;
  raise BrokerError;
end;

function TXWBWinsock.BuildPar1(hSocket: integer; api, RPCVer: string; const
    Parameters: TParams): String;
var
  i,ParamCount: integer;
  num: integer;
  tsize: longint;
  arr: LongInt;
  param,x,hdr,strout: string;
  tout,psize,tResult,RPCVersion: string;
  sin: TStringList;
  subscript: string;
begin
  sin := TStringList.Create;
  sin.clear;
  x := '';
  param := '';
  arr := 0;
  if Parameters = nil then ParamCount := 0
  else ParamCount := Parameters.Count;
  for i := 0 to ParamCount - 1 do
    if Parameters[i].PType <> undefined then begin
      with Parameters[i] do begin

        {if PType= null then
          param:='';}

        if PType = literal then
          param := param + strpack('0' + Value,3);

        if PType = reference then
          param := param + strpack('1' + Value,3);

        if (PType = list) {or (PType = wordproc)} then begin
          Value := '.x';
          param := param + strpack('2' + Value,3);
          if Pos('.',Value) >0 then
            x := Copy(Value,2,length(Value));
            {if PType = wordproc then dec(last);}
            subscript := Mult.First;
            while subscript <> '' do begin
              if Mult[subscript] = '' then Mult[subscript] := #1;
              sin.Add(StrPack(subscript,3) + StrPack(Mult[subscript],3));
              subscript := Mult.Order(subscript,1);
            end{while};
            sin.Add('000');
            arr := 1;
        end{if};
      end{with};
    end{if};

  param := Copy(param,1,Length(param));
  tsize := 0;

  tResult := '';
  tout := '';

  hdr := BuildHdr('XWB','','','');
  strout := strpack(hdr + BuildApi(api,param,arr),5);
//  num :=0;   //  JLI 040608 to correct handling of empty arrays

  RPCVersion := '';
  RPCVersion := VarPack(RPCVer);

  {if sin.Count-1 > 0 then} num := sin.Count-1;   //  JLI 040608 to correct handling of empty arrays
//  if sin.Count-1 > 0 then num := sin.Count-1;
    

  if {num} sin.Count > 0 then     //  JLI 040608 to correct handling of empty arrays
//  if num > 0 then
  begin
        for i := 0 to num do
          tsize := tsize + length(sin.strings[i]);
        x := '00000' + IntToStr(tsize + length(strout)+ length(RPCVersion));
  end;
  if {num} sin.Count = 0 then   //  JLI 040608 to correct handling of empty arrays
//   if num = 0 then
   begin
        x := '00000' + IntToStr(length(strout)+ length(RPCVersion));
   end;

  psize := x;
  psize := Copy(psize,length(psize)-5,5);
  tResult := psize;
  tResult := ConCat(tResult, RPCVersion);
  tout := strout;
  tResult := ConCat(tResult, tout);

  if {num} sin.Count > 0 then   //  JLI 040608 to correct handling of empty arrays
//   if num > 0 then
   begin
        for i := 0 to num do
            tResult := ConCat(tResult, sin.strings[i]);
   end;

  sin.free;

  Result := Prefix + tResult;  {return result}

end;

end.



