MSEND.C
/* msend.c */ 
/* (C) COPYRIGHT DATA CONNECTION LIMITED 1994 */ 
 
/*****************************************************************************/ 
/* Change History                                                            */ 
/*                                                                           */ 
/*       21/02/94 DK  Created                                                */ 
/*       15/03/94 DK  Thread priorities added and signalled event goes to    */ 
/*                    back of the list (for WaitForMultipleObjects)          */ 
/*       14/04/94 DK  OutputResults removed                                  */ 
/*****************************************************************************/ 
 
/*****************************************************************************/ 
/* If you want internal tracing, #define SRTRC here                          */ 
/*****************************************************************************/ 
//#define SRTRC 
 
/*****************************************************************************/ 
/*                                                                           */ 
/* ROUTINE : SEND using event completion                                     */ 
/*                                                                           */ 
/* FUNCTION: This file contains the routines for a multi-threaded routine    */ 
/*           which uses asynchronous APPC calls with event completion        */ 
/*           to send data.                                                   */ 
/*                                                                           */ 
/*           It runs with either the single-threaded or the multi-threaded   */ 
/*           version of receive (mrcv or recvtp).                            */ 
/*                                                                           */ 
/* INPUTS  : MSEND.CFG (file) (documented below)                             */ 
/*                                                                           */ 
/* OUTPUTS : MSEND.OUT                                                       */ 
/*           MSEND.TRC                                                       */ 
/*                                                                           */ 
/*****************************************************************************/ 
 
/*****************************************************************************/ 
/* Operation:                                                                */ 
/*                                                                           */ 
/* This is a Windows NT application which runs in a minimized window.        */ 
/*                                                                           */ 
/* Thread structure:                                                         */ 
/*                                                                           */ 
/*   A variable number of send threads                                       */ 
/*     Each send thread processes a variable number of conversations and     */ 
/*     issues a WaitForMultipleObjects to wait for completion of any of its  */ 
/*     send operations.  Each conversation starts with TP_STARTED, followed  */ 
/*     by MC_ALLOCATE and MC_CONFIRM.  Then it issues MC_SEND_DATA verbs     */ 
/*     to send data.  MC_CONFIRMs are issued at configurable intervals.      */ 
/*     If a confirm fails, an attempt is made to restart the conversation    */ 
/*     after five seconds.                                                   */ 
/*                                                                           */ 
/*   Note:  this program is compatible with the single-threaded version of   */ 
/*   receive, which can be run for example on WIN16 clients.                 */ 
/*                                                                           */ 
/*****************************************************************************/ 
 
/*****************************************************************************/ 
/* Configuration file:                                                       */ 
/*                                                                           */ 
/* The configuration file is called MSEND.CFG and must reside in the         */ 
/* same directory as the program.  It contains the following, in any order.  */ 
/* If any parameter is omitted, the default is assumed.                      */ 
/*                                                                           */ 
/* TraceFile = <Name of file for tracing, default MSEND.TRC>                 */ 
/* RemoteTPName = <Name used for allocate, default MRCVTP>                   */ 
/* LocalLUAlias = <Alias for local LU, default SENDLU>                       */ 
/* RemoteLUAlias = <Alias for remote LU, default RECVLU>                     */ 
/* ModeName = <Mode Name, default #INTER>                                    */ 
/*                                                                           */ 
/* NumSendConvs = <Number of conversations to be sent, default = 4>          */ 
/* NumSends = <number of SEND_DATA verbs per conversation, default = 8>      */ 
/* ConfirmEvery = <number of SEND_DATA verbs between CONFIRMs, default = 2>  */ 
/* SendSize = <number of bytes per SEND_DATA, default = 256>                 */ 
/*                                                                           */ 
/* The name used for TP_STARTED is fixed at MSEND.                           */ 
/*                                                                           */ 
/* If NumSends is zero, the TP will never DEALLOCATE a conversation.         */ 
/* If ConfirmEvery is zero, the TP will not issue CONFIRM verbs except       */ 
/*    after the MC_ALLOCATE.                                                 */ 
/* If ConfirmEvery is non-zero, the TP issues a CONFIRM verbs ConfirmEvery   */ 
/*    sends.                                                                 */ 
/*                                                                           */ 
/* Configuration constants (in msend.h)                                      */ 
/*                                                                           */ 
/* #define MAX_SEND_PER_THREAD  Max conversations per send thread      (64)  */ 
/*                                                                           */ 
/* MAX_SEND_PER_THREAD is constrained by the limit of 64 objects that can be */ 
/* waited for in WaitForMultipleObjects.                                     */ 
/*                                                                           */ 
/*****************************************************************************/ 
 
#include <windows.h> 
HINSTANCE hInst; 
BOOL verbs_started = FALSE; 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <winappc.h> 
#include <wincsv.h> 
#include "msend.h" 
 
/*****************************************************************************/ 
/* Trace macros                                                              */ 
/*****************************************************************************/ 
#ifdef SRTRC 
#define SRTROPEN() t = fopen(TraceFile,"w"); 
#define SRTRFLUSH() fflush(t); 
#define SRTRCLOSE() fclose(t); 
#define SRTRACE fprintf 
#else 
#define SRTROPEN() 
#define SRTRFLUSH() 
#define SRTRCLOSE() 
#define SRTRACE 1 ? (void) 0 : fprintf 
#endif 
 
