home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / netds / sna / msendrcv / mrcv.c < prev    next >
Text File  |  1997-04-09  |  52KB  |  1,283 lines

  1. /* mrcv.c */
  2. /* (C) COPYRIGHT DATA CONNECTION LIMITED 1994 */
  3.  
  4. /*****************************************************************************/
  5. /* Change History                                                            */
  6. /*                                                                           */
  7. /*       21/02/94 DK  Created                                                */
  8. /*       15/03/94 DK  Thread priorities added and signalled event goes to    */
  9. /*                    back of the list (for WaitForMultipleObjects)          */
  10. /*       14/04/94 DK  OutputResults added                                    */
  11. /*****************************************************************************/
  12.  
  13. /*****************************************************************************/
  14. /* If you want internal tracing, #define SRTRC here                          */
  15. /*****************************************************************************/
  16. //#define SRTRC
  17.  
  18. /*****************************************************************************/
  19. /*                                                                           */
  20. /* ROUTINE : RECEIVE using event completion                                  */
  21. /*                                                                           */
  22. /* FUNCTION: This file contains the routines for a multi-threaded routine    */
  23. /*           which uses asynchronous APPC calls with event completion        */
  24. /*           to receive data.                                                */
  25. /*                                                                           */
  26. /*           It runs with either the single-threaded or the multi-threaded   */
  27. /*           version of send (msend or sendtp).                              */
  28. /*                                                                           */
  29. /* INPUTS  : MRCV.CFG (file) (documented below)                              */
  30. /*                                                                           */
  31. /* OUTPUTS : MRCV.TRC                                                        */
  32. /*                                                                           */
  33. /*****************************************************************************/
  34.  
  35. /*****************************************************************************/
  36. /* Operation:                                                                */
  37. /*                                                                           */
  38. /* This is a Windows NT application which runs in a minimized window.        */
  39. /*                                                                           */
  40. /* Thread structure:                                                         */
  41. /*                                                                           */
  42. /*   A receive_allocate thread                                               */
  43. /*     This thread issues a receive_allocate and, when it completes, hands   */
  44. /*     it to a receive thread.  It then waits for the receive thread to      */
  45. /*     accept the conversation.  This thread uses event completion, but      */
  46. /*     its operation is essentially synchronous.                             */
  47. /*                                                                           */
  48. /*   A variable number of receive threads                                    */
  49. /*     Each receive thread processes a variable number of conversations and  */
  50. /*     issues a WaitForMultipleObjects call to wait for completion of any    */
  51. /*     one of its receive operations or for a request for a new conversation */
  52. /*     from the receive_allocate thread.  It sets a second event to tell the */
  53. /*     receive_allocate thread that it has accepted the conversation.        */
  54. /*     Each conversation issues MC_RECEIVE_AND_WAIT verbs to receive data.   */
  55. /*     If confirmation is requested, an MC_CONFIRM verb is issued.           */
  56. /*                                                                           */
  57. /*   Note:  this program is compatible with the single-threaded versions of  */
  58. /*   send, which can be run for example on WIN16 clients.                    */
  59. /*                                                                           */
  60. /*****************************************************************************/
  61.  
  62. /*****************************************************************************/
  63. /* Configuration file:                                                       */
  64. /*                                                                           */
  65. /* The configuration file is called MRCV.CFG and must reside in the          */
  66. /* same directory as the program.  It contains the following, in any order.  */
  67. /* If any parameter is omitted, the default is assumed.                      */
  68. /*                                                                           */
  69. /* ResultFile = <Name of file for results, default MRCV.OUT>                 */
  70. /* TraceFile = <Name of file for tracing, default MRCV.TRC>                  */
  71. /* LocalTPName = <Name used for receive_allocate, default MRCVTP>            */
  72. /*                                                                           */
  73. /* NumRcvConvs = <Number of conversations to be received, default = 4>       */
  74. /* NumRcvThreads = <Number of receive threads, default = 2>                  */
  75. /*                                                                           */
  76. /* The name used for TP_STARTED is fixed at MRCV.                            */
  77. /*                                                                           */
  78. /* If NumRcvConvs is zero, the TP will keep issuing receive_allocates.       */
  79. /* If NumRcvConvs is non-zero and NumRcvThreads is greater than NumRcvConvs, */
  80. /*    NumRcvThreads is reduced to NumRcvConvs.                               */
  81. /* If NumRcvConvs is non-zero and NumRcvThreads*63 < NumRcvConvs,            */
  82. /*    NumRcvThreads is increased to NumRcvConvs / 63 + 1.                    */
  83. /*                                                                           */
  84. /* Configuration constants (in mrcv.h)                                       */
  85. /*                                                                           */
  86. /* #define MAX_RECEIVE_THREADS  Max number of receive threads          (64)  */
  87. /* #define MAX_RCV_PER_THREAD   Max conversations per receive thread   (63)  */
  88. /*                                                                           */
  89. /* MAX_RCV_PER_THREAD is constrained by the limit of 64 objects that can be  */
  90. /* waited for in WaitForMultipleObjects.                                     */
  91. /*                                                                           */
  92. /*****************************************************************************/
  93.  
  94. #include <windows.h>
  95. HINSTANCE hInst;
  96. BOOL verbs_started = FALSE;
  97. #include <stdio.h>
  98. #include <stdlib.h>
  99. #include <string.h>
  100. #include <winappc.h>
  101. #include <wincsv.h>
  102. #include "mrcv.h"
  103.  
  104. /*****************************************************************************/
  105. /* Trace macros                                                              */
  106. /*****************************************************************************/
  107. #ifdef SRTRC
  108. #define SRTROPEN() t = fopen(TraceFile,"w");
  109. #define SRTRFLUSH() fflush(t);
  110. #define SRTRCLOSE() fclose(t);
  111. #define SRTRACE fprintf
  112. #else
  113. #define SRTROPEN()
  114. #define SRTRFLUSH()
  115. #define SRTRCLOSE()
  116. #define SRTRACE 1 ? (void) 0 : fprintf
  117. #endif
  118.  
  119. /*****************************************************************************/
  120. /* WinMain - reads initialization info and controls message loop             */
  121. /*           NT version                                                      */
  122. /*****************************************************************************/
  123. int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  124.                    LPSTR lpCmdLine, int nCmdShow)
  125. {
  126.  
  127.   MSG msg;
  128.   DWORD Tid;
  129.   int i;
  130.   HANDLE ThreadHandle;
  131.  
  132.   hInst = hInstance;
  133.  
  134.   InitializeMain();
  135.  
  136.   if (!InitializeWinMain(hInstance))
  137.   {
  138.     return (FALSE);
  139.   }
  140.  
  141.   ReadConfig();
  142.   SRTROPEN()
  143.  
  144.   /***************************************************************************/
  145.   /* Create two events for each receive thread                               */
  146.   /***************************************************************************/
  147.   for (i = 0; i < NumRcvThreads; i++)
  148.   {
  149.     /*************************************************************************/
  150.     /* Create an event for kicking the receive thread                        */
  151.     /*************************************************************************/
  152.     RcvThreadArray[i].convptr = NULL;
  153.     RcvThreadArray[i].event1 = CreateEvent(NULL,FALSE,FALSE,NULL);
  154.     RcvThreadArray[i].event2 = CreateEvent(NULL,FALSE,FALSE,NULL);
  155.   }
  156.  
  157.   /***************************************************************************/
  158.   /* Create receive threads                                                  */
  159.   /***************************************************************************/
  160.   for (i = 0; i < NumRcvThreads; i++)
  161.   {
  162.     SRTRACE(t,"Created event handles %p %p for receive thread %d\n",
  163.             RcvThreadArray[i].event1, RcvThreadArray[i].event2,i);
  164.  
  165.     /*************************************************************************/
  166.     /* Create the thread                                                     */
  167.     /*************************************************************************/
  168.     ThreadHandle = CreateThread(NULL,
  169.                                 16000,
  170.                                 (LPTHREAD_START_ROUTINE)ReceiveThread,
  171.                                 (void *)i,
  172.                                 0,
  173.                                 &Tid);
  174.     if (ThreadHandle == NULL)
  175.     {
  176.       GetLastError();
  177.       DebugBreak();
  178.     }
  179.     SetThreadPriority(ThreadHandle,THREAD_PRIORITY_LOWEST);
  180.     SRTRACE(t,"Created receive thread %d with priority %d\n",
  181.             i,GetThreadPriority(ThreadHandle));
  182.     CloseHandle(ThreadHandle);
  183.   }
  184.  
  185.   /***************************************************************************/
  186.   /* Create receive_allocate thread                                          */
  187.   /***************************************************************************/
  188.   if (NumRcvThreads > 0)
  189.   {
  190.     ThreadHandle = CreateThread(NULL,
  191.                                 16000,
  192.                                 (LPTHREAD_START_ROUTINE)RcvAllocThread,
  193.                                 NULL,
  194.                                 0,
  195.                                 &Tid);
  196.     if (ThreadHandle == NULL)
  197.     {
  198.       GetLastError();
  199.       DebugBreak();
  200.     }
  201.     SetThreadPriority(ThreadHandle,THREAD_PRIORITY_NORMAL);
  202.     SRTRACE(t,"Created receive allocate thread with priority %d\n",
  203.             GetThreadPriority(ThreadHandle));
  204.     CloseHandle(ThreadHandle);
  205.   }
  206.  
  207.   /***************************************************************************/
  208.   /* Windows processing loop                                                 */
  209.   /***************************************************************************/
  210.   while(GetMessage(&msg,NULL,0,0))
  211.   {
  212.     TranslateMessage(&msg);
  213.     DispatchMessage(&msg);
  214.   }
  215.  
  216.   WinAPPCCleanup();
  217.   OutputResults();
  218.   SRTRFLUSH()
  219.   SRTRCLOSE()
  220.   DeleteCriticalSection(&runsem);
  221.   return msg.wParam;         /* save exit parameter for return               */
  222.  
  223. }
  224.  
  225. /*****************************************************************************/
  226. /* RcvAllocThread - separate thread for receive_allocates                    */
  227. /*****************************************************************************/
  228. DWORD WINAPI RcvAllocThread()
  229. {
  230.   /***************************************************************************/
  231.   /* Local variables                                                         */
  232.   /***************************************************************************/
  233.   CONVCB * convptr;
  234.   struct appc_hdr * vcbptr;
  235.   unsigned short ThreadNo;
  236.   unsigned short NextReceive = 0;
  237.   BOOL   RcvAllocEnded = FALSE;
  238.   DWORD rc;
  239.   SYSTEMTIME st;
  240.  
  241.   /***************************************************************************/
  242.   /* Count threads within critical section                                   */
  243.   /***************************************************************************/
  244.   EnterCriticalSection(&runsem);
  245.   ThreadCount++;
  246.   ThreadNo = ThreadCount;
  247.   LeaveCriticalSection(&runsem);
  248.  
  249.   SRTRACE(t,"Thread %d (ralc) Started\n",ThreadNo);
  250.  
  251.   /***************************************************************************/
  252.   /* Loop round until the required number of conversations have been         */
  253.   /* allocated                                                               */
  254.   /***************************************************************************/
  255.   while ((NumRcvConvs == 0) || (NumRalcs < NumRcvConvs))
  256.   {
  257.     /*************************************************************************/
  258.     /* Make a conversation control block                                     */
  259.     /*************************************************************************/
  260.     convptr = malloc (sizeof(CONVCB));
  261.     convptr->thread       = ThreadNo;
  262.     convptr->conv         = 0;
  263.     convptr->TPid[0]      = '\0';
  264.     convptr->Convid       = 0;
  265.     convptr->async_corr   = 0;
  266.     convptr->RcvCount     = 0;
  267.     convptr->TPEnded      = FALSE;
  268.     convptr->Deallocated  = FALSE;
  269.     convptr->DataPtr      = malloc(RcvSize);
  270.     vcbptr = (struct appc_hdr *) &convptr->vcb;
  271.     memset(vcbptr,0,sizeof(VCB));
  272.     strcpy (convptr->type,"ralc");
  273.  
  274.     /*************************************************************************/
  275.     /* Create an event                                                       */
  276.     /*************************************************************************/
  277.     convptr->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  278.     SRTRACE(t,"Thread %d (ralc) convptr %p\n",ThreadNo,convptr);
  279.     SRTRACE(t,"Thread %d (ralc) event handle %p\n",ThreadNo,convptr->hEvent);
  280.  
  281.     /*************************************************************************/
  282.     /* Build a receive_allocate                                              */
  283.     /*************************************************************************/
  284.     Build_RECEIVE_ALLOCATE (convptr);
  285.  
  286.     /*************************************************************************/
  287.     /* Issue receive_allocate using event notification                       */
  288.     /*************************************************************************/
  289.     convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent,
  290.                                          (long)(char *)(vcbptr));
  291. //  SRTRACE(t,"Thread %d (ralc) Receive_Allocate issued corr %p\n",
  292. //          ThreadNo,convptr->async_corr);
  293.     SRTRFLUSH()
  294.     if (convptr->async_corr == 0)
  295.     {
  296.       SRTRACE(t,"Thread %d (ralc) WinAsync call %x failed zero corr %p\n",
  297.               ThreadNo,vcbptr->opcode,convptr->async_corr);
  298.       break;
  299.     }
  300.     else
  301.     {
  302.       /***********************************************************************/
  303.       /* Wait for event completion                                           */
  304.       /***********************************************************************/
  305.       rc = WaitForSingleObject(convptr->hEvent,INFINITE);
  306.       if (rc == WAIT_FAILED)
  307.       {
  308.         rc = GetLastError();
  309.         SRTRACE(t,"Thread %d (ralc) wait for event_1 %p failed with rc %d\n",
  310.                 ThreadNo,convptr->hEvent,rc);
  311.         break;
  312.       }
  313.       if (vcbptr->primary_rc != AP_OK)
  314.       {
  315.         SRTRACE(t,"Thread %d (ralc) WinAsync error: %s prc %4.4x src %8.8x\n",
  316.                 ThreadNo,VerbName[vcbptr->opcode],
  317.                 APPC_FLIPI(vcbptr->primary_rc),
  318.                 APPC_FLIPL(vcbptr->secondary_rc));
  319.         break;
  320.       }
  321.       else
  322.       {
  323.         NumRalcs++;
  324.         SRTRACE(t,"Thread %d (ralc) Receive_Allocate %d completed\n",
  325.                 ThreadNo,NumRalcs);
  326.  
  327.         /*********************************************************************/
  328.         /* give it to a receive thread by kicking event 1                    */
  329.         /*********************************************************************/
  330.         RcvThreadArray[NextReceive].convptr = convptr;
  331. //      SRTRACE(t,"Thread %d (ralc) convptr %p\n",ThreadNo,convptr);
  332. //      SRTRACE(t,"Thread %d (ralc) kicking receive thread %d\n",
  333. //              ThreadNo,NextReceive);
  334. //      SRTRACE(t,"Thread %d (ralc) setting event_1 %p\n",ThreadNo,
  335. //              RcvThreadArray[NextReceive].event1);
  336.         rc = SetEvent (RcvThreadArray[NextReceive].event1);
  337.         if (rc == FALSE)
  338.         {
  339.           rc = GetLastError();
  340.           SRTRACE(t,"Thread %d (ralc) setting event_1 %p failed with rc %d\n",
  341.                   ThreadNo,RcvThreadArray[NextReceive].event1,rc);
  342.           break;
  343.         }
  344.  
  345.         /*********************************************************************/
  346.         /* wait for receive thread to release event 2                        */
  347.         /*********************************************************************/
  348. //      SRTRACE(t,"Thread %d (ralc) waiting for event_2 %d\n",ThreadNo,
  349. //              RcvThreadArray[NextReceive].event2);
  350.         rc = WaitForSingleObject(RcvThreadArray[NextReceive].event2,INFINITE);
  351.         if (rc == WAIT_FAILED)
  352.         {
  353.           rc = GetLastError();
  354.           SRTRACE(t,"Thread %d (ralc) wait for event_2 %p failed with rc %d\n",
  355.                   ThreadNo,RcvThreadArray[NextReceive].event2,rc);
  356.           break;
  357.         }
  358.  
  359.         /*********************************************************************/
  360.         /* ready for next receive allocate                                   */
  361.         /*********************************************************************/
  362.         NextReceive++;
  363.         if (NextReceive == NumRcvThreads)
  364.         {
  365.           NextReceive = 0;
  366.         }
  367.       }
  368.     }
  369.   }
  370.  
  371.   /***************************************************************************/
  372.   /* Kick receive threads for last time in case they are idle                */
  373.   /* convptr is set to NULL to indicate this final kick                      */
  374.   /***************************************************************************/
  375.   SRTRACE(t,"Thread %d (ralc) last kicks\n",ThreadNo);
  376.   for (NextReceive = 0; NextReceive < NumRcvThreads; NextReceive++)
  377.   {
  378.     RcvThreadArray[NextReceive].convptr = NULL;
  379.     rc = SetEvent (RcvThreadArray[NextReceive].event1);
  380.   }
  381.  
  382.   /***************************************************************************/
  383.   /* Count threads within critical section                                   */
  384.   /***************************************************************************/
  385.   EnterCriticalSection(&runsem);
  386.   ThreadCount--;
  387.   GetLocalTime(&st);
  388.   SRTRACE(t,"Thread %d (ralc) Exit at %d:%d:%d: ThreadCount %d\n",
  389.           ThreadNo,st.wHour,st.wMinute,st.wSecond,ThreadCount);
  390.   if (ThreadCount == 0)
  391.   {
  392.     PostMessage(hWndMain, WM_CLOSE, 0, 0);
  393.   }
  394.   LeaveCriticalSection(&runsem);
  395.   return(0);
  396. }
  397.  
  398. /*****************************************************************************/
  399. /* ReceiveThread - service thread which processes multiple conversations     */
  400. /*****************************************************************************/
  401. DWORD WINAPI ReceiveThread(DWORD k)
  402. {
  403.   /***************************************************************************/
  404.   /* Separate variables for each instance of this thread                     */
  405.   /***************************************************************************/
  406.   DWORD ObjIndex;
  407.   DWORD i;
  408.   DWORD j;
  409.   DWORD rc;
  410.   unsigned short NumConvs = 0;
  411.   unsigned short RcvMax = 0;
  412.   unsigned short ThreadNo;
  413.   unsigned short RcvThreadNo;
  414.   CONVCB * ConvptrArray [MAX_RCV_PER_THREAD+2];
  415.   HANDLE EventArray [MAX_RCV_PER_THREAD+2];
  416.   SYSTEMTIME st;
  417.  
  418.   EventArray[0] = RcvThreadArray[k].event1;
  419.   ConvptrArray[0] = NULL;
  420.   for (i = 1; i < MAX_RCV_PER_THREAD+2; i++)
  421.   {
  422.     EventArray[i] = NULL;
  423.     ConvptrArray[i] = NULL;
  424.   }
  425.  
  426.   /***************************************************************************/
  427.   /* Count threads                                                           */
  428.   /***************************************************************************/
  429.   EnterCriticalSection(&runsem);
  430.   ThreadCount++;
  431.   RcvThreads++;
  432.   ThreadNo = ThreadCount;
  433.   RcvThreadNo = RcvThreads;
  434.   OUTPUTNUMBER
  435.   LeaveCriticalSection(&runsem);
  436.  
  437.   SRTRACE(t,"Thread %d (recv) Started\n",k);
  438.   SRTRACE(t,"Thread %d (recv) has event handles %p %p\n",k,
  439.           RcvThreadArray[k].event1,RcvThreadArray[k].event2);
  440.  
  441.   /***************************************************************************/
  442.   /* Loop round until finished                                               */
  443.   /***************************************************************************/
  444.   while (TRUE)
  445.   {
  446.     /*************************************************************************/
  447.     /* Wait for event completion                                             */
  448.     /*************************************************************************/
  449. //  SRTRACE(t,"Thread %d (recv) waiting for %d events to complete\n",
  450. //          k,NumConvs+1);
  451.     ObjIndex = WaitForMultipleObjects(NumConvs+1,EventArray,FALSE,INFINITE);
  452.     if (ObjIndex == WAIT_FAILED)
  453.     {
  454.       rc = GetLastError();
  455.       SRTRACE(t,"Thread %d (recv) wait for %d events failed with rc %d\n",
  456.               k,NumConvs+1,rc);
  457.       for (j = 0; j <= NumConvs; j++)
  458.       {
  459.         SRTRACE(t,"Thread %d (recv) event %d has handle %p\n",
  460.                 k,j,EventArray[j]);
  461.       }
  462.       break;
  463.     }
  464.     ObjIndex -= WAIT_OBJECT_0;
  465. //  SRTRACE(t,"Thread %d (recv) event %d has completed\n",k,ObjIndex);
  466.     if (ObjIndex == 0)
  467.     {
  468.       /***********************************************************************/
  469.       /* final kick from receive_allocate thread - terminate if idle         */
  470.       /***********************************************************************/
  471.       if (RcvThreadArray[k].convptr == NULL)
  472.       {
  473.         SRTRACE(t,"Thread %d (recv) ralc thread terminating\n",k);
  474.         SRTRACE(t,"Thread %d (recv) NumRalcs %d NumRcvConvs %d NumConvs %d\n",
  475.                 k,NumRalcs,NumRcvConvs,NumConvs);
  476.         if (NumConvs == 0)
  477.         {
  478.           break;
  479.         }
  480.         else
  481.         {
  482.           continue;
  483.         }
  484.       }
  485.       else
  486.       {
  487.         /*********************************************************************/
  488.         /* new conversation - save convptr and release rcv_alloc thread      */
  489.         /*********************************************************************/
  490.         SRTRACE(t,"Thread %d (recv) new conversation\n",k);
  491.  
  492.         /*********************************************************************/
  493.         /* select next entry in the convptr array as conversation index      */
  494.         /*********************************************************************/
  495.         NumConvs++;
  496.         i = NumConvs;
  497.  
  498.         /*********************************************************************/
  499.         /* set up convptr array                                              */
  500.         /* save service thread number and slot number in convcb for tracing  */
  501.         /* add event to event array                                          */
  502.         /*********************************************************************/
  503.         ConvptrArray[i] = RcvThreadArray[k].convptr;
  504. //      SRTRACE(t,"Thread %d:%d (recv) using convptr %p\n",k,i,ConvptrArray[i]);
  505.         ConvptrArray[i]->thread = (unsigned short)k;
  506.         ConvptrArray[i]->conv   = (unsigned short)i;
  507.         strcpy (ConvptrArray[i]->type,"recv");
  508.         EventArray[i] = ConvptrArray[i]->hEvent;
  509.         EnterCriticalSection(&runsem);
  510.         SimRcvConvs++;
  511.         OUTPUTNUMBER
  512.         SRTRACE(t,"Thread %d:%d (recv) NumConvs %d SimRcvConvs %d\n",
  513.                 k,i,NumConvs,SimRcvConvs);
  514.         LeaveCriticalSection(&runsem);
  515.  
  516.         /*********************************************************************/
  517.         /* release receive_allocate thread                                   */
  518.         /*********************************************************************/
  519. //      SRTRACE(t,"Thread %d:%d (recv) setting event_2 %p\n",
  520. //              k,i,RcvThreadArray[k].event2);
  521.         rc = SetEvent (RcvThreadArray[k].event2);
  522.         if (rc == FALSE)
  523.         {
  524.           rc = GetLastError();
  525.           SRTRACE(t,"Thread %d:%d (recv) setting event_2 %p failed with rc %d\n",
  526.                   k,i,RcvThreadArray[k].event2,rc);
  527.           break;
  528.         }
  529.       }
  530.     }
  531.     else
  532.     {
  533.       /***********************************************************************/
  534.       /* Object index locates existing conversation                          */
  535.       /***********************************************************************/
  536.       i = ObjIndex;
  537.     }
  538.  
  539.     /*************************************************************************/
  540.     /* Issue the next verb                                                   */
  541.     /*************************************************************************/
  542. //  SRTRACE(t,"Thread %d:%d (recv) issuing next verb\n",
  543. //          k,ConvptrArray[i]->conv);
  544.     ConvptrArray[i]->TPEnded = IssueRcvVerb(ConvptrArray[i]);
  545.     if (ConvptrArray[i]->TPEnded)
  546.     {
  547.       /***********************************************************************/
  548.       /* end of conversation                                                 */
  549.       /***********************************************************************/
  550.       SRTRACE(t,"Thread %d:%d (recv) conversation completed\n",
  551.               k,ConvptrArray[i]->conv);
  552.       NumConvs--;
  553.       EnterCriticalSection(&runsem);
  554.       SimRcvConvs--;
  555.       RcvConvs++;
  556.       OUTPUTNUMBER
  557.       SRTRACE(t,"Thread %d:%d (recv) NumConvs %d SimRcvConvs %d RcvConvs %d\n",
  558.               k,ConvptrArray[i]->conv,NumConvs,SimRcvConvs,RcvConvs);
  559.       SRTRACE(t,"Thread %d:%d (recv) NumRalcs %d NumRcvConvs %d NumConvs %d\n",
  560.               k,ConvptrArray[i]->conv,NumRalcs,NumRcvConvs,NumConvs);
  561.       LeaveCriticalSection(&runsem);
  562.  
  563.       /***********************************************************************/
  564.       /* free resources                                                      */
  565.       /***********************************************************************/
  566.       SRTRACE(t,"Thread %d:%d (recv) clearing entry %d\n",
  567.               k,ConvptrArray[i]->conv,i);
  568.       CloseHandle(ConvptrArray[i]->hEvent);
  569.       free (ConvptrArray[i]->DataPtr);
  570.       free (ConvptrArray[i]);
  571.       ConvptrArray[i] = NULL;
  572.       EventArray[i]   = NULL;
  573.  
  574.       /***********************************************************************/
  575.       /* if the required number of conversations have been allocated and     */
  576.       /* this thread has just finished its last conversation, end loop       */
  577.       /***********************************************************************/
  578.       if ((NumRcvConvs > 0) && (NumRalcs == NumRcvConvs) && (NumConvs == 0))
  579.       {
  580.         break;
  581.       }
  582.  
  583.       /***********************************************************************/
  584.       /* the event array cannot have holes in it, so shuffle up the          */
  585.       /* pointers and events                                                 */
  586.       /* note that this means convptr->conv no longer matches i              */
  587.       /* which is why tracing uses ConvptrArray[i]->conv                     */
  588.       /***********************************************************************/
  589.       for (j = i; j <= NumConvs; j++)
  590.       {
  591.         ConvptrArray [j] = ConvptrArray [j+1];
  592.         EventArray [j] = EventArray [j+1];
  593.       }
  594.       ConvptrArray [NumConvs+1] = NULL;
  595.       EventArray [NumConvs+1] = NULL;
  596.     }
  597.     else
  598.     {
  599.       /***********************************************************************/
  600.       /* to stop one conversation getting an unfair share of the time,       */
  601.       /* move this conversation to the end and shuffle up the pointers.      */
  602.       /* note that this means convptr->conv no longer matches i              */
  603.       /* which is why tracing uses ConvptrArray[i]->conv                     */
  604.       /***********************************************************************/
  605.       ConvptrArray [NumConvs+1] = ConvptrArray [i];
  606.       EventArray [NumConvs+1] = EventArray [i];
  607.       for (j = i; j <= NumConvs; j++)
  608.       {
  609.         ConvptrArray [j] = ConvptrArray [j+1];
  610.         EventArray [j] = EventArray [j+1];
  611.       }
  612.       ConvptrArray [NumConvs+1] = NULL;
  613.       EventArray [NumConvs+1] = NULL;
  614.     }
  615.   }
  616.  
  617.   /***************************************************************************/
  618.   /* Count threads within critical section                                   */
  619.   /***************************************************************************/
  620.   EnterCriticalSection(&runsem);
  621.   ThreadCount--;
  622.   RcvThreads--;
  623.   GetLocalTime(&st);
  624.   SRTRACE(t,"Thread %d (recv) Exit at %d:%d:%d: ThreadCount %d\n",
  625.           k,st.wHour,st.wMinute,st.wSecond,ThreadCount);
  626.   OUTPUTNUMBER
  627.   if (ThreadCount == 0)
  628.   {
  629.     PostMessage(hWndMain, WM_CLOSE, 0, 0);
  630.   }
  631.   LeaveCriticalSection(&runsem);
  632.  
  633.   return(0);
  634. }
  635.  
  636. /*****************************************************************************/
  637. /* InitializeWinMain - does the windows bits of initialisation               */
  638. /*****************************************************************************/
  639. BOOL InitializeWinMain(HINSTANCE hInstance)
  640. {
  641.   WAPPCDATA APPCData;
  642.   WNDCLASS class;
  643.   #define WinAPPCVERSION  0x0001
  644.  
  645.   /***************************************************************************/
  646.   /* Startup WinAPPC                                                         */
  647.   /***************************************************************************/
  648.   if (WinAPPCStartup(WinAPPCVERSION,&APPCData))
  649.   {
  650.     return (FALSE);
  651.   }
  652.  
  653.   /***************************************************************************/
  654.   /* Register Window Class for our icon                                      */
  655.   /***************************************************************************/
  656.  
  657.   class.style = 0;
  658.   class.lpfnWndProc   = (WNDPROC)TPWndProc;
  659.   class.cbClsExtra    = (DWORD)0;
  660.   class.cbWndExtra    = (DWORD)0;
  661.   class.hInstance     = hInstance;
  662.   class.hIcon         = LoadIcon(hInstance,"MainIcon");
  663.   class.hCursor       = LoadCursor(NULL, IDC_ARROW);
  664.   class.hbrBackground = GetStockObject(WHITE_BRUSH);
  665.   class.lpszMenuName  = (LPSTR) NULL;
  666.   class.lpszClassName = (LPSTR) "MRCV\0";
  667.  
  668.   if (!RegisterClass(&class))
  669.   {
  670.     return (FALSE);
  671.   }
  672.  
  673.   /***************************************************************************/
  674.   /* Create the window                                                       */
  675.   /***************************************************************************/
  676.   sprintf(title,"APPC Receive TP\0");
  677.  
  678.   if ((hWndMain = CreateWindow("MRCV\0",          /* window class            */
  679.       title,                                      /* window name             */
  680.       WS_MINIMIZE|WS_OVERLAPPEDWINDOW,            /* window style            */
  681.       0,                                          /* x position              */
  682.       0,                                          /* y position              */
  683.       10,                                         /* width                   */
  684.       10,                                         /* height                  */
  685.       NULL,                                       /* parent handle           */
  686.       NULL,                                       /* menu or child ID        */
  687.       hInstance,                                  /* instance                */
  688.       NULL))                                      /* additional info         */
  689.       == NULL)
  690.   {
  691.     return (FALSE);
  692.   }
  693.  
  694.   ShowWindow(hWndMain, SW_MINIMIZE);
  695.  
  696.   return(TRUE);
  697.  
  698. }
  699.  
  700. /*****************************************************************************/
  701. /* Window proc for the iconised window                                       */
  702. /*****************************************************************************/
  703. LONG PASCAL TPWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
  704. {
  705.   switch (message)
  706.   {
  707.     case WM_CREATE:
  708.       break;
  709.  
  710.     case WM_QUERYOPEN:
  711.       /***********************************************************************/
  712.       /* Prevent the window being opened                                     */
  713.       /***********************************************************************/
  714.       break;
  715.  
  716.     case WM_CLOSE:
  717.       return DefWindowProc(hWnd, message, wParam, lParam);
  718.       break;
  719.  
  720.     case WM_DESTROY:
  721.       PostQuitMessage(0);
  722.       break;
  723.  
  724.     default:
  725.       return DefWindowProc(hWnd, message, wParam, lParam);
  726.       break;
  727.   }
  728.   return 0l;
  729. }
  730.  
  731. /*****************************************************************************/
  732. /* InitializeMain - blanks out variables not set in ReadConfig               */
  733. /*****************************************************************************/
  734. void InitializeMain()
  735. {
  736.   RcvThreads  = 0;
  737.   ThreadCount = 0;
  738.   NumResults  = 0;
  739.   cnvtptr     = (char *)&cnvt;
  740.  
  741.   InitializeCriticalSection(&runsem);
  742. }
  743.  
  744. /*****************************************************************************/
  745. /* IssueRcvVerb - looks at the verb which has just completed and does the    */
  746. /*                 next one                                                  */
  747. /*****************************************************************************/
  748. BOOL IssueRcvVerb(CONVCB * convptr)
  749. {
  750.   BOOL TPEnded;
  751.   struct appc_hdr * vcbptr;
  752.  
  753. //SRTRACE(t,"Thread %d:%d (recv) IssueRcvVerb\n",
  754. //        convptr->thread,convptr->conv);
  755.   TPEnded = FALSE;
  756.   vcbptr = (struct appc_hdr *) &convptr->vcb;
  757.   if (vcbptr->opcode != 0x0000)
  758.   {
  759.     TPEnded = ProcessReturns(convptr);
  760.   }
  761.   if (!TPEnded)
  762.   {
  763.     switch (vcbptr->opcode)
  764.     {
  765.       case 0x0000:
  766.         Build_RECEIVE_ALLOCATE(convptr);
  767.         break;
  768.  
  769.       case AP_RECEIVE_ALLOCATE:
  770.         StartConversation(convptr);
  771.         Build_MC_RECEIVE_AND_WAIT(convptr);
  772.         break;
  773.  
  774.       case AP_M_RECEIVE_AND_WAIT:
  775.         if ((P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE) ||
  776.             (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM) ||
  777.             (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM_DEALL))
  778.         {
  779.           convptr->RcvCount++;
  780.         }
  781.         if ((P_M_RAW(vcbptr)->primary_rc == AP_DEALLOC_NORMAL))
  782.         {
  783.           /*******************************************************************/
  784.           /* Issue TP_ENDED every time conversation deallocated              */
  785.           /*******************************************************************/
  786.           EndConversation(convptr);
  787.           Build_TP_ENDED(convptr);
  788.         }
  789.         else if ((P_M_RAW(vcbptr)->what_rcvd == AP_CONFIRM_WHAT_RECEIVED) ||
  790.                  (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM))
  791.         {
  792.           Build_MC_CONFIRMED(convptr);
  793.           convptr->Deallocated = FALSE;
  794.         }
  795.         else if ((P_M_RAW(vcbptr)->what_rcvd == AP_CONFIRM_DEALLOCATE) ||
  796.                  (P_M_RAW(vcbptr)->what_rcvd == AP_DATA_COMPLETE_CONFIRM_DEALL))
  797.         {
  798.           Build_MC_CONFIRMED(convptr);
  799.           convptr->Deallocated = TRUE;
  800.         }
  801.         else
  802.         {
  803.           Build_MC_RECEIVE_AND_WAIT(convptr);
  804.         }
  805.         break;
  806.  
  807.       case AP_M_CONFIRMED:
  808.         if (convptr->Deallocated)
  809.         {
  810.           /*******************************************************************/
  811.           /* Issue TP_ENDED every time conversation deallocated              */
  812.           /*******************************************************************/
  813.           EndConversation(convptr);
  814.           Build_TP_ENDED(convptr);
  815.         }
  816.         else
  817.         {
  818.           Build_MC_RECEIVE_AND_WAIT(convptr);
  819.         }
  820.         break;
  821.  
  822.       case AP_TP_ENDED:
  823.         /*********************************************************************/
  824.         /* set TPEnded                                                       */
  825.         /*********************************************************************/
  826.         TPEnded = TRUE;
  827.         break;
  828.  
  829.       default:
  830.         /*********************************************************************/
  831.         /* What is this verb then ??                                         */
  832.         /*********************************************************************/
  833.         TPEnded = TRUE;
  834.         DebugBreak();
  835.         break;
  836.  
  837.     } /* Op-code switch */
  838.     SRTRFLUSH()
  839.  
  840.   }
  841.  
  842.   /***************************************************************************/
  843.   /* Now go ahead and issue the verb, if we're not finished                  */
  844.   /***************************************************************************/
  845.   if (!TPEnded)
  846.   {
  847.     convptr->async_corr = WinAsyncAPPCEx(convptr->hEvent,
  848.                           (long)(char *)(vcbptr));
  849.     if (convptr->async_corr == 0)
  850.     {
  851.       SRTRACE(t,"Thread %d:%d (recv) WinAsync call %x failed corr %p\n",
  852.               convptr->thread,convptr->conv,
  853.               vcbptr->opcode,convptr->async_corr);
  854.       convptr->TPEnded = TRUE;
  855.     }
  856.   }
  857.  
  858.   SRTRFLUSH()
  859.   return(TPEnded);
  860.  
  861. } /* Issue receive verb */
  862.  
  863. /*****************************************************************************/
  864. /* Build routines to build all required verbs                                */
  865. /*****************************************************************************/
  866.  
  867. void Build_TP_ENDED(CONVCB * convptr)
  868. {
  869.   TP_ENDED * vcbptr;
  870.   SRTRACE(t,"Thread %d:%d (%s) Build_TP_Ended\n",
  871.           convptr->thread,convptr->conv,convptr->type);
  872.   vcbptr = (TP_ENDED *) &(convptr->vcb);
  873.  
  874.   CLEARVCB
  875.  
  876.   vcbptr->opcode = AP_TP_ENDED;
  877.   memcpy(&(vcbptr->tp_id), convptr->TPid, 8);
  878.   vcbptr->type = AP_SOFT;
  879. }
  880.  
  881. void Build_RECEIVE_ALLOCATE(CONVCB * convptr)
  882. {
  883.   RECEIVE_ALLOCATE * vcbptr;
  884.   SRTRACE(t,"Thread %d:%d (%s) Build_Receive_Allocate\n",
  885.           convptr->thread,convptr->conv,convptr->type);
  886.   vcbptr = (RECEIVE_ALLOCATE *) &(convptr->vcb);
  887.  
  888.   CLEARVCB
  889.  
  890.   vcbptr->opcode = AP_RECEIVE_ALLOCATE;
  891.   memcpy(vcbptr->tp_name, LocalTPName, 64);
  892. }
  893.  
  894. void Build_MC_CONFIRMED(CONVCB * convptr)
  895. {
  896.   MC_CONFIRMED * vcbptr;
  897. //SRTRACE(t,"Thread %d:%d (%s) Build_MC_Confirmed\n",
  898. //        convptr->thread,convptr->conv,convptr->type);
  899.   vcbptr = (MC_CONFIRMED *) &(convptr->vcb);
  900.  
  901.   CLEARVCB
  902.  
  903.   vcbptr->opcode = AP_M_CONFIRMED;
  904.   vcbptr->opext = AP_MAPPED_CONVERSATION;
  905.   memcpy(&(vcbptr->tp_id),convptr->TPid, 8);
  906.   vcbptr->conv_id = convptr->Convid;
  907. }
  908.  
  909. void Build_MC_RECEIVE_AND_WAIT(CONVCB * convptr)
  910. {
  911.   MC_RECEIVE_AND_WAIT * vcbptr;
  912. //SRTRACE(t,"Thread %d:%d (%s) Build_MC_Receive_And_Wait\n",
  913. //        convptr->thread,convptr->conv,convptr->type);
  914.   vcbptr = (MC_RECEIVE_AND_WAIT *) &(convptr->vcb);
  915.  
  916.   CLEARVCB
  917.  
  918.   vcbptr->opcode = AP_M_RECEIVE_AND_WAIT;
  919.   vcbptr->opext = AP_MAPPED_CONVERSATION;
  920.   memcpy(&(vcbptr->tp_id),convptr->TPid, 8);
  921.   vcbptr->conv_id = convptr->Convid;
  922.   vcbptr->rtn_status = AP_YES;
  923.   vcbptr->max_len = RcvSize;
  924.   vcbptr->dptr = convptr->DataPtr;
  925. }
  926.  
  927. /*****************************************************************************/
  928. /* ProcessReturns - Checks return codes from the last verb to complete and   */
  929. /*                  saves conversation id and tp id in the conversation cb   */
  930. /*****************************************************************************/
  931. BOOL ProcessReturns(CONVCB * convptr)
  932. {
  933.   BOOL TPEnded = FALSE;
  934.   struct appc_hdr * vcbptr;
  935.   SYSTEMTIME st;
  936.  
  937. //SRTRACE(t,"Thread %d:%d (%s) ProcessReturns\n",
  938. //        convptr->thread,convptr->conv,convptr->type);
  939.   vcbptr = (struct appc_hdr *) &(convptr->vcb);
  940.  
  941.   GetLocalTime(&st);
  942.   if (vcbptr->primary_rc != AP_OK)
  943.   {
  944.     SRTRACE(t,"Thread %d:%d (%s) error: %s prc %4.4x src %8.8x at %d:%d:%d\n",
  945.             convptr->thread,convptr->conv,convptr->type,
  946.             VerbName[vcbptr->opcode],
  947.             APPC_FLIPI(vcbptr->primary_rc),APPC_FLIPL(vcbptr->secondary_rc),
  948.             st.wHour,st.wMinute,st.wSecond);
  949.     if ((vcbptr->opcode == AP_M_RECEIVE_AND_WAIT) &&
  950.         (vcbptr->primary_rc == AP_DEALLOC_NORMAL))
  951.     {
  952.       SRTRACE(t,"Thread %d:%d (%s) MC_RECEIVE_AND_WAIT completed with DEALLOC_NORMAL\n",\
  953.               convptr->thread,convptr->conv,convptr->type);
  954.     }
  955.     else if ((vcbptr->opcode == AP_M_CONFIRM) &&
  956.              (vcbptr->primary_rc == AP_ALLOCATION_ERROR))
  957.     {
  958.       SRTRACE(t,"Thread %d:%d (%s) MC_CONFIRM completed with ALLOCATION_ERROR\n",
  959.               convptr->thread,convptr->conv,convptr->type);
  960.     }
  961.     else
  962.     {
  963.       TPEnded = TRUE;
  964.       SRTRACE(t,"Thread %d:%d (%s) unexpected error on %s - set TPEnded\n",
  965.               convptr->thread,convptr->conv,convptr->type,
  966.               VerbName[vcbptr->opcode]);
  967.     }
  968.   }
  969.   else
  970.   {
  971.     switch (vcbptr->opcode)
  972.     {
  973.       case AP_TP_ENDED:
  974.         SRTRACE(t,"Thread %d:%d (%s) TP_Ended completed at %d:%d:%d\n",
  975.                 convptr->thread,convptr->conv,convptr->type,
  976.                 st.wHour,st.wMinute,st.wSecond);
  977.         break;
  978.  
  979.       case AP_RECEIVE_ALLOCATE:
  980.         SRTRACE(t,"Thread %d:%d (%s) Receive_Allocate completed at %d:%d:%d\n",
  981.                 convptr->thread,convptr->conv,convptr->type,
  982.                 st.wHour,st.wMinute,st.wSecond);
  983.         memcpy(convptr->TPid,&(P_RAL(vcbptr)->tp_id),8);
  984.         convptr->Convid = P_RAL(vcbptr)->conv_id;
  985.         break;
  986.  
  987.       case AP_M_RECEIVE_AND_WAIT:
  988. //      SRTRACE(t,"Thread %d:%d (%s) MC_Receive_And_Wait completed at %d:%d:%d\n",
  989. //              convptr->thread,convptr->conv,convptr->type,
  990. //              st.wHour,st.wMinute,st.wSecond);
  991.         break;
  992.  
  993.       case AP_M_CONFIRMED:
  994. SRTRACE(t,"Thread %d:%d (%s) MC_Confirmed completed at %d:%d:%d: receive count %d\n",
  995.                 convptr->thread,convptr->conv,convptr->type,
  996.                 st.wHour,st.wMinute,st.wSecond,
  997.                 convptr->RcvCount);
  998.         break;
  999.  
  1000.       default:
  1001.         SRTRACE(t,"Thread %d:%d (%s) UNKNOWN opcode - set TPEnded\n",
  1002.                 convptr->thread,convptr->conv,convptr->type);
  1003.         TPEnded = TRUE;
  1004.         DebugBreak();
  1005.         break;
  1006.     }
  1007.   }
  1008.   SRTRFLUSH()
  1009.   return(TPEnded);
  1010. }
  1011.  
  1012. /*****************************************************************************/
  1013. /* ReadConfig - Reads config info from MRCV.CFG and allocates buffer for     */
  1014. /*              receiving                                                    */
  1015. /*****************************************************************************/
  1016. void ReadConfig()
  1017. {
  1018.   char buffer[200];
  1019.  
  1020.   if (!ReadString("ResultFile",FileName,60))
  1021.   {
  1022.     strcpy(FileName,"MRCV.OUT");
  1023.   }
  1024.  
  1025.   if (!ReadString("TraceFile",TraceFile,60))
  1026.   {
  1027.     strcpy(TraceFile,"MRCV.TRC");
  1028.   }
  1029.  
  1030.   strcpy(TPName,"MRCV");
  1031.   PadString(TPName,64);
  1032.   CONV_A_TO_E(TPName,64);
  1033.  
  1034.   if (!ReadString("LocalTPName",LocalTPName,64))
  1035.   {
  1036.     strcpy(LocalTPName,"MRCVTP");
  1037.   }
  1038.   PadString(LocalTPName,64);
  1039.   CONV_A_TO_E(LocalTPName,64);
  1040.  
  1041.   RcvSize=4096;
  1042.   if (ReadString("RcvSize",buffer,200))
  1043.   {
  1044.     RcvSize=atoi(buffer);
  1045.   }
  1046.  
  1047.   NumRcvThreads = 2;
  1048.   if (ReadString("NumRcvThreads",buffer,200))
  1049.   {
  1050.     NumRcvThreads=atoi(buffer);
  1051.   }
  1052.  
  1053.   NumRcvConvs = 4;
  1054.   if (ReadString("NumRcvConvs",buffer,200))
  1055.   {
  1056.     NumRcvConvs=atoi(buffer);
  1057.   }
  1058.   NumRalcs = 0;
  1059.   RcvConvs = 0;
  1060.  
  1061.   if (NumRcvConvs > 0 && NumRcvThreads * 63 < NumRcvConvs)
  1062.   {
  1063.     NumRcvThreads = NumRcvConvs / 63 + 1;
  1064.   }
  1065.  
  1066.   if (NumRcvConvs > 0 && NumRcvThreads > NumRcvConvs)
  1067.   {
  1068.     NumRcvThreads = NumRcvConvs;
  1069.   }
  1070. }
  1071.  
  1072. /*****************************************************************************/
  1073. /* CONV_A_TO_E - ASCII to EBCDIC conversion routine                          */
  1074. /*****************************************************************************/
  1075. void CONV_A_TO_E(char * string,int length)
  1076. {
  1077.   memset(cnvtptr,0,sizeof(cnvt));
  1078.  
  1079.   cnvt.opcode       = SV_CONVERT;
  1080.   cnvt.direction    = SV_ASCII_TO_EBCDIC;
  1081.   cnvt.char_set     = SV_AE;
  1082.  
  1083.   cnvt.len          = length;
  1084.   cnvt.source       = string;
  1085.   cnvt.target       = string;
  1086.  
  1087.   ACSSVC_C((long)(char *) (cnvtptr));
  1088. }
  1089.  
  1090. /*****************************************************************************/
  1091. /* CONV_E_TO_A - EBCDIC to ASCII conversion routine                          */
  1092. /*****************************************************************************/
  1093. void CONV_E_TO_A(char * string,int length)
  1094. {
  1095.   memset(cnvtptr,0,sizeof(cnvt));
  1096.  
  1097.   cnvt.opcode       = SV_CONVERT;
  1098.   cnvt.direction    = SV_EBCDIC_TO_ASCII;
  1099.   cnvt.char_set     = SV_AE;
  1100.   cnvt.len          = length;
  1101.   cnvt.source       = string;
  1102.   cnvt.target       = string;
  1103.  
  1104.   ACSSVC_C((long)(char *) (cnvtptr));
  1105. }
  1106.  
  1107. /*****************************************************************************/
  1108. /* StartConversation - Record start time for this conversation               */
  1109. /*****************************************************************************/
  1110. void StartConversation(CONVCB * convptr)
  1111. {
  1112.   convptr->ConvStarted = GetTickCount();
  1113. }
  1114.  
  1115. /*****************************************************************************/
  1116. /* EndConversation - Calculate elapsed time for this conversation            */
  1117. /*****************************************************************************/
  1118. void EndConversation(CONVCB * convptr)
  1119. {
  1120.   if (ResultPtr != NULL)
  1121.   {
  1122.     /*************************************************************************/
  1123.     /* Make sure we don't go over the end of the allocated area              */
  1124.     /*************************************************************************/
  1125.     if (NumResults < NumRcvConvs)
  1126.     {
  1127.       *ResultPtr++ = (GetTickCount() - convptr->ConvStarted);
  1128.       NumResults++;
  1129.     }
  1130.     else
  1131.     {
  1132.       SRTRACE(t,"Thread %d:%d Too many calls to EndConversation\n",
  1133.               convptr->thread,convptr->conv);
  1134.     }
  1135.   }
  1136. }
  1137.  
  1138. /*****************************************************************************/
  1139. /* OutputResults - dump the times of conversations to file                   */
  1140. /*****************************************************************************/
  1141. void OutputResults()
  1142. {
  1143.   FILE *h = NULL;
  1144.   RESULT * ptr = NULL;
  1145.   unsigned short i = 0;
  1146.  
  1147.   h = fopen(FileName,"w");
  1148.   if (h != NULL)
  1149.   {
  1150.     fprintf(h,"\nMRCV Results\n------------\n\n");
  1151.     CONV_E_TO_A(LocalTPName,64);
  1152.     fprintf(h,"Local TP Name              = %.64s\n",LocalTPName);
  1153.     CONV_E_TO_A(RemoteTPName,64);
  1154.     fprintf(h,"Remote TP Name             = %.64s\n",RemoteTPName);
  1155.     fprintf(h,"Local LU Alias             = %.8s\n",LocalLUAlias);
  1156.     fprintf(h,"Remote LU Alias            = %.8s\n",RemoteLUAlias);
  1157.     CONV_E_TO_A(ModeName,8);
  1158.     fprintf(h,"ModeName                   = %.8s\n",ModeName);
  1159.     fprintf(h,"No. of receive threads     = %d\n",NumRcvThreads);
  1160.     fprintf(h,"No. of rcv convs started   = %d\n",NumRalcs);
  1161.     fprintf(h,"No. of rcv convs completed = %d\n",RcvConvs);
  1162.     fprintf(h,"\n");
  1163.  
  1164.     ptr = ResultBuf;
  1165.     while (ptr < ResultPtr)
  1166.     {
  1167.       fprintf(h,"Conversation number %d, time = %.3f seconds\n",i++,
  1168.                                                 (((float) *ptr++) / 1000.0 ));
  1169.     }
  1170.     fclose(h);
  1171.   }
  1172. }
  1173.  
  1174. /*****************************************************************************/
  1175. /* ReadString - Get a line of text from the config file                      */
  1176. /*****************************************************************************/
  1177. int ReadString(char * lpValueName,char * lpData, int maxlen)
  1178. {
  1179.   char       buffer[200];
  1180.   char      *p = NULL;
  1181.   FILE      *h = NULL;
  1182.   BOOL       match = FALSE;
  1183.   BOOL       eof   = FALSE;
  1184.   int        rc = 0;
  1185.   int        ch = 0;
  1186.   int        i = 0;
  1187.   BOOL       gotdata = FALSE;
  1188.   char       separators[] = " =\t\n";
  1189.  
  1190.   GetModuleFileName( hInst, buffer, sizeof(buffer) );
  1191.   lstrcpy( buffer+lstrlen(buffer) - 4, ".CFG" );
  1192.   h = fopen( buffer, "r" );
  1193.   buffer[0] = '\0';
  1194.  
  1195.   lpValueName=strupr(lpValueName);
  1196.  
  1197.   if (h != NULL)
  1198.   {
  1199.     while ((!match) && (!eof))
  1200.     {
  1201.       /***********************************************************************/
  1202.       /* Use fgetc to read a line of text from the file                      */
  1203.       /***********************************************************************/
  1204.       for (i=0; (i<sizeof(buffer))     &&
  1205.                 ((ch=getc(h)) != EOF)  &&
  1206.                 ((char)ch != '\n');
  1207.                                    i++)
  1208.       {
  1209.         buffer[i] = (char)ch;
  1210.       }
  1211.       if ((char)ch == '\n')
  1212.       {
  1213.         buffer[i++] = (char)ch;
  1214.       }
  1215.       if (ch == EOF)
  1216.       {
  1217.         eof = TRUE;
  1218.       }
  1219.       else
  1220.       {
  1221.         /*********************************************************************/
  1222.         /* Compare the first token in the line read with the requested parm  */
  1223.         /*********************************************************************/
  1224.         if (!strcmpi(strupr(strtok(buffer, separators)), lpValueName))
  1225.         {
  1226.           match = TRUE;
  1227.           /*******************************************************************/
  1228.           /* Get a pointer to the second token (the value we want)           */
  1229.           /*******************************************************************/
  1230.           p = strtok(NULL, separators);
  1231.  
  1232.           /*******************************************************************/
  1233.           /* Copy the data IF there is some                                  */
  1234.           /*******************************************************************/
  1235.           if (p != NULL)
  1236.           {
  1237.             /*****************************************************************/
  1238.             /* Force a NULL after the second token                           */
  1239.             /*****************************************************************/
  1240.             strtok(NULL, separators);
  1241.  
  1242.             /*****************************************************************/
  1243.             /* Copy the data                                                 */
  1244.             /*****************************************************************/
  1245.             strncpy(lpData, p, maxlen);
  1246.             gotdata = TRUE;
  1247.           }
  1248.           else
  1249.           {
  1250.             gotdata = FALSE;
  1251.           }
  1252.         }
  1253.       }
  1254.     }
  1255.  
  1256.     if (gotdata)
  1257.     {
  1258.       rc = 1;
  1259.     }
  1260.  
  1261.     fclose(h);
  1262.  
  1263.   }
  1264.  
  1265.   return(rc);
  1266. }
  1267.  
  1268. /*****************************************************************************/
  1269. /* PadString - Remove terminating NULL  and pad on the right with spaces     */
  1270. /*****************************************************************************/
  1271. void PadString(char * string,int length)
  1272. {
  1273.   char * p;
  1274.   if ((p=memchr(string,'\0',length)) != NULL)
  1275.   {
  1276.     while (p < string+length)
  1277.     {
  1278.       *p++=' ';
  1279.     }
  1280.   }
  1281. }
  1282.  
  1283.