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 / aremote / appclib.c next >
Text File  |  1997-04-09  |  29KB  |  821 lines

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <winappc.h>
  6. #include <wincsv.h>
  7. #include "appclib.h"
  8. #include "queue.h"
  9.  
  10. #define memalloc(size) LocalAlloc(LMEM_FIXED, size)
  11. #define memfree(pointer) LocalFree(pointer)
  12.  
  13. // --- internal functions used by the module ---
  14. void appcerror(vcb_t vcb);
  15. BOOL callappc(vcb_t *vcb);
  16. void conv_a_to_e(char *string, int length);
  17. void memcpypad(char *dest, TCHAR *source, int length);
  18. void memcpya2e(char *dest, TCHAR *source, int length);
  19. int do_receive_and_wait(char *tp_id, unsigned long int conv_id,
  20.                         void *data, int maxlen, BOOL *dealloc); 
  21. void do_send_data(char *tp_id, unsigned long int conv_id, void *data, int len);
  22. void do_confirm(char *tp_id, unsigned long int conv_id);
  23. void do_prepare_to_receive(char *tp_id, unsigned long int conv_id);
  24. void do_get_fqplu_name(char *tp_id, unsigned long int conv_id, 
  25.                        char *fqplu_name);
  26. void do_tp_started(TCHAR *lu_alias, TCHAR *tp_name, char *tp_id);
  27. unsigned long int do_mc_allocate(TCHAR *lu_alias, TCHAR *tp_name, 
  28.                                  TCHAR *mode_name, char *tp_id);
  29. void do_deallocate(char *tp_id, unsigned long int conv_id);
  30. void do_tp_ended(char *tp_id);
  31.  
  32. queue       *pairq;                     // queue of sessions that have
  33.                                         // both read and write established
  34. HANDLE      hPairqSem;                  // semaphore of number of objects
  35.                                         // in pairq;
  36. HANDLE      hListenThread = NULL;       // handle to listenthread()
  37. HANDLE        hShutdownEvent = NULL;        // event to signal that the application
  38.                                         // is shutting down
  39. int            outstanding;                // number of outstanding APPC reqs
  40. HANDLE        hOutstandingZero = NULL;    // event set when outstanding goes to
  41.                                         // zero
  42. CRITICAL_SECTION    csPairq;            // critical section for pairq
  43.  
  44. /*
  45.  * convert string from ascii to ebcdic (overwriting the original string).
  46.  */
  47. void conv_a_to_e(char *string, int length) {
  48.     struct convert cnvt;
  49.  
  50.     memset(&cnvt, 0, sizeof(cnvt));
  51.     cnvt.opcode = SV_CONVERT;
  52.     cnvt.direction = SV_ASCII_TO_EBCDIC;
  53.     cnvt.char_set = SV_AE;
  54.     cnvt.len = length;
  55.     cnvt.source = string;
  56.     cnvt.target = string;
  57.  
  58.     ACSSVC_C((long) &cnvt);
  59. }
  60.  
  61. /*
  62.  * copy a string from <source> to <dest>, padding the end of it with spaces so
  63.  * it takes <length> bytes.
  64.  *
  65.  * input is assumed to be Unicode if UNICODE is defined, output is always Ansi
  66.  */
  67. void memcpypad(char *dest, TCHAR *source, int length) {
  68. #ifdef UNICODE
  69.     char ansisrc[1024];
  70.  
  71.     // convert the source string from Unicode to ANSI
  72.     wcstombs(ansisrc, source, 1024);
  73.  
  74.     // copy the ANSI string into the destination
  75.     memset(dest, ' ', length);
  76.     memcpy(dest, ansisrc, strlen(ansisrc));
  77. #else
  78.     memset(dest, ' ', length);
  79.     memcpy(dest, source, strlen(source));
  80. #endif
  81. }
  82.  
  83. /*
  84.  * copy a string from <source> to <dest>, padding the end of it with spaces 
  85.  * so it takes <length> bytes.  convert the <dest> string from ascii to ebcdic
  86.  *
  87.  * input is assumed to be Unicode if UNICODE is defined, output is always Ansi
  88.  */
  89. void memcpya2e(char *dest, TCHAR *source, int length) {
  90. #ifdef UNICODE
  91.     char ansisrc[1024];
  92.  
  93.     // convert the source string from Unicode to ANSI
  94.     wcstombs(ansisrc, source, 1024);
  95.  
  96.     // copy the ANSI string into the destination
  97.     memset(dest, ' ', length);
  98.     memcpy(dest, ansisrc, strlen(ansisrc));
  99.  
  100.     // convert ansi to ebcdic
  101.     conv_a_to_e(dest, length);
  102. #else
  103.     memset(dest, ' ', length);
  104.     memcpy(dest, source, strlen(source));
  105.     conv_a_to_e(dest, length);
  106. #endif
  107. }
  108.  
  109. /*
  110.  * a standard error handler for APPC errors.  prints out the last verb and the
  111.  * two return codes
  112.  */
  113. void appcerror(vcb_t vcb) {
  114.     char errorbuf[1024];
  115.  
  116.     printf("\nAPPC Error: Verb = 0x%0x   PriRC = 0x%0x   SecRC = 0x%0x\n",
  117.         APPC_FLIPI(vcb.hdr.opcode), APPC_FLIPI(vcb.hdr.primary_rc), 
  118.         APPC_FLIPL(vcb.hdr.secondary_rc));
  119.  
  120.     GetAppcReturnCode((struct appc_hdr *) &(vcb.hdr), 1024, errorbuf);
  121.  
  122.     printf("\n%s\n", errorbuf);
  123.  
  124.     appcdestroy();
  125.  
  126.     ExitProcess(1);
  127. }
  128.  
  129. /*
  130.  * initialize APPC.  all programs should call this before using any 
  131.  * APPC functions
  132.  */
  133. int appcinit(void) {
  134.     WAPPCDATA APPCData;
  135.  
  136.     // create the shutdown event
  137.     hShutdownEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  138.     if (hShutdownEvent == NULL) return -1;
  139.  
  140.     // create the event that is triggered when outstanding goes to zero
  141.     // this only gets set when hShutdownEvent has been set and all requests
  142.     // are being cancelled
  143.     hOutstandingZero = CreateEvent(NULL, FALSE, FALSE, NULL);
  144.     if (hOutstandingZero == NULL) return -1;
  145.  
  146.     return WinAPPCStartup(WINAPPCVERSION, &APPCData);
  147. }
  148.  
  149. /*
  150.  * kill APPC.  this should be called when a program is done using APPC
  151.  */
  152. BOOL WINAPI appcdestroy(void) {
  153.     // if there are any outstanding requests
  154.     if (outstanding > 0) {
  155.         // tell all threads to cancel their outstanding requests.  when the last
  156.         // one cancels its request it will set hOutstandingZero.
  157.         SetEvent(hShutdownEvent);
  158.         // wait up to 60 seconds for all outstanding requests to be canceled
  159.         while 
  160.             ((WaitForSingleObject(hOutstandingZero, 20000) == WAIT_OBJECT_0) &&
  161.              (outstanding > 0));
  162.     }
  163.  
  164.     // tell WinAPPC that we're done with it
  165.     return WinAPPCCleanup();
  166. }
  167.  
  168. // a wrapper around APPC which handles hShutdownEvent for clean shutdowns
  169. BOOL callappc(vcb_t *vcb) {
  170.     HANDLE hAsync, objs[2];
  171.     DWORD event;
  172.  
  173.     // create an event handle
  174.     objs[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
  175.  
  176.     InterlockedIncrement(&outstanding);
  177.  
  178.     // make the APPC call
  179.     ASYNCAPPC(objs[0], vcb, hAsync);
  180.     // see if the async APPC call failed
  181.     if (hAsync == 0) {
  182.         InterlockedDecrement(&outstanding);
  183.         SetLastError(APPCLIB_ASYNC_FAILED);
  184.         return FALSE;
  185.     }
  186.  
  187.     // wait for either the APPC call to complete or the hShutdownEvent to be
  188.     // triggered
  189.     objs[1] = hShutdownEvent;
  190.     event = WaitForMultipleObjects(2, objs, FALSE, INFINITE) - WAIT_OBJECT_0;
  191.     switch (event) {
  192.         case 0:                            // APPC call completed
  193.             InterlockedDecrement(&outstanding);
  194.             break;
  195.         case 1:                            // shutdown event triggered
  196.             // cancel the outstanding request
  197.             WinAPPCCancelAsyncRequest(hAsync);
  198.             // if this is the last cancel than let appcdestroy() know that
  199.             if (InterlockedDecrement(&outstanding) <= 0) {
  200.                 SetEvent(hOutstandingZero);
  201.             }
  202.             break;
  203.         case WAIT_FAILED:
  204.             SetLastError(APPCLIB_NO_INIT);
  205.             return FALSE;
  206.     }
  207.     CloseHandle(objs[0]);
  208.     CloseHandle(hAsync);
  209.  
  210.     return TRUE;
  211. }
  212.  
  213. /*
  214.  * execute a MC_RECEIVE_AND_WAIT verb with the given TP ID and Conversation ID
  215.  * data is copied into the buffer at <data> for a maximum of <maxlen> bytes.  
  216.  * Returns the number of bytes actually read.
  217.  *
  218.  * If the sender requests confirmation a MC_CONFIRM verb is send to them.
  219.  * if the conversation gets deallocated then dealloc is set to true.
  220.  */
  221. int do_receive_and_wait(char *tp_id, unsigned long int conv_id, 
  222.                         void *data, int maxlen, BOOL *dealloc) {
  223.     vcb_t vcb;
  224.     int read;
  225.  
  226.     CLEARVCB(vcb);
  227.  
  228.     *dealloc = FALSE;
  229.  
  230.     vcb.rcvwait.opcode = AP_M_RECEIVE_AND_WAIT;
  231.     vcb.rcvwait.opext = AP_MAPPED_CONVERSATION;
  232.     memcpy(&(vcb.rcvwait.tp_id), tp_id, 8);
  233.     vcb.rcvwait.conv_id = conv_id;
  234.     vcb.rcvwait.rtn_status = AP_YES;
  235.     vcb.rcvwait.max_len = maxlen;
  236.     vcb.rcvwait.dptr = data;
  237.  
  238.     callappc(&vcb);                                         
  239.     APPCDBG(printf("vcb.rcvwait.what_rcvd = 0x%x\n", APPC_FLIPI(vcb.rcvwait.what_rcvd)));
  240.     APPCDBG(printf("vcb.rcvwait.primary_rc = 0x%x\n", APPC_FLIPI(vcb.rcvwait.primary_rc)));
  241.     APPCDBG(printf("vcb.rcvwait.dlen = %i\n", vcb.rcvwait.dlen));
  242.     read = vcb.rcvwait.dlen;
  243.  
  244.     if ((vcb.rcvwait.primary_rc == AP_DEALLOC_NORMAL) || 
  245.         (vcb.rcvwait.primary_rc == AP_DEALLOC_ABEND)) {
  246.         do_tp_ended(tp_id);
  247.         *dealloc = TRUE;
  248.     } else if (vcb.rcvwait.primary_rc != AP_OK) {
  249.         appcerror(vcb);
  250.     } else if ((vcb.rcvwait.what_rcvd == AP_CONFIRM_DEALLOCATE) ||
  251.                (vcb.rcvwait.what_rcvd == AP_DATA_COMPLETE_CONFIRM_DEALL)) {
  252.         /* confirm deallocation */
  253.         CLEARVCB(vcb);
  254.         vcb.confirmed.opcode = AP_M_CONFIRMED;
  255.         vcb.confirmed.opext = AP_MAPPED_CONVERSATION;
  256.         memcpy(vcb.confirmed.tp_id, tp_id, 8);
  257.         vcb.confirmed.conv_id = conv_id;
  258.         callappc(&vcb);
  259.         if (vcb.confirmed.primary_rc != AP_OK) appcerror(vcb);
  260.  
  261.         do_tp_ended(tp_id);
  262.         *dealloc = TRUE;
  263.     } else if ((vcb.rcvwait.what_rcvd == AP_CONFIRM_WHAT_RECEIVED) || 
  264.                (vcb.rcvwait.what_rcvd == AP_CONFIRM_SEND) ||
  265.                (vcb.rcvwait.what_rcvd == AP_DATA_COMPLETE_CONFIRM) || 
  266.                (vcb.rcvwait.what_rcvd == AP_DATA_CONFIRM)) {
  267.         /* do a confirmation if one was requested */
  268.         CLEARVCB(vcb);
  269.         vcb.confirmed.opcode = AP_M_CONFIRMED;
  270.         vcb.confirmed.opext = AP_MAPPED_CONVERSATION;
  271.         memcpy(vcb.confirmed.tp_id, tp_id, 8);
  272.         vcb.confirmed.conv_id = conv_id;
  273.         callappc(&vcb);
  274.         if (vcb.confirmed.primary_rc != AP_OK) appcerror(vcb);
  275.     }
  276.     
  277.     return read;
  278. }
  279.  
  280. /*
  281.  * do a MC_SEND_DATA verb with the given TP ID, Conversation ID and <len> 
  282.  * bytes of data at the <data> pointer.  asks for a confirmation upon
  283.  * sending.
  284.  */
  285. void do_send_data(char *tp_id, unsigned long int conv_id, void *data, int len) {
  286.     vcb_t vcb;
  287.  
  288.     CLEARVCB(vcb);
  289.  
  290.     vcb.snddata.opcode = AP_M_SEND_DATA;
  291.     vcb.snddata.opext = AP_MAPPED_CONVERSATION;
  292.     memcpy(vcb.snddata.tp_id, tp_id, 8);
  293.     vcb.snddata.conv_id = conv_id;
  294.     vcb.snddata.dlen = len;
  295.     vcb.snddata.dptr = data;
  296.     vcb.snddata.type = AP_SEND_DATA_CONFIRM;
  297.  
  298.     callappc(&vcb);
  299.  
  300.     if (vcb.snddata.primary_rc != AP_OK) appcerror(vcb);
  301. }
  302.  
  303. /*
  304.  * ask for a confirmation on a given TP ID and Conversation ID.  used for
  305.  * syncronizing data flow.
  306.  */
  307. void do_confirm(char *tp_id, unsigned long int conv_id) {
  308.     vcb_t vcb;
  309.  
  310.     CLEARVCB(vcb);
  311.     vcb.confirm.opcode = AP_M_CONFIRM;
  312.     vcb.confirm.opext = AP_MAPPED_CONVERSATION;
  313.     memcpy(vcb.confirm.tp_id, tp_id, 8);
  314.     vcb.confirm.conv_id = conv_id;
  315.         
  316.     callappc(&vcb);
  317.  
  318.     if (vcb.confirm.primary_rc != AP_OK) appcerror(vcb);
  319. }
  320.  
  321. /*
  322.  * switch the conversation into a receive state and ask the partner TP
  323.  * to confirm this.
  324.  */
  325. void do_prepare_to_receive(char *tp_id, unsigned long int conv_id) {
  326.     vcb_t vcb;
  327.  
  328.     CLEARVCB(vcb);
  329.     vcb.torec.opcode = AP_M_PREPARE_TO_RECEIVE;
  330.     vcb.torec.opext = AP_MAPPED_CONVERSATION;
  331.     vcb.torec.ptr_type = AP_SYNC_LEVEL;
  332.     vcb.torec.locks = AP_SHORT;
  333.     memcpy(vcb.torec.tp_id, tp_id, 8);
  334.     vcb.torec.conv_id = conv_id;
  335.         
  336.     callappc(&vcb);
  337.  
  338.     if (vcb.confirm.primary_rc != AP_OK) appcerror(vcb);
  339. }   
  340.  
  341. /*
  342.  * wait on a TP name for a MC_ALLOCATE request.  the TP ID is written into
  343.  * the buffer at <tp_id>, the conversation ID is returned.
  344.  */
  345. void do_get_fqplu_name(char *tp_id, unsigned long int conv_id, 
  346.                        char *fqplu_name) {
  347.     vcb_t vcb;
  348.  
  349.     CLEARVCB(vcb);
  350.     vcb.getattrib.opcode = AP_M_GET_ATTRIBUTES;
  351.     vcb.getattrib.opext = AP_MAPPED_CONVERSATION;
  352.     memcpy(vcb.getattrib.tp_id, tp_id, 8);
  353.     vcb.getattrib.conv_id = conv_id;
  354.     
  355.     callappc(&vcb);
  356.     if (vcb.rcvalloc.primary_rc != AP_OK) appcerror(vcb);
  357.  
  358.     memcpy(fqplu_name, vcb.getattrib.fqplu_name, 17);
  359. }
  360.  
  361. /*
  362.  * start a new TP.  this registers the LU alias and TP name for your TP
  363.  * to use and returns a TP ID
  364.  */
  365. void do_tp_started(TCHAR *lu_alias, TCHAR *tp_name, char *tp_id) {
  366.     vcb_t vcb;
  367.  
  368.     CLEARVCB(vcb);
  369.     vcb.tpstart.opcode = AP_TP_STARTED;
  370.     memcpypad(vcb.tpstart.lu_alias, lu_alias, 8);
  371.     memcpya2e(vcb.tpstart.tp_name, tp_name, 64);
  372.  
  373.     callappc(&vcb);
  374.     if (vcb.tpstart.primary_rc != AP_OK) appcerror(vcb);
  375.  
  376.     memcpy(tp_id, vcb.tpstart.tp_id, 8);
  377. }
  378.  
  379. /*
  380.  * allocate a conversation with a partner TP.  This takes the partner TP's
  381.  * alias, tp name, mode name, and your TP ID and returns a conversation ID.
  382.  *
  383.  * it does a MC_CONFIRM directly afterwords to see if the conversation was
  384.  * really allocated.  if it wasn't it sleeps for 100ms and tries again for
  385.  * up to 20 retries (about 2 seconds).
  386.  */
  387. unsigned long int do_mc_allocate(TCHAR *lu_alias, TCHAR *tp_name, 
  388.                                  TCHAR *mode_name, char *tp_id) {
  389.     vcb_t vcb;
  390.     int tries = 0;
  391.     unsigned long int conv_id;
  392.  
  393.     do {
  394.         CLEARVCB(vcb);
  395.         vcb.allocate.opcode = AP_M_ALLOCATE;
  396.         vcb.allocate.opext = AP_MAPPED_CONVERSATION;
  397.         memcpy(vcb.allocate.tp_id, tp_id, 8);
  398.         vcb.allocate.sync_level = AP_CONFIRM_SYNC_LEVEL;
  399.         vcb.allocate.rtn_ctl = AP_WHEN_SESSION_ALLOCATED;
  400.     
  401.         memcpypad(vcb.allocate.plu_alias, lu_alias, 8);
  402.         memcpya2e(vcb.allocate.mode_name, mode_name, 8);
  403.         memcpya2e(vcb.allocate.tp_name, tp_name, 64);
  404.         vcb.allocate.security = AP_NONE;
  405.  
  406.         callappc(&vcb);
  407.         conv_id = vcb.allocate.conv_id;
  408.         if (vcb.allocate.primary_rc != AP_OK) appcerror(vcb);
  409.  
  410.         if (vcb.allocate.primary_rc == AP_ALLOCATION_ERROR) Sleep(100);
  411.     } while ((tries++ < 20) && 
  412.              (vcb.allocate.primary_rc == AP_ALLOCATION_ERROR));
  413.     
  414.     if (vcb.allocate.primary_rc != AP_OK) appcerror(vcb);
  415.                
  416.     return conv_id;
  417. }
  418.  
  419. /*
  420.  * deallocate a conversation.  should only be called with a TP ID and
  421.  * conversation ID that are in the send state.
  422.  */
  423. void do_deallocate(char *tp_id, unsigned long int conv_id) {
  424.     vcb_t vcb;
  425.  
  426.     CLEARVCB(vcb);
  427.     vcb.dealloc.opcode = AP_M_DEALLOCATE;
  428.     vcb.dealloc.opext = AP_MAPPED_CONVERSATION;
  429.     memcpy(vcb.confirm.tp_id, tp_id, 8);
  430.     vcb.confirm.conv_id = conv_id;
  431.     vcb.dealloc.dealloc_type = AP_SYNC_LEVEL;
  432.  
  433.     callappc(&vcb);
  434.  
  435.     if (vcb.dealloc.primary_rc != AP_OK) appcerror(vcb);
  436. }
  437.  
  438. void do_tp_ended(char *tp_id) {
  439.     vcb_t vcb;
  440.  
  441.     CLEARVCB(vcb);
  442.     vcb.tpend.opcode = AP_TP_ENDED;
  443.     vcb.tpend.type = AP_SOFT;
  444.     memcpy(vcb.confirm.tp_id, tp_id, 8);
  445.  
  446.     callappc(&vcb);
  447.  
  448.     if (vcb.dealloc.primary_rc != AP_OK) appcerror(vcb);
  449. }
  450.  
  451. DWORD WINAPI listenthread(TCHAR *tpname) {
  452.     vcb_t       vcb_r, vcb_w;               // verb control block for each sess
  453.     TCHAR       tpname_r[9], tpname_w[9];   // tp name for each session
  454.     HANDLE      h_event_r, h_event_w;       // event handles for each session
  455.     HANDLE      h_async_r, h_async_w;       // async task handles for each sess
  456.     HANDLE      objs[3];                    // array of handles to wait for
  457.     DWORD       event;                      // which event returned
  458.     tpconvid_t  tpconv;                     // temporary tpconv pointer
  459.     name_time_t name_time;                  // logical unit of work & time key
  460.     int         dread;
  461.     BOOL        dealloc;                    // deallocation flag on session
  462.     queue       *readq;                     // queue of sessions that have
  463.                                             // read establish, but not write
  464.     readnode_t  readnode;                   // node for readq
  465.     unsigned long int w_conv_id;            // conversation id for write sess
  466.     unsigned char w_tp_id[8];               // tp_id for write session
  467.     qnode       *n;                         // temp node for searching queues
  468.  
  469.     // create the event handles for each session
  470.     h_event_r = CreateEvent(NULL, FALSE, FALSE, NULL);
  471.     h_event_w = CreateEvent(NULL, FALSE, FALSE, NULL);
  472.     if ((h_event_r == NULL) || (h_event_w == NULL)) {
  473.         printf("appclib: CreateEvent() failed near line %i\n", __LINE__);
  474.         return FALSE;
  475.     }
  476.  
  477.     // create the q
  478.     readq = newq();
  479.     if (readq == NULL) {
  480.         printf("appclib: newq() failed near line %i\n", __LINE__);
  481.         return FALSE;
  482.     }
  483.  
  484.     // we actually use two different TP names, one for each session.  
  485.     // the name that the server reads on ends in a R, the name that the
  486.     // server writes on ends in a W
  487.  
  488.     //
  489.     // last minute change to ensure we work as an autostarted TP where
  490.     // the tp name we issue in the rcv_alloc must match the TP name we
  491.     // registered as with Service Control Manager.  On the Server read
  492.     // tp we do not alter the name.  The server write TP is still altered.
  493.     // will most likely not work if the TP name is 8 chars long and ends
  494.     // in a W.
  495.     //
  496.     memcpy(tpname_r, tpname, 8 * sizeof(TCHAR)); 
  497. //    tpname_r[7] = 0; 
  498. //    lstrcat(tpname_r, TEXT("R"));
  499.     
  500.     memcpy(tpname_w, tpname, 7 * sizeof(TCHAR)); 
  501.     tpname_w[7] = 0; 
  502.     lstrcat(tpname_w, TEXT("W"));
  503.  
  504.     // start an receive_allocate on the read tp
  505.     InterlockedIncrement(&outstanding);
  506.     CLEARVCB(vcb_r);
  507.     vcb_r.rcvalloc.opcode = AP_RECEIVE_ALLOCATE;
  508.     memcpya2e(vcb_r.rcvalloc.tp_name, tpname_r, 64);
  509.     ASYNCAPPC(h_event_r, &vcb_r, h_async_r);
  510.     if (h_async_r == 0) {
  511.         InterlockedDecrement(&outstanding);
  512.         return FALSE;
  513.     }
  514.     
  515.     // start an receive_allocate on the write tp
  516.     InterlockedIncrement(&outstanding);
  517.     CLEARVCB(vcb_w);
  518.     vcb_w.rcvalloc.opcode = AP_RECEIVE_ALLOCATE;
  519.     memcpya2e(vcb_w.rcvalloc.tp_name, tpname_w, 64);
  520.     ASYNCAPPC(h_event_w, &vcb_w, h_async_w);
  521.     if (h_async_w == 0) {
  522.         InterlockedDecrement(&outstanding);
  523.         printf("appclib: WinAsyncAPPCEx failed with verb 0x%x near line %i\n", 
  524.             APPC_FLIPI(vcb_r.rcvalloc.opcode), __LINE__);
  525.         return FALSE;
  526.     }
  527.  
  528.     while (TRUE) {
  529.         objs[0] = h_event_r;
  530.         objs[1] = h_event_w;
  531.         objs[2] = hShutdownEvent;
  532.         event = WaitForMultipleObjects(3, objs, FALSE, INFINITE)-WAIT_OBJECT_0;
  533.         switch (event) {
  534.             case 0:                        // someone connected to reader
  535.                 InterlockedDecrement(&outstanding);
  536.  
  537.                 // check the APPC return codes
  538.                 if (vcb_r.rcvalloc.primary_rc != AP_OK) appcerror(vcb_r);
  539.  
  540.                 // allocate a new readnode struct and the tpconv structure
  541.                 // inside it.  save the tp_id and conv_id into the tpconv 
  542.                 // structure
  543.                 readnode = (readnode_t) memalloc(sizeof(struct readnode_st));
  544.                 readnode->tpconv = 
  545.                     (tpconvid_t) memalloc(sizeof(struct tpconvid_st));
  546.                 memcpy(readnode->tpconv->r_tp_id, vcb_r.rcvalloc.tp_id, 8);
  547.                 readnode->tpconv->r_conv_id = vcb_r.rcvalloc.conv_id;
  548.                 
  549.                 // start another receive_allocate on the read TP
  550.                 InterlockedIncrement(&outstanding);
  551.                 CLEARVCB(vcb_r);
  552.                 vcb_r.rcvalloc.opcode = AP_RECEIVE_ALLOCATE;
  553.                 memcpya2e(vcb_r.rcvalloc.tp_name, tpname_r, 64);
  554.                 ASYNCAPPC(h_event_r, &vcb_r, h_async_r);
  555.                 if (h_async_r == 0) {
  556.                     InterlockedDecrement(&outstanding);
  557.                     return FALSE;
  558.                 }
  559.  
  560.                 // receive_and_wait for the name_time key for this session
  561.                 dread = do_receive_and_wait(readnode->tpconv->r_tp_id, 
  562.                     readnode->tpconv->r_conv_id, &(readnode->name_time), 
  563.                     sizeof(name_time_t), &dealloc);
  564.                 if ((dread != sizeof(name_time_t)) || (dealloc)) {
  565.                     // close session
  566.                     memfree(readnode->tpconv);
  567.                     memfree(readnode);
  568.                     break;
  569.                 }
  570.  
  571.                 // add all of this to the readq
  572.                 addtoq(readq, (void *) readnode);
  573.                 break;
  574.             case 1:                            // someone connected to writer
  575.                 InterlockedDecrement(&outstanding);
  576.  
  577.                 // check the APPC return codes
  578.                 if (vcb_w.rcvalloc.primary_rc != AP_OK) appcerror(vcb_w);
  579.  
  580.                 // save the tp id and conversation id for this session
  581.                 memcpy(w_tp_id, vcb_w.rcvalloc.tp_id, 8);
  582.                 w_conv_id = vcb_w.rcvalloc.conv_id;
  583.  
  584.                 // start another receive_allocate on the write TP
  585.                 InterlockedIncrement(&outstanding);
  586.                 CLEARVCB(vcb_w);
  587.                 vcb_w.rcvalloc.opcode = AP_RECEIVE_ALLOCATE;
  588.                 memcpya2e(vcb_w.rcvalloc.tp_name, tpname_w, 64);
  589.                 ASYNCAPPC(h_event_w, &vcb_w, h_async_w);
  590.                 if (h_async_w == 0) {
  591.                     InterlockedDecrement(&outstanding);
  592.                     return FALSE;
  593.                 }
  594.  
  595.                 // receive and wait for the name_time key for this session
  596.                 dread = do_receive_and_wait(w_tp_id, w_conv_id, &name_time,
  597.                     sizeof(name_time_t), &dealloc);
  598.                 if ((dread != sizeof(name_time_t)) || (dealloc)) {
  599.                     // close session
  600.                     break;
  601.                 }
  602.  
  603.                 // find the matching name_time key in the readq
  604.                 for (n = readq->head; n != NULL; n = n->next) {
  605.                     if (memcmp(&name_time, 
  606.                         &(((readnode_t) (n->ptr))->name_time), 
  607.                         sizeof(name_time_t)) == 0) break;
  608.                 }
  609.                 if (n == NULL) {
  610.                     // close session
  611.                     break;
  612.                 }
  613.  
  614.                 readnode = (readnode_t) removeqnodefromq(readq, n);
  615.                 tpconv = readnode->tpconv;
  616.                 memfree(readnode);
  617.                 memcpy(tpconv->w_tp_id, w_tp_id, 8);
  618.                 tpconv->w_conv_id = w_conv_id;
  619.                 tpconv->r_valid = TRUE;
  620.                 tpconv->w_valid = TRUE;
  621.  
  622.                 // receive and wait to confirm data turnaround
  623.                 do_receive_and_wait(tpconv->w_tp_id, tpconv->w_conv_id, 
  624.                     NULL, 0, &dealloc);
  625.  
  626.                 // add it to the pairq and signal that there is one more
  627.                 // pair available
  628.                 EnterCriticalSection(&csPairq);
  629.                 addtoq(pairq, (void *) tpconv);
  630.                 ReleaseSemaphore(hPairqSem, 1, NULL);
  631.                 LeaveCriticalSection(&csPairq);
  632.                 break;
  633.             case 2:                            // shutdown event
  634.                 WinAPPCCancelAsyncRequest(h_async_r);
  635.                 InterlockedDecrement(&outstanding);
  636.                 WinAPPCCancelAsyncRequest(h_async_w);
  637.                 if (InterlockedDecrement(&outstanding) <= 0) 
  638.                     SetEvent(hOutstandingZero);
  639.                 return FALSE;
  640.         }
  641.     }
  642. }
  643.  
  644. /*
  645.  * ---- functions to setup easy two-way APPC communication ----
  646.  */
  647.  
  648. /*
  649.  * listen on a TP name for incoming conversations.
  650.  *
  651.  * each TP can only listen to one tpname at a time (because this only
  652.  * spawns one listenthread).  If a TP needs to be written that can handle
  653.  * listening to multiple TP names in one run then this will need to be 
  654.  * modified to make one listenthread() for each appclisten() that gets
  655.  * called with a unique TP name.
  656.  *
  657.  * returns a tpconvid that can be used with appcread, appcwrite and 
  658.  * appcclose.
  659.  */
  660. tpconvid_t appclisten(TCHAR *tpname) {
  661.     tpconvid_t tpconv;
  662.     DWORD tid;
  663.  
  664.     APPCDBG(printf("appclisten(%ws) starting\n", tpname));
  665.  
  666.     if (hListenThread == NULL) {
  667.         APPCDBG(printf("appclisten: creating listenthread\n"));
  668.         // create the semaphore for the pairq
  669.         hPairqSem = CreateSemaphore(NULL, 0, 100000, NULL);
  670.         if (hPairqSem == NULL) return NULL;
  671.  
  672.         // create the pairq
  673.         pairq = newq();
  674.         if (pairq == NULL) return NULL;
  675.  
  676.         // create a critical section for dealing with the pairq
  677.         InitializeCriticalSection(&csPairq);
  678.     
  679.         // create the listen thread
  680.         hListenThread = CreateThread(NULL, 0, 
  681.             (LPTHREAD_START_ROUTINE) listenthread, tpname, 0, &tid);
  682.         if (hListenThread == NULL) return NULL;
  683.     }
  684.  
  685.     // wait for the listen thread to put a tpconv pair into the pairq
  686.     WaitForSingleObject(hPairqSem, INFINITE);
  687.  
  688.     // remove the first tpconv pair from the pairq and return it
  689.     EnterCriticalSection(&csPairq);
  690.     tpconv = removeheadfromq(pairq);
  691.     LeaveCriticalSection(&csPairq);
  692.  
  693.     APPCDBG(printf("appclisten done\n", tpname));
  694.  
  695.     return tpconv;
  696. }
  697.  
  698. /*
  699.  * connect to a remote server.  this allocates two conversations, one
  700.  * to write on and one to read on.  
  701.  *
  702.  * the LU's should be listed as partners under SNA Admin with a mode name of
  703.  * <mode_name> between them.
  704.  *
  705.  * returns a tpconvid which can be used with appcread, appcwrite, and 
  706.  * appcclose.
  707.  */
  708. tpconvid_t appcconnect(TCHAR *lu_alias, TCHAR *plu_alias, 
  709.                        TCHAR *tp_name, TCHAR *loc_tp_name, TCHAR *mode_name) {
  710.     tpconvid_t  tpconv;
  711.     name_time_t name_time;
  712.     TCHAR       tpname_w[9], tpname_r[9];
  713.     TCHAR       loctpname_w[9], loctpname_r[9];
  714.  
  715.  
  716.     APPCDBG(printf("appcconnect(%ws, %ws, %ws, %ws) starting\n", lu_alias, plu_alias, tp_name, mode_name));
  717.  
  718.     // we actually use two different TP names, one for each session.  
  719.     // the name that the server reads on ends in a R, the name that the
  720.     // server writes on ends in a W
  721.     memcpy(tpname_r, tp_name, 8 * sizeof(TCHAR)); 
  722. //    tpname_r[7] = 0; 
  723. //    lstrcat(tpname_r, TEXT("R"));
  724.     
  725.     memcpy(tpname_w, tp_name, 7 * sizeof(TCHAR)); 
  726.     tpname_w[7] = 0; 
  727.     lstrcat(tpname_w, TEXT("W"));
  728.  
  729.     memcpy(loctpname_r, loc_tp_name, 8 * sizeof(TCHAR)); 
  730. //    loctpname_r[7] = 0; 
  731. //    lstrcat(loctpname_r, TEXT("R"));
  732.     
  733.     memcpy(loctpname_w, loc_tp_name, 7 * sizeof(TCHAR)); 
  734.     loctpname_w[7] = 0; 
  735.     lstrcat(loctpname_w, TEXT("W"));
  736.  
  737.     tpconv = (tpconvid_t) memalloc(sizeof(struct tpconvid_st));
  738.     if (tpconv == NULL) {
  739.         printf("appclib: couldn't allocate memory for tpconvid_t\n");
  740.         exit(1);
  741.     }
  742.  
  743.     // connect the first session
  744.     do_tp_started(lu_alias, loctpname_w, tpconv->w_tp_id);
  745.     tpconv->w_conv_id = do_mc_allocate(plu_alias, tpname_r, mode_name, 
  746.         tpconv->w_tp_id);
  747.     // fill in the logical unit of work/time structure
  748.     do_get_fqplu_name(tpconv->w_tp_id, tpconv->w_conv_id, name_time.fqplu_name);
  749.     name_time.time = GetTickCount();
  750.     // send the fqplu name/time information
  751.     do_send_data(tpconv->w_tp_id, tpconv->w_conv_id, (void *) &name_time, 
  752.         sizeof(name_time_t));
  753.     tpconv->w_valid = TRUE;
  754.  
  755.     // connect the second session
  756.     do_tp_started(lu_alias, loctpname_r, tpconv->r_tp_id);
  757.     tpconv->r_conv_id = do_mc_allocate(plu_alias, tpname_w, mode_name, 
  758.         tpconv->r_tp_id);
  759.     // send the fqplu name/time information
  760.     do_send_data(tpconv->r_tp_id, tpconv->r_conv_id, (void *) &name_time, 
  761.         sizeof(name_time_t));
  762.     // reverse the conversation
  763.     do_prepare_to_receive(tpconv->r_tp_id, tpconv->r_conv_id);
  764.     tpconv->r_valid = TRUE;
  765.  
  766.     APPCDBG(printf("appcconnect done\n"));
  767.  
  768.     return tpconv;
  769. }
  770.  
  771. /*
  772.  * read from a conversation setup by appcconnect() or appclisten().  
  773.  * will read at most <maxlen> bytes into the buffer at <data>.
  774.  */
  775. int appcread(tpconvid_t tpconv, void *data, int maxlen) {
  776.     BOOL dealloc;
  777.     int read;
  778.  
  779.     APPCDBG(printf("appcread(tpconv, data, %i) starting\n", maxlen));
  780.  
  781.     if (!tpconv->r_valid) return -1;
  782.     read = do_receive_and_wait(tpconv->r_tp_id, tpconv->r_conv_id, data, maxlen,
  783.         &dealloc);
  784.     if (dealloc) tpconv->r_valid = FALSE;
  785.  
  786.     APPCDBG(printf("appcread done, returning %i, dealloc = %i, tpconv->r_valid = %i\n", read, dealloc, tpconv->r_valid));
  787.  
  788.     return read;
  789. }
  790.  
  791. /*
  792.  * write to a conversation setup by appcconnect() or appclisten().
  793.  * will write at most <maxlen> bytes into the buffer at <data>
  794.  */
  795. void appcwrite(tpconvid_t tpconv, void *data, int len) {
  796.     APPCDBG(printf("appcwrite(tpconv, data, %i) starting\n", len));
  797.     do_send_data(tpconv->w_tp_id, tpconv->w_conv_id, data, len);
  798.     APPCDBG(printf("appcwrite done\n"));
  799. }
  800.  
  801. int appcvalid(tpconvid_t tpconv) {
  802.     int rc;
  803.  
  804.     APPCDBG(printf("appcvalid starting and done, returning %i\n", (tpconv->r_valid && tpconv->w_valid)));
  805.     rc = (tpconv->r_valid && tpconv->w_valid);
  806.     return rc;
  807. }
  808.  
  809. void appcclose(tpconvid_t tpconv) {
  810.     APPCDBG(printf("appcclose(tpconv) starting\n"));
  811.  
  812.     /* deallocate the write channel */
  813.     if (tpconv->w_valid) {
  814.         do_deallocate(tpconv->w_tp_id, tpconv->w_conv_id);
  815.         do_tp_ended(tpconv->w_tp_id);
  816.         tpconv->w_valid = FALSE;
  817.     }
  818.  
  819.     APPCDBG(printf("appcclose done\n"));
  820. }
  821.