/*****************************************************************************/ 
/* WinMain - reads initialization info and controls message loop             */ 
/*           NT version                                                      */ 
/*****************************************************************************/ 
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                   LPSTR lpCmdLine, int nCmdShow) 
{ 
 
  MSG msg; 
  DWORD Tid; 
  int i; 
  DWORD NumConvs; 
  HANDLE ThreadHandle; 
 
  hInst = hInstance; 
 
  InitializeMain(); 
 
  if (!InitializeWinMain(hInstance)) 
  { 
    return (FALSE); 
  } 
 
  ReadConfig(); 
  SRTROPEN() 
 
  /***************************************************************************/ 
  /* Create enough send threads to process conversations                     */ 
  /***************************************************************************/ 
  i = NumSendConvs; 
  while (i > 0) 
  { 
    NumConvs = (i > 64) ? 64 : i; 
    ThreadHandle = CreateThread(NULL, 
                                16000, 
                                (LPTHREAD_START_ROUTINE)SendThread, 
                                (void *)NumConvs, 
                                0, 
                                &Tid); 
    if (ThreadHandle == NULL) 
    { 
      GetLastError(); 
      DebugBreak(); 
    } 
    SetThreadPriority(ThreadHandle,THREAD_PRIORITY_BELOW_NORMAL); 
    SRTRACE(t,"Created send thread with %d conversations and priority %d\n", 
            NumConvs,GetThreadPriority(ThreadHandle)); 
    CloseHandle(ThreadHandle); 
    i -= NumConvs; 
  } 
 
  /***************************************************************************/ 
  /* Windows processing loop                                                 */ 
  /***************************************************************************/ 
  while(GetMessage(&msg,NULL,0,0)) 
  { 
    TranslateMessage(&msg); 
    DispatchMessage(&msg); 
  } 
 
  WinAPPCCleanup(); 
  SRTRFLUSH() 
  SRTRCLOSE() 
  DeleteCriticalSection(&runsem); 
  return msg.wParam;         /* save exit parameter for return               */ 
 
} 
 
/*****************************************************************************/ 
/* SendThread - thread which processes multiple conversations                */ 
/*****************************************************************************/ 
DWORD WINAPI SendThread(DWORD NumConvs) 
{ 
  /***************************************************************************/ 
  /* Separate variables for each instance of this thread                     */ 
  /***************************************************************************/ 
  CONVCB * convptr; 
  struct appc_hdr * vcbptr; 
  unsigned short ThreadNo; 
  unsigned short SendThreadNo; 
  DWORD ObjIndex; 
  DWORD i; 
  DWORD j; 
  DWORD rc; 
  CONVCB * ConvptrArray [MAX_SEND_PER_THREAD+1]; 
  HANDLE EventArray [MAX_SEND_PER_THREAD+1]; 
  SYSTEMTIME st; 
 
  for (i = 0; i < MAX_SEND_PER_THREAD+1; i++) 
  { 
    ConvptrArray[i] = NULL; 
    EventArray[i] = NULL; 
  } 
 
  /***************************************************************************/ 
  /* Count threads within critical section                                   */ 
  /***************************************************************************/ 
  EnterCriticalSection(&runsem); 
  ThreadCount++; 
  SendThreads++; 
  ThreadNo = ThreadCount; 
  SendThreadNo = SendThreads; 
  OUTPUTNUMBER 
  LeaveCriticalSection(&runsem); 
 
  for (i = 0; i < NumConvs; i++) 
  { 
    /*************************************************************************/ 
    /* Make a conversation control block                                     */ 
    /*************************************************************************/ 
    convptr = malloc (sizeof(CONVCB)); 
    convptr->thread       = SendThreadNo; 
    convptr->conv         = (unsigned short)i; 
    convptr->async_corr   = 0; 
    convptr->TPid[0]      = '\0'; 
    convptr->Convid       = 0; 
    convptr->SendCount    = 0; 
    convptr->ConfirmCount = 0; 
    convptr->TPEnded      = FALSE; 
    convptr->Deallocated  = FALSE; 
    convptr->Counted      = FALSE; 
    convptr->SendSize     = SendSize; 
    convptr->DataPtr      = malloc(convptr->SendSize); 
    vcbptr = (struct appc_hdr *) &convptr->vcb; 
    memset(vcbptr,0,sizeof(VCB)); 
    strcpy (convptr->type,"send"); 
//  SRTRACE(t,"Thread %d:%d (send) has convptr %p\n", 
//          SendThreadNo,i,convptr); 
 
    /*************************************************************************/ 
    /* Create an event                                                       */ 
    /*************************************************************************/ 
    convptr->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); 
    SRTRACE(t,"Thread %d:%d (send) has event handle %p\n", 
            SendThreadNo,i,convptr->hEvent); 
 
    /*************************************************************************/ 
    /* Add to arrays                                                         */ 
    /*************************************************************************/ 
    ConvptrArray[i] = convptr; 
    EventArray[i]   = convptr->hEvent; 
 
    /*************************************************************************/ 
    /* Build a tp_started                                                    */ 
    /*************************************************************************/ 
    Build_TP_STARTED(convptr); 
    SRTRFLUSH() 
 
    /*************************************************************************/ 
    /*PERF* Don't re-generate data each time we issue a send!                */ 
    /*************************************************************************/ 
    GenerateData(convptr); 
 
    /*************************************************************************/ 
    /* Issue tp_started                                                      */ 
    /*************************************************************************/ 
    convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent, 
                                         (long)(char *)(vcbptr)); 
//  SRTRACE(t,"Thread %d:%d (send) tp_started issued corr %p\n", 
//          SendThreadNo,i,convptr->async_corr); 
    SRTRFLUSH() 
    if (convptr->async_corr == 0) 
    { 
      SRTRACE(t,"Thread %d:%d (send) WinAsync call %x failed corr %p\n", 
              SendThreadNo,i,vcbptr->opcode,convptr->async_corr); 
      convptr->TPEnded = TRUE; 
    } 
  } 
 
  /***************************************************************************/ 
  /* Loop round until finished                                               */ 
  /***************************************************************************/ 
  while (TRUE) 
  { 
    /*************************************************************************/ 
    /* Wait for event completion                                             */ 
    /*************************************************************************/ 
//  SRTRACE(t,"Thread %d (send) waiting for %d events to complete\n", 
//          SendThreadNo,NumConvs); 
    ObjIndex = WaitForMultipleObjects(NumConvs,EventArray,FALSE,INFINITE); 
    if (ObjIndex == WAIT_FAILED) 
    { 
      rc = GetLastError(); 
      SRTRACE(t,"Thread %d (send) wait for %d events failed with rc %d\n", 
              SendThreadNo,NumConvs,rc); 
      for (j = 0; j < NumConvs; j++) 
      { 
        SRTRACE(t,"Thread %d (send) event %d has handle %p\n", 
                SendThreadNo,j,EventArray[j]); 
      } 
      break; 
    } 
 
    /*************************************************************************/ 
    /* Get index to conversation array                                       */ 
    /*************************************************************************/ 
    i = ObjIndex - WAIT_OBJECT_0; 
//  SRTRACE(t,"Thread %d:%d (send) event %d has completed\n", 
//          SendThreadNo,ConvptrArray[i]->conv,i); 
 
    /*************************************************************************/ 
    /* Issue the next verb                                                   */ 
    /*************************************************************************/ 
//  SRTRACE(t,"Thread %d:%d (send) issuing next send verb\n", 
//          SendThreadNo,ConvptrArray[i]->conv); 
    ConvptrArray[i]->TPEnded = IssueSendVerb(ConvptrArray[i]); 
 
    if (ConvptrArray[i]->TPEnded) 
    { 
      /***********************************************************************/ 
      /* end of conversation                                                 */ 
      /***********************************************************************/ 
      NumConvs--; 
      SRTRACE(t,"Thread %d:%d (send) conversation completed: NumConvs %d\n", 
              SendThreadNo,ConvptrArray[i]->conv,NumConvs); 
      EnterCriticalSection(&runsem); 
      SendConvs++; 
      OUTPUTNUMBER 
      SRTRACE(t,"Thread %d:%d (send) NumConvs %d SendConvs %d\n", 
              SendThreadNo,ConvptrArray[i]->conv,NumConvs,SendConvs); 
      LeaveCriticalSection(&runsem); 
 
      /***********************************************************************/ 
      /* free resources                                                      */ 
      /***********************************************************************/ 
      CloseHandle(ConvptrArray[i]->hEvent); 
      free (ConvptrArray[i]->DataPtr); 
      free (ConvptrArray[i]); 
      ConvptrArray[i] = NULL; 
      EventArray[i]   = NULL; 
 
      /***********************************************************************/ 
      /* if all conversations have completed, end loop                       */ 
      /***********************************************************************/ 
      if (NumConvs == 0) 
      { 
        break; 
      } 
      /***********************************************************************/ 
      /* the event array cannot have holes in it, so shuffle up the          */ 
      /* pointers and events                                                 */ 
      /* note that this means convptr->conv no longer matches i              */ 
      /* which is why tracing uses ConvptrArray[i]->conv                     */ 
      /***********************************************************************/ 
      for (j = i; j < MAX_SEND_PER_THREAD - 1; j++) 
      { 
        ConvptrArray [j] = ConvptrArray [j+1]; 
        EventArray [j] = EventArray [j+1]; 
      } 
      ConvptrArray [MAX_SEND_PER_THREAD-1] = NULL; 
      EventArray [MAX_SEND_PER_THREAD-1] = NULL; 
    } 
    else 
    { 
      /***********************************************************************/ 
      /* to stop one conversation getting an unfair share of the time,       */ 
      /* move this conversation to the end and shuffle up the pointers.      */ 
      /* note that this means convptr->conv no longer matches i              */ 
      /* which is why tracing uses ConvptrArray[i]->conv                     */ 
      /***********************************************************************/ 
      ConvptrArray [NumConvs] = ConvptrArray [i]; 
      EventArray [NumConvs] = EventArray [i]; 
      for (j = i; j < NumConvs; j++) 
      { 
        ConvptrArray [j] = ConvptrArray [j+1]; 
        EventArray [j] = EventArray [j+1]; 
      } 
      ConvptrArray [NumConvs] = NULL; 
      EventArray [NumConvs] = NULL; 
    } 
  } 
 
  /***************************************************************************/ 
  /* Count threads within critical section                                   */ 
  /***************************************************************************/ 
  EnterCriticalSection(&runsem); 
  ThreadCount--; 
  SendThreads--; 
  GetLocalTime(&st); 
  SRTRACE(t,"Thread %d (send) Exit at %d:%d:%d: ThreadCount %d\n", 
          SendThreadNo,st.wHour,st.wMinute,st.wSecond,ThreadCount); 
  OUTPUTNUMBER 
  if (ThreadCount == 0) 
  { 
    PostMessage(hWndMain, WM_CLOSE, 0, 0); 
  } 
  LeaveCriticalSection(&runsem); 
  return(0); 
} 
 
/*****************************************************************************/ 
/* InitializeWinMain - does the windows bits of initialisation               */ 
/*****************************************************************************/ 
BOOL InitializeWinMain(HINSTANCE hInstance) 
{ 
  WAPPCDATA APPCData; 
  WNDCLASS class; 
  #define WinAPPCVERSION  0x0001 
 
  /***************************************************************************/ 
  /* Startup WinAPPC                                                         */ 
  /***************************************************************************/ 
  if (WinAPPCStartup(WinAPPCVERSION,&APPCData)) 
  { 
    return (FALSE); 
  } 
 
  /***************************************************************************/ 
  /* Register Window Class for our icon                                      */ 
  /***************************************************************************/ 
 
  class.style = 0; 
  class.lpfnWndProc   = (WNDPROC)TPWndProc; 
  class.cbClsExtra    = (DWORD)0; 
  class.cbWndExtra    = (DWORD)0; 
  class.hInstance     = hInstance; 
  class.hIcon         = LoadIcon(hInstance,"MainIcon"); 
  class.hCursor       = LoadCursor(NULL, IDC_ARROW); 
  class.hbrBackground = GetStockObject(WHITE_BRUSH); 
  class.lpszMenuName  = (LPSTR) NULL; 
  class.lpszClassName = (LPSTR) "MSEND\0"; 
 
  if (!RegisterClass(&class)) 
  { 
    return (FALSE); 
  } 
 
  /***************************************************************************/ 
  /* Create the window                                                       */ 
  /***************************************************************************/ 
  sprintf(title,"APPC Send TP\0"); 
 
  if ((hWndMain = CreateWindow("MSEND\0",         /* window class            */ 
      title,                                      /* window name             */ 
      WS_MINIMIZE|WS_OVERLAPPEDWINDOW,            /* window style            */ 
      0,                                          /* x position              */ 
      0,                                          /* y position              */ 
      10,                                         /* width                   */ 
      10,                                         /* height                  */ 
      NULL,                                       /* parent handle           */ 
      NULL,                                       /* menu or child ID        */ 
      hInstance,                                  /* instance                */ 
      NULL))                                      /* additional info         */ 
      == NULL) 
  { 
    return (FALSE); 
  } 
 
  ShowWindow(hWndMain, SW_MINIMIZE); 
 
  return(TRUE); 
 
} 
 
/*****************************************************************************/ 
/* Window proc for the iconised window                                       */ 
/*****************************************************************************/ 
LONG PASCAL TPWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) 
{ 
  switch (message) 
  { 
    case WM_CREATE: 
      break; 
 
    case WM_QUERYOPEN: 
      /***********************************************************************/ 
      /* Prevent the window being opened                                     */ 
      /***********************************************************************/ 
      break; 
 
    case WM_CLOSE: 
      return DefWindowProc(hWnd, message, wParam, lParam); 
      break; 
 
    case WM_DESTROY: 
      PostQuitMessage(0); 
      break; 
 
    default: 
      return DefWindowProc(hWnd, message, wParam, lParam); 
      break; 
  } 
  return 0l; 
} 
 
/*****************************************************************************/ 
/* InitializeMain - blanks out variables not set in ReadConfig               */ 
/*****************************************************************************/ 
void InitializeMain() 
{ 
  SendThreads = 0; 
  ThreadCount = 0; 
  cnvtptr     = (char *)&cnvt; 
 
  datach      = 'A'; 
 
  InitializeCriticalSection(&runsem); 
} 
 
/*****************************************************************************/ 
/* IssueSendVerb - looks at the verb which has just completed and does the   */ 
/*                 next one                                                  */ 
/*****************************************************************************/ 
BOOL IssueSendVerb(CONVCB * convptr) 
{ 
  BOOL TPEnded; 
  struct appc_hdr * vcbptr; 
 
//SRTRACE(t,"Thread %d (send) IssueSendVerb\n",convptr->thread); 
  TPEnded = FALSE; 
  vcbptr = (struct appc_hdr *) &convptr->vcb; 
  if (vcbptr->opcode != 0x0000) 
  { 
    TPEnded = ProcessReturns(convptr); 
  } 
  if (!TPEnded) 
  { 
    switch (vcbptr->opcode) 
    { 
      case 0x0000: 
        Build_TP_STARTED(convptr); 
 
        /*********************************************************************/ 
        /*PERF* Don't re-generate data each time we issue a send!            */ 
        /*********************************************************************/ 
        GenerateData(convptr); 
        break; 
 
      case AP_TP_STARTED: 
        Build_MC_ALLOCATE(convptr); 
        break; 
 
      case AP_M_ALLOCATE: 
        if (vcbptr->primary_rc == AP_OK) 
        { 
          /*******************************************************************/ 
          /* confirm before the first send to find out if anyone there       */ 
          /*******************************************************************/ 
          Build_MC_CONFIRM(convptr); 
        } 
        else 
        { 
          /*******************************************************************/ 
          /* allocate timed out or connection is for incoming call           */ 
          /* if there are no conversations active, try again in 5 seconds    */ 
          /* otherwise retry immediately - someone must be out there         */ 
          /*******************************************************************/ 
          if (SimSendConvs == 0) 
          { 
            SRTRACE(t,"Thread %d:%d (send) wait 5 seconds before retry\n", 
                    convptr->thread,convptr->conv); 
            Sleep (5000); 
          } 
          SRTRACE(t,"Thread %d:%d (send) retrying\n", 
                  convptr->thread,convptr->conv); 
          Build_MC_ALLOCATE(convptr); 
        } 
        break; 
 
      case AP_M_SEND_DATA: 
        convptr->SendCount++; 
        convptr->ConfirmCount++; 
        if ((NumSends != 0) && (convptr->SendCount == NumSends)) 
        { 
          /*******************************************************************/ 
          /* all sends done - deallocate                                     */ 
          /*******************************************************************/ 
          Build_MC_DEALLOCATE(convptr); 
        } 
        else if ((ConfirmEvery != 0) && 
                 (convptr->ConfirmCount == ConfirmEvery)) 
        { 
          /*******************************************************************/ 
          /* time to confirm                                                 */ 
          /*******************************************************************/ 
          Build_MC_CONFIRM(convptr); 
        } 
        else 
        { 
          /*******************************************************************/ 
          /* just send more data                                             */ 
          /*******************************************************************/ 
          Build_MC_SEND_DATA(convptr); 
        } 
        break; 
 
      case AP_M_CONFIRM: 
        if (vcbptr->primary_rc == AP_OK) 
        { 
          if (convptr->SendCount == 0) 
          { 
            convptr->Counted = TRUE; 
            EnterCriticalSection(&runsem); 
            SimSendConvs++; 
            OUTPUTNUMBER 
            SRTRACE(t,"Thread %d (send) SimSendConvs %d\n", 
                    convptr->thread,SimSendConvs); 
            LeaveCriticalSection(&runsem); 
          } 
          convptr->ConfirmCount=0; 
          Build_MC_SEND_DATA(convptr); 
        } 
        else 
        { 
          /*******************************************************************/ 
          /* if there are no conversations active, try again in 5 seconds    */ 
          /* otherwise retry immediately - someone must be out there         */ 
          /*******************************************************************/ 
          if (SimSendConvs == 0) 
          { 
            SRTRACE(t,"Thread %d:%d (send) wait 5 seconds before retry\n", 
                    convptr->thread,convptr->conv); 
            Sleep (5000); 
          } 
          SRTRACE(t,"Thread %d:%d (send) retrying\n", 
                  convptr->thread,convptr->conv); 
          Build_MC_ALLOCATE(convptr); 
        } 
        break; 
 
      case AP_M_DEALLOCATE: 
        Build_TP_ENDED(convptr); 
        break; 
 
      case AP_TP_ENDED: 
        /*********************************************************************/ 
        /* quit                                                              */ 
        /*********************************************************************/ 
        TPEnded = TRUE; 
        break; 
 
      default: 
        /*********************************************************************/ 
        /* What is this verb then ??                                         */ 
        /*********************************************************************/ 
        TPEnded = TRUE; 
        DebugBreak(); 
        break; 
 
    } /* Op-code switch */ 
 
  } 
 
  /***************************************************************************/ 
  /* If send conversation has finished and was counted, count it down        */ 
  /***************************************************************************/ 
  if (TPEnded && convptr->Counted) 
  { 
    EnterCriticalSection(&runsem); 
    SimSendConvs--; 
    OUTPUTNUMBER 
    SRTRACE(t,"Thread %d (send) SimSendConvs %d\n", 
            convptr->thread,SimSendConvs); 
    LeaveCriticalSection(&runsem); 
  } 
 
  /***************************************************************************/ 
  /* Now go ahead and issue the verb, if we're not finished                  */ 
  /***************************************************************************/ 
  if (!TPEnded) 
  { 
    convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent, 
(long)(char *)(vcbptr)); 
    if (convptr->async_corr == 0) 
      { 
        SRTRACE(t,"Thread %d (send) WinAsync call %x failed corr %p\n", 
                convptr->thread,vcbptr->opcode, 
                convptr->async_corr); 
        convptr->TPEnded = TRUE; 
      } 
  } 
 
  SRTRFLUSH() 
  return(TPEnded); 
 
} /* Issue send verb */ 
 
/*****************************************************************************/ 
/* Build routines to build all required verbs                                */ 
/*****************************************************************************/ 
 
void Build_TP_STARTED(CONVCB * convptr) 
{ 
  TP_STARTED * vcbptr; 
  SRTRACE(t,"Thread %d:%d (%s) Build_TP_Started\n", 
          convptr->thread,convptr->conv,convptr->type); 
  vcbptr = (TP_STARTED *) &(convptr->vcb); 
 
  CLEARVCB 
 
  vcbptr->opcode = AP_TP_STARTED; 
  memcpy(&(vcbptr->lu_alias), LocalLUAlias, 8); 
  memcpy(&(vcbptr->tp_name), TPName, 64); 
} 
 
void Build_TP_ENDED(CONVCB * convptr) 
{ 
  TP_ENDED * vcbptr; 
  SRTRACE(t,"Thread %d:%d (%s) Build_TP_Ended\n", 
          convptr->thread,convptr->conv,convptr->type); 
  vcbptr = (TP_ENDED *) &(convptr->vcb); 
 
  CLEARVCB 
 
  vcbptr->opcode = AP_TP_ENDED; 
  memcpy(&(vcbptr->tp_id), convptr->TPid, 8); 
  vcbptr->type = AP_SOFT; 
} 
 
void Build_MC_ALLOCATE(CONVCB * convptr) 
{ 
  MC_ALLOCATE * vcbptr; 
  SRTRACE(t,"Thread %d:%d (%s) Build_MC_Allocate\n", 
          convptr->thread,convptr->conv,convptr->type); 
  vcbptr = (MC_ALLOCATE *) &(convptr->vcb); 
 
  CLEARVCB 
 
  vcbptr->opcode = AP_M_ALLOCATE; 
  vcbptr->opext = AP_MAPPED_CONVERSATION; 
  memcpy(vcbptr->tp_id,convptr->TPid, 8); 
  vcbptr->sync_level = AP_CONFIRM_SYNC_LEVEL; 
  vcbptr->rtn_ctl = AP_WHEN_SESSION_ALLOCATED; 
  memcpy(vcbptr->plu_alias, RemoteLUAlias, 8); 
  memcpy(vcbptr->mode_name, ModeName, 8); 
  memcpy(vcbptr->tp_name, RemoteTPName, 64); 
  vcbptr->security = AP_NONE; 
} 
 
void Build_MC_CONFIRM(CONVCB * convptr) 
{ 
  MC_CONFIRM * vcbptr; 
//SRTRACE(t,"Thread %d:%d (%s) Build_MC_Confirm\n", 
//        convptr->thread,convptr->conv,convptr->type); 
  vcbptr = (MC_CONFIRM *) &(convptr->vcb); 
 
  CLEARVCB 
 
  vcbptr->opcode = AP_M_CONFIRM; 
  vcbptr->opext = AP_MAPPED_CONVERSATION; 
  memcpy(vcbptr->tp_id,convptr->TPid, 8); 
  vcbptr->conv_id = convptr->Convid; 
} 
 
void Build_MC_DEALLOCATE(CONVCB * convptr) 
{ 
  MC_DEALLOCATE * vcbptr; 
  SRTRACE(t,"Thread %d:%d (%s) Build_MC_Deallocate\n", 
          convptr->thread,convptr->conv,convptr->type); 
  vcbptr = (MC_DEALLOCATE *) &(convptr->vcb); 
 
  CLEARVCB 
 
  vcbptr->opcode = AP_M_DEALLOCATE; 
  vcbptr->opext = AP_MAPPED_CONVERSATION; 
  memcpy(&(vcbptr->tp_id),convptr->TPid, 8); 
  vcbptr->conv_id = convptr->Convid; 
  if (ConfirmEvery == 0) 
  { 
     vcbptr->dealloc_type = AP_FLUSH; 
  } 
  else 
  { 
     vcbptr->dealloc_type = AP_SYNC_LEVEL; 
  } 
} 
 
void Build_MC_SEND_DATA(CONVCB * convptr) 
{ 
  MC_SEND_DATA * vcbptr; 
//SRTRACE(t,"Thread %d:%d (%s) Build_MC_Send_Data\n", 
//        convptr->thread,convptr->conv,convptr->type); 
  vcbptr = (MC_SEND_DATA *) &(convptr->vcb); 
 
  CLEARVCB 
 
  //  PERF - GenerateData(); 
 
  vcbptr->opcode = AP_M_SEND_DATA; 
  vcbptr->opext = AP_MAPPED_CONVERSATION; 
  memcpy(&(vcbptr->tp_id),convptr->TPid, 8); 
  vcbptr->conv_id = convptr->Convid; 
  vcbptr->dlen = convptr->SendSize; 
  vcbptr->dptr = convptr->DataPtr; 
  vcbptr->type = AP_NONE; 
} 
 
/*****************************************************************************/ 
/* ProcessReturns - Checks return codes from the last verb to complete and   */ 
/*                  saves conversation id and tp id in the conversation cb   */ 
/*****************************************************************************/ 
BOOL ProcessReturns(CONVCB * convptr) 
{ 
  BOOL TPEnded = FALSE; 
  struct appc_hdr * vcbptr; 
  SYSTEMTIME st; 
 
//SRTRACE(t,"Thread %d:%d (%s) ProcessReturns\n", 
//        convptr->thread,convptr->conv,convptr->type); 
  vcbptr = (struct appc_hdr *) &(convptr->vcb); 
 
  GetLocalTime(&st); 
  if (vcbptr->primary_rc != AP_OK) 
  { 
    SRTRACE(t,"Thread %d:%d (%s) error: %s prc %4.4x src %8.8x at %d:%d:%d\n", 
            convptr->thread,convptr->conv,convptr->type, 
            VerbName[vcbptr->opcode], 
            APPC_FLIPI(vcbptr->primary_rc),APPC_FLIPL(vcbptr->secondary_rc), 
            st.wHour,st.wMinute,st.wSecond); 
    if ((vcbptr->opcode == AP_M_CONFIRM) && 
             (vcbptr->primary_rc == AP_ALLOCATION_ERROR)) 
    { 
      SRTRACE(t,"Thread %d:%d (%s) MC_CONFIRM completed with ALLOCATION_ERROR\n", 
              convptr->thread,convptr->conv,convptr->type); 
    } 
    else 
    { 
      TPEnded = TRUE; 
      SRTRACE(t,"Thread %d:%d (%s) unexpected error on %s - set TPEnded\n", 
              convptr->thread,convptr->conv,convptr->type, 
              VerbName[vcbptr->opcode]); 
    } 
  } 
  else 
  { 
    switch (vcbptr->opcode) 
    { 
      case AP_TP_STARTED: 
        SRTRACE(t,"Thread %d:%d (%s) TP_Started completed at %d:%d:%d\n", 
                convptr->thread,convptr->conv,convptr->type, 
                st.wHour,st.wMinute,st.wSecond); 
        memcpy(convptr->TPid,&(P_TPS(vcbptr)->tp_id),8); 
        break; 
 
      case AP_TP_ENDED: 
        SRTRACE(t,"Thread %d:%d (%s) TP_Ended completed at %d:%d:%d\n", 
                convptr->thread,convptr->conv,convptr->type, 
                st.wHour,st.wMinute,st.wSecond); 
        break; 
 
      case AP_M_ALLOCATE: 
        SRTRACE(t,"Thread %d:%d (%s) MC_Allocate completed at %d:%d:%d\n", 
                convptr->thread,convptr->conv,convptr->type, 
                st.wHour,st.wMinute,st.wSecond); 
        convptr->Convid = P_M_ALC(vcbptr)->conv_id; 
        break; 
 
      case AP_M_SEND_DATA: 
//      SRTRACE(t,"Thread %d:%d (%s) MC_Send_Data completed at %d:%d:%d\n", 
//              convptr->thread,convptr->conv,convptr->type, 
//              st.wHour,st.wMinute,st.wSecond); 
        break; 
 
      case AP_M_DEALLOCATE: 
SRTRACE(t,"Thread %d:%d (%s) MC_Deallocate completed at %d:%d:%d: send count %d\n", 
                convptr->thread,convptr->conv,convptr->type, 
                st.wHour,st.wMinute,st.wSecond, 
                convptr->SendCount); 
        convptr->Convid = 0; 
        break; 
 
      case AP_M_CONFIRM: 
SRTRACE(t,"Thread %d:%d (%s) MC_Confirm completed at %d:%d:%d: send count %d\n", 
                convptr->thread,convptr->conv,convptr->type, 
                st.wHour,st.wMinute,st.wSecond, 
                convptr->SendCount); 
        break; 
 
      default: 
        SRTRACE(t,"Thread %d:%d (%s) UNKNOWN opcode - set TPEnded\n", 
                convptr->thread,convptr->conv,convptr->type); 
        TPEnded = TRUE; 
        DebugBreak(); 
        break; 
    } 
  } 
  SRTRFLUSH() 
  return(TPEnded); 
} 
 
/*****************************************************************************/ 
/* ReadConfig - Reads config info from MSEND.CFG and allocates buffer for    */ 
/*              sending                                                      */ 
/*****************************************************************************/ 
void ReadConfig() 
{ 
  char buffer[200]; 
 
  if (!ReadString("TraceFile",TraceFile,60)) 
  { 
    strcpy(TraceFile,"MSEND.TRC"); 
  } 
 
  strcpy(TPName,"MSEND"); 
  PadString(TPName,64); 
  CONV_A_TO_E(TPName,64); 
 
  if (!ReadString("RemoteTPName",RemoteTPName,64)) 
  { 
    strcpy(RemoteTPName,"MRCVTP"); 
  } 
  PadString(RemoteTPName,64); 
  CONV_A_TO_E(RemoteTPName,64); 
 
  if (!ReadString("LocalLUAlias",LocalLUAlias,8)) 
  { 
    strcpy(LocalLUAlias,"SENDLU"); 
  } 
  PadString(LocalLUAlias,8); 
 
  if (!ReadString("RemoteLUAlias",RemoteLUAlias,8)) 
  { 
    strcpy(RemoteLUAlias,"RECVLU"); 
  } 
  PadString(RemoteLUAlias,8); 
 
  if (!ReadString("ModeName",ModeName,8)) 
  { 
    strcpy(ModeName,"#INTER"); 
  } 
  PadString(ModeName,8); 
  CONV_A_TO_E(ModeName,8); 
 
  NumSends=8; 
  if (ReadString("NumSends",buffer,200)) 
  { 
    NumSends=atoi(buffer); 
  } 
 
  ConfirmEvery=2; 
  if (ReadString("ConfirmEvery",buffer,200)) 
  { 
    ConfirmEvery=atoi(buffer); 
  } 
 
  SendSize=256; 
  if (ReadString("SendSize",buffer,200)) 
  { 
    SendSize=atoi(buffer); 
  } 
 
  NumSendConvs = 4; 
  if (ReadString("NumSendConvs",buffer,200)) 
  { 
    NumSendConvs=atoi(buffer); 
  } 
 
} 
 
/*****************************************************************************/ 
/* CONV_A_TO_E - ASCII to EBCDIC conversion routine                          */ 
/*****************************************************************************/ 
void CONV_A_TO_E(char * string,int length) 
{ 
  memset(cnvtptr,0,sizeof(cnvt)); 
 
  cnvt.opcode       = SV_CONVERT; 
  cnvt.direction    = SV_ASCII_TO_EBCDIC; 
  cnvt.char_set     = SV_AE; 
 
  cnvt.len          = length; 
  cnvt.source       = string; 
  cnvt.target       = string; 
 
  ACSSVC_C((long)(char *) (cnvtptr)); 
} 
 
/*****************************************************************************/ 
/* CONV_E_TO_A - EBCDIC to ASCII conversion routine                          */ 
/*****************************************************************************/ 
void CONV_E_TO_A(char * string,int length) 
{ 
  memset(cnvtptr,0,sizeof(cnvt)); 
 
  cnvt.opcode       = SV_CONVERT; 
  cnvt.direction    = SV_EBCDIC_TO_ASCII; 
  cnvt.char_set     = SV_AE; 
  cnvt.len          = length; 
  cnvt.source       = string; 
  cnvt.target       = string; 
 
  ACSSVC_C((long)(char *) (cnvtptr)); 
} 
 
/*****************************************************************************/ 
/* GenerateData    - Fill in data buffer                                     */ 
/*****************************************************************************/ 
void GenerateData(CONVCB * convptr) 
{ 
  int i; 
  int div; 
  int rem; 
  char * dptr; 
 
  dptr = convptr->DataPtr; 
  div = convptr->SendSize / 5; 
  rem = convptr->SendSize % 5; 
 
  for (; div--;) 
  { 
    for (i=4; i--; *dptr++ = datach); 
    *dptr++ = '.'; 
  } 
  for (; rem--; *dptr++ = datach); 
 
  EnterCriticalSection(&runsem); 
  datach = (datach=='Z' ? 'A' : datach + 1); 
  LeaveCriticalSection(&runsem); 
} 
 
/*****************************************************************************/ 
/* ReadString - Get a line of text from the config file                      */ 
/*****************************************************************************/ 
int ReadString(char * lpValueName,char * lpData, int maxlen) 
{ 
  char       buffer[200]; 
  char      *p = NULL; 
  FILE      *h = NULL; 
  BOOL       match = FALSE; 
  BOOL       eof   = FALSE; 
  int        rc = 0; 
  int        ch = 0; 
  int        i = 0; 
  BOOL       gotdata = FALSE; 
  char       separators[] = " =\t\n"; 
 
  GetModuleFileName( hInst, buffer, sizeof(buffer) ); 
  lstrcpy( buffer+lstrlen(buffer) - 4, ".CFG" ); 
  h = fopen( buffer, "r" ); 
  buffer[0] = '\0'; 
 
  if (h != NULL) 
  { 
    while ((!match) && (!eof)) 
    { 
      /***********************************************************************/ 
      /* Use fgetc to read a line of text from the file                      */ 
      /***********************************************************************/ 
      for (i=0; (i<sizeof(buffer))     && 
                ((ch=getc(h)) != EOF)  && 
                ((char)ch != '\n'); 
                                   i++) 
      { 
        buffer[i] = (char)ch; 
      } 
      if ((char)ch == '\n') 
      { 
        buffer[i++] = (char)ch; 
      } 
      if (ch == EOF) 
      { 
        eof = TRUE; 
      } 
      else 
      { 
        /*********************************************************************/ 
        /* Compare the first token in the line read with the requested parm  */ 
        /*********************************************************************/ 
        if (!strcmpi(strupr(strtok(buffer, separators)), lpValueName)) 
        { 
          match = TRUE; 
          /*******************************************************************/ 
          /* Get a pointer to the second token (the value we want)           */ 
          /*******************************************************************/ 
          p = strtok(NULL, separators); 
 
          /*******************************************************************/ 
          /* Copy the data IF there is some                                  */ 
          /*******************************************************************/ 
          if (p != NULL) 
          { 
            /*****************************************************************/ 
            /* Force a NULL after the second token                           */ 
            /*****************************************************************/ 
            strtok(NULL, separators); 
 
            /*****************************************************************/ 
            /* Copy the data                                                 */ 
            /*****************************************************************/ 
            strncpy(lpData, p, maxlen); 
            gotdata = TRUE; 
          } 
          else 
          { 
            gotdata = FALSE; 
          } 
        } 
      } 
    } 
 
    if (gotdata) 
    { 
      rc = 1; 
    } 
 
    fclose(h); 
 
  } 
 
  return(rc); 
} 
 
/*****************************************************************************/ 
/* PadString - Remove terminating NULL  and pad on the right with spaces     */ 
/*****************************************************************************/ 
void PadString(char * string,int length) 
{ 
  char * p; 
  if ((p=memchr(string,'\0',length)) != NULL) 
  { 
    while (p < string+length) 
    { 
      *p++=' '; 
    } 
  } 
}