home *** CD-ROM | disk | FTP | other *** search
/ PC Format (South-Africa) 2001 June / PCFJune.iso / mweb / MWEB Utils / ws295sdk.exe / Ws2sdkzp.exe / SAMPLES / MCHAT / MCHAT.C next >
Encoding:
C/C++ Source or Header  |  1997-06-06  |  49.4 KB  |  1,247 lines

  1. /******************************************************************************\ 
  2. *       This is a part of the Microsoft Source Code Samples.  
  3. *       Copyright (C) 1993-1996 Microsoft Corporation. 
  4. *       All rights reserved.  
  5. *       This source code is only intended as a supplement to  
  6. *       Microsoft Development Tools and/or WinHelp documentation. 
  7. *       See these sources for detailed information regarding the  
  8. *       Microsoft samples programs. 
  9. \******************************************************************************/
  10. /* Include this file first to prevent inclusion of winsock.h by windows.h*/
  11. #include <winsock2.h>   
  12. #include <windows.h>
  13. #include <ws2tcpip.h>   /*tcpip specific options*/
  14. #include <tchar.h>
  15. #include <stdio.h>
  16. #include <string.h>
  17.  
  18.  
  19. /**************************** CONSTANTS ***************************************/
  20.  
  21. /* Size of the buffer to receive messages from peers */
  22. #define BUF_SIZE        2048
  23. /* Width of the mini-console allocated for each multicast address */
  24. #define MINI_COLS       24
  25. /* Number of console rows reserved for header */
  26. #define HEADER_ROWS     2
  27. /* Number of console rows reserved for input */
  28. #define INPUT_ROWS      2
  29. /* Zero coordinates */
  30. const COORD             Zero = {0, 0};
  31.  
  32. #define HELP_MSG    TEXT ("Type a message and press <Enter> to send it on all sessions, CTRL\\{Z | C} exits.")
  33. #define HELP_MSG_LEN (sizeof(HELP_MSG)/sizeof(CHAR)-1)
  34.  
  35. /**************************** STRUCTURES **************************************/
  36.  
  37. /* Context associated with each mutlicast session */
  38. typedef struct _MCAST_CONTEXT MCAST_CONTEXT, *PMCAST_CONTEXT;
  39. struct _MCAST_CONTEXT {
  40.     PMCAST_CONTEXT      next;   /* Next record in the list */
  41.     COORD               coord;  /* Current screen coordinates */
  42.     LPTSTR              str;    /* User address argument string */
  43.     LPTSTR                ifstr;    /* Interface address string (if specified) */
  44.  
  45.     SOCKET              s;      /* Multicast socket */
  46.     struct in_addr      addr;   /* Multicast address to join in */
  47.     SHORT               port;   /* Port on which to join */
  48.     struct in_addr      ifad;   /* Interface to register multicast address */
  49.     
  50.     struct sockaddr_in  from;   /* Peer address */
  51.     DWORD               fromlen;/* Peer address length */
  52.     WSAOVERLAPPED       ovlp;   /* Overlapped (for asyncronous IO) */
  53.     char                buf[BUF_SIZE];/* Buffer to receive peer messages */
  54. } ;
  55.  
  56.  
  57. /****************** INTERNAL FUNCTION PROTOTYPES ******************************/
  58.  
  59. int
  60. CheckWinsockVersion (
  61.     VOID
  62.     );
  63.  
  64. int
  65. ParseAddress (
  66.     LPTSTR          argv,
  67.     PMCAST_CONTEXT  *ctx
  68.     );
  69.  
  70. int
  71. JoinSession (
  72.     PMCAST_CONTEXT  ctx
  73.     );
  74.  
  75. int
  76. PostReceiveRequest (
  77.     PMCAST_CONTEXT  ctx
  78.     );
  79.  
  80. void CALLBACK
  81. RecvCompletion(
  82.     IN DWORD            dwError, 
  83.     IN DWORD            cbTransferred, 
  84.     IN LPWSAOVERLAPPED  lpOverlapped, 
  85.     IN DWORD            dwFlags
  86.     );
  87.  
  88.  
  89. BOOL CALLBACK
  90. CtrlHandler(
  91.     DWORD   dwCtrlType
  92.     );
  93.  
  94. VOID
  95. WriteMiniConsoleA (
  96.     IN OUT COORD    *coord,
  97.     LPSTR           output,
  98.     DWORD           len
  99.     );
  100.  
  101. VOID
  102. PaintScreen (
  103.     VOID
  104.     );
  105.  
  106. DWORD WINAPI
  107. InputThread (
  108.     LPVOID  param
  109.     );
  110.  
  111. /**************************** GLOBALS *****************************************/
  112.  
  113. /* List of multicast session contexts */
  114. PMCAST_CONTEXT                  CtxList;
  115. /* Number of multicast sessions */
  116. int                             NumSessions;
  117. /* Number of pending receive requests (so we can wait till they complete before
  118.  * exiting the thread in which we posted them).*/
  119. LONG                            NumPendingRcvs = 0;
  120. /* Event to be signal when we want to stop and exit */
  121. HANDLE                          StopEvent;
  122. /* Console handles */
  123. HANDLE                          HStdout, HStdin;
  124. /* Handle of the input thread */
  125. HANDLE                          HInputThread;
  126. /* Console geometry */
  127. CONSOLE_SCREEN_BUFFER_INFO      StdScreen, /* Used by this program */
  128.                                 OldScreen; /* Preserved to restore on exit */
  129. /* Options with their initial values */
  130. BOOL                            Loopback = TRUE;
  131. BOOL                            UseWSAJoin = TRUE;
  132. BOOL                            ReuseAddress = TRUE;
  133. int                             McastTTL = 0;
  134.  
  135. /*********************************************************************
  136. * FUNCTION: _tmain                                                   *
  137. *                                                                    *
  138. * PURPOSE:  Program main.  Sets up the console and starts asyncronous* 
  139. *           network packet and user input processing, then waits for *
  140. *           signal to cleanup and exit.                              *
  141. *                                                                    * 
  142. * INPUT:    number of arguments read from command line and array of  *
  143. *           arguments themselves.                                    *
  144. *                                                                    *
  145. * RETURNS:  0 if everything goes OK, error code if something fails.  *
  146. *                                                                    *
  147. *********************************************************************/ 
  148. int _cdecl
  149. _tmain (
  150.     int       argc,
  151.     TCHAR     *argv[]
  152.     ) {
  153.     int     err=0;  /* Error code */
  154.     DWORD   idThread;/* Input thread ID */
  155.     PMCAST_CONTEXT  ctx, temp;    /* Current context */
  156.     int     i;      /* Variable to loop thru command line arguments */
  157.     
  158.     /* First get us true console handles (event if parent process
  159.        has redirected them) */
  160.     HStdout = CreateFile (TEXT ("CONOUT$"),
  161.                             GENERIC_READ | GENERIC_WRITE,
  162.                             FILE_SHARE_READ | FILE_SHARE_WRITE,
  163.                             NULL,
  164.                             OPEN_EXISTING,
  165.                             0, NULL);
  166.     HStdin = CreateFile (TEXT ("CONIN$"),
  167.                             GENERIC_READ | GENERIC_WRITE,
  168.                             FILE_SHARE_READ | FILE_SHARE_WRITE,
  169.                             NULL,
  170.                             OPEN_EXISTING,
  171.                             0, NULL);
  172.  
  173.     if ((HStdout==INVALID_HANDLE_VALUE)
  174.             || (HStdin==INVALID_HANDLE_VALUE)
  175.             || !GetConsoleScreenBufferInfo (HStdout, &OldScreen))
  176.         /* If any of the above fails, we won't be able to operate anyway */
  177.         return -1;
  178.  
  179.     if ((argc<=1) || (_tcscmp(argv[1], TEXT ("-?"))==0)) {
  180.             /* Shortcut to let user see the proper command-line format
  181.                without going thru socket DLL loading and fancy screen 
  182.                setup */
  183.         goto Usage;
  184.     }
  185.  
  186.         /* Don't let user interrupt us while we are setting up */
  187.     SetConsoleCtrlHandler (NULL, TRUE);
  188.         /* Ensure acceptable input and output console mode */
  189.     SetConsoleMode (HStdout, ENABLE_PROCESSED_OUTPUT);
  190.     SetConsoleMode (HStdin, 
  191.         ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT);
  192.  
  193.         /* Verify that we have appropriate winsock funtionality */
  194.     err = CheckWinsockVersion ();
  195.     if (err==0) {
  196.             /* Allocate required resources */
  197.         StopEvent = WSACreateEvent ();
  198.         if (StopEvent!=NULL) {
  199.                 /* Win32 console input (and output) does not provide
  200.                    for asyncronous (overlapped) operations, so we create
  201.                    a thread dedicated to handling user input */
  202.             HInputThread = CreateThread (NULL, 0, 
  203.                                   InputThread,
  204.                                   NULL,
  205.                                   CREATE_SUSPENDED,
  206.                                   &idThread);
  207.             if (HInputThread!=NULL) {
  208.                     /* Process command line arguments */
  209.                 NumSessions = 0;   /* Zero out number of multicast sessions */
  210.                 for (i=1; i<argc; i++) {
  211.                         /* First single out options */
  212.                     if (_tcsicmp (argv[i], TEXT ("-l"))==0) {
  213.                         Loopback = FALSE;
  214.                     }
  215.                     else if (_tcsicmp (argv[i], TEXT ("-r"))==0) {
  216.                         ReuseAddress = FALSE;
  217.                     }
  218.                     else if (_tcsicmp (argv[i], TEXT ("-j"))==0) {
  219.                         UseWSAJoin = FALSE;
  220.                     }
  221.                     else if (_tcsicmp (argv[i], TEXT ("-t"))==0) {
  222.                         if (++i<argc) {
  223.                             LPSTR   endp;   /* End of string pointer */
  224.                             McastTTL = _tcstoul (argv[i], &endp, 10);
  225.                             if ((McastTTL==0) || (*endp!=0)) {
  226.                                 err = ERROR_INVALID_PARAMETER;
  227.                                 _tprintf (TEXT ("Invalid time to live value.\n"));
  228.                                 break;
  229.                             }
  230.                         }
  231.                         else {
  232.                             err = ERROR_INVALID_PARAMETER;
  233.                             _tprintf (TEXT ("Time to live value must be specified.\n"));
  234.                             break;
  235.                         }
  236.                     }
  237.                     else {
  238.                             /* Not an option. must be an address specification */
  239.                         err = ParseAddress (argv[i], &ctx);
  240.                         if (err==0) {
  241.                                 /* Insert new context is the list */
  242.                             ctx->next = CtxList;
  243.                             CtxList = ctx;
  244.                             NumSessions += 1;
  245.                         }
  246.                     }
  247.                 }
  248.  
  249.                 if (err==0) {
  250.                         /* Prepare to reverse the list (to restore
  251.                            command-line set processing order */
  252.                     ctx = CtxList;
  253.                     CtxList = NULL;
  254.                     while (ctx!=NULL) {
  255.                             /* Join multicast session */
  256.                         err = JoinSession (ctx);
  257.                         if (err==0) {
  258.                                 /* Post asynchronous receive request
  259.                                    on the multicast socket */
  260.                             err = PostReceiveRequest (ctx);
  261.                             if (err==0) {
  262.                                     /* Reverse the context list */
  263.                                 temp = ctx;
  264.                                 ctx = ctx->next;
  265.                                 temp->next = CtxList;
  266.                                 CtxList = temp;
  267.                                 continue;
  268.                             }
  269.                             else {
  270.                                 _tprintf (
  271.                                     TEXT ("Could not start receive for %s (err: %d)\n"),
  272.                                     ctx->str, err);
  273.                                 closesocket (ctx->s);
  274.                             }
  275.                         }
  276.                         break;
  277.                     }
  278.                             
  279.  
  280.                     if (err==0) {
  281.                             /* Resume (start) input thread */
  282.                         ResumeThread (HInputThread);
  283.  
  284.                             /* Allow normal interrupt processing */
  285.                         SetConsoleCtrlHandler (NULL, FALSE);
  286.                             /* Intercept interrupts to cleanup properly */
  287.                         SetConsoleCtrlHandler (CtrlHandler, TRUE);
  288.  
  289.                             
  290.                             /* Allow processing of received packets till
  291.                                stop event is signalled or wait error
  292.                                occurs */
  293.                         while (TRUE) {
  294.                                 /* Wait alertably to let completion routines
  295.                                    run */
  296.                             DWORD status = WaitForSingleObjectEx (
  297.                                 StopEvent,
  298.                                 INFINITE,
  299.                                 TRUE);
  300.                             switch (status) {
  301.                             case WAIT_IO_COMPLETION:
  302.                                     /* Continue processing IO completion */
  303.                                 continue;
  304.                             case WAIT_OBJECT_0:
  305.                             default:
  306.                                     /* Break out in case of stop signal or
  307.                                        error */
  308.                                 break;
  309.                             }
  310.                             break;
  311.                         }
  312.                     }
  313.                     else {
  314.                             /* Dispose of all unprocessed sockets contexts
  315.                                (open sockets are processed below in non-
  316.                                error case) */
  317.                         while (ctx!=NULL) {
  318.                             temp = ctx;
  319.                             ctx = ctx->next;
  320.                             free (temp);
  321.                         }
  322.                     }
  323.  
  324.                         /* Close all open sockets to force completion
  325.                            of all outstanding IO requests */
  326.                     ctx = CtxList;
  327.                     while (ctx!=NULL) {
  328.                         closesocket (ctx->s);
  329.                         ctx = ctx->next;
  330.                     }
  331.  
  332.                         /* Keep this thread around until all posted requests
  333.                            complete */
  334.                     while (NumPendingRcvs>0)
  335.                             /* Sleep alertably to let completion routines
  336.                                run */
  337.                         SleepEx (INFINITE, TRUE);
  338.  
  339.                         /* Dispose of all remaining contexts */
  340.                     while (CtxList!=NULL) {
  341.                         ctx = CtxList;
  342.                         CtxList = CtxList->next;
  343.                         free (ctx);
  344.                     }
  345.                 }
  346.                     /* Close input thread handle */
  347.                 CloseHandle (HInputThread);
  348.             }
  349.             else {
  350.                 _tprintf (TEXT ("Could not create input thread.\n"));
  351.                 err = GetLastError ();
  352.             }
  353.                 /* Close stop event */
  354.             WSACloseEvent (StopEvent);
  355.         }
  356.         else {
  357.             _tprintf (TEXT ("Could not create stop event.\n"));
  358.             err = GetLastError ();
  359.         }
  360.             /* Cleanup winsock DLL */
  361.         WSACleanup ();
  362.     }
  363.  
  364.         /* Restore screen buffer to original size */
  365.     SetConsoleScreenBufferSize (HStdout, OldScreen.dwSize);
  366.     
  367.         /* Close input handles */
  368.     CloseHandle (HStdout);
  369.     if (HStdin != INVALID_HANDLE_VALUE)
  370.         CloseHandle (HStdin);
  371.  
  372.  
  373.         /* Print usage message if we suspect that user passed in
  374.            invalid parameter */
  375.     if (err==ERROR_INVALID_PARAMETER) {
  376. Usage:
  377.         _tprintf (
  378.                 TEXT("Multi-Chat - join in and chat on multiple multicast addresses.\n")
  379.                 TEXT("Usage:\n")
  380.                 TEXT("   %s [options] mc_addr[:port][,if_addr] [...]\n")
  381.                 TEXT("Where:\n")
  382.                 TEXT("   mc_addr - multicast address to join,\n")
  383.                 TEXT("   port    - port on which to join,\n")
  384.                 TEXT("   if_addr - local interface to join on,\n")
  385.                 TEXT("   ...     - specify as many addresses as can feet on your screen.\n")
  386.                 TEXT("Options:\n")
  387.                 TEXT("   -?      - print this message (must be first, rest is ignored),\n")
  388.                 TEXT("   -l      - don't loopback my own messages,\n")
  389.                 TEXT("   -r      - dissallow reuse of allocated addresses (ports),\n")
  390.                 TEXT("   -j      - use IP_ADD_MEMBERSHIP to join instead of WSAJoinLeaf,\n")
  391.                 TEXT("   -t ttl  - override defult time to live value with ttl.\n\n")
  392.                 TEXT("Example:\n")
  393.                 TEXT("   %s 224.0.0.39 224.0.0.40:1001 224.0.0.41:1002,157.55.88.254\n"),
  394.                 argv[0],
  395.                 argv[0]);
  396.     }
  397.  
  398.     return err;
  399. }
  400.  
  401.  
  402.  
  403. /*********************************************************************
  404. * FUNCTION: CheckWinsockVersion                                      *
  405. *                                                                    *
  406. * PURPOSE:  Verifies that proper version of WinSock DLL is installed *
  407. *           on the system.                                           *
  408. *                                                                    * 
  409. * INPUT:    None                                                     *
  410. *                                                                    *
  411. * RETURNS:  0 if WinSock DLL is appropriate for our purposes,        *
  412. *           WSAVERNOTSUPPORTED or other system error code if WinSock *
  413. *               DLL cannot support required functions or cannot be   *
  414. *               loaded.                                              *
  415. *                                                                    *
  416. *********************************************************************/ 
  417. int
  418. CheckWinsockVersion (
  419.     VOID
  420.     ) {
  421.     WORD wVersionRequested;
  422.     WSADATA wsaData;
  423.     int err;
  424.     
  425.         /* Asynchronous IO and multicast semantics we use supported
  426.            starting only with WinSock 2.0 */
  427.     wVersionRequested = MAKEWORD (2, 0);
  428.  
  429.     err = WSAStartup (wVersionRequested, &wsaData);
  430.     if (err==0) {
  431.  
  432.         /* Confirm that the WinSock DLL supports 2.0.
  433.            Note that if the DLL supports versions greater
  434.            than 2.0 in addition to 2.0, it will still return
  435.            2.0 in wVersion since that is the version we
  436.            requested. */
  437.  
  438.         if ((LOBYTE (wsaData.wVersion)==2)
  439.                 && (HIBYTE (wsaData.wVersion)==0)) {
  440.             /* The WinSock DLL is acceptable. Proceed. */
  441.             return 0;
  442.         }
  443.  
  444.         WSACleanup( );
  445.         err = WSAVERNOTSUPPORTED;
  446.     }
  447.  
  448.         /* Tell the user that we couldn't find a usable
  449.            WinSock DLL.*/
  450.     _tprintf (TEXT ("WinSock DLL does not support requested API version.\n"));
  451.  
  452.  
  453.     return err;
  454. }
  455.  
  456.  
  457.  
  458. /*********************************************************************
  459. * FUNCTION: ParseAddress                                             *
  460. *                                                                    *
  461. * PURPOSE:  Parses multicast and interface addresses in the string   *
  462. *           and allocates and initializes context block for multicast*
  463. *           session.                                                 *
  464. *                                                                    * 
  465. * INPUT:    Address string and vairable to place allocated context.  *
  466. *                                                                    *
  467. * RETURNS:  0 if addresses were successfully processed and context   *
  468. *               allocated and returned,                              *
  469. *           ERROR_INVALID_PARAMETER or WSAEINVAL if specified        *
  470. *               address could not be parsed,                         *
  471. *           ERROR_OUT_OF_MEMORY if there is not enough resources to  *
  472. *               allocate context for multicast session.              *
  473. *                                                                    *
  474. *********************************************************************/ 
  475. int
  476. ParseAddress (
  477.     LPTSTR          argv,
  478.     PMCAST_CONTEXT  *ctx
  479.     ) {
  480.     struct sockaddr_in  addr;   /* Address structure returned by WinSock DLL */
  481.     int                 len;    /* Address length returned by the WinSock DLL */
  482.     LPTSTR              pszAddr;/* Pointer to the address to parse */    
  483.     int                 err = 0;/* Error code */
  484.  
  485.     *ctx = (PMCAST_CONTEXT)malloc (sizeof (MCAST_CONTEXT));
  486.     if (*ctx==NULL) {
  487.         _tprintf (TEXT ("Not enough memory to allocate session context.\n"));
  488.         return ERROR_NOT_ENOUGH_MEMORY;
  489.     }
  490.  
  491.     (*ctx)->str = argv;
  492.         
  493.         /* Separate out multicast address and port */
  494.     pszAddr = _tcstok (argv, TEXT(","));
  495.  
  496.         /* Call WinSock2 DLL to parse the address */
  497.     len = sizeof(addr);
  498.     err = WSAStringToAddress (pszAddr,
  499.                         AF_INET,
  500.                         NULL,
  501.                         (LPSOCKADDR)&addr,
  502.                         &len);
  503.     if (err==0) {
  504.         (*ctx)->addr = addr.sin_addr;
  505.             /* Get the interface address if it is provided*/
  506.         pszAddr = _tcstok (NULL, TEXT(","));
  507.         if (pszAddr!=NULL) {
  508.             /* Save the pointer to interface address text*/
  509.             (*ctx)->ifstr = pszAddr;
  510.             len = sizeof(addr);
  511.             err = WSAStringToAddress (pszAddr,
  512.                                 AF_INET,
  513.                                 NULL,
  514.                                 (LPSOCKADDR)&addr,
  515.                                 &len);
  516.             if (err==0)
  517.                 (*ctx)->ifad = addr.sin_addr;
  518.             else {
  519.                 err = WSAGetLastError ();
  520.                 _tprintf (TEXT ("Could not parse interface address: %s"), pszAddr);
  521.             }
  522.         }
  523.         else {
  524.                 /* Use default address if it was not specified */
  525.             (*ctx)->ifad.S_un.S_addr = INADDR_ANY;
  526.             (*ctx)->ifstr = pszAddr;
  527.         }
  528.     }
  529.     else {
  530.         err = WSAGetLastError ();
  531.         _tprintf (TEXT ("Could not parse multicast address: %s\n"), pszAddr);
  532.     }
  533.  
  534.     return err;
  535. }
  536.  
  537. /*********************************************************************
  538. * FUNCTION: JoinSession                                              *
  539. *                                                                    *
  540. * PURPOSE:  Joins in multicast session.                              *
  541. *                                                                    * 
  542. * INPUT:    Multicast session context.                               *
  543. *                                                                    *
  544. * RETURNS:  0 if operation succeds,                                  *
  545. *           WinSock error code if it fails.                          *
  546. *                                                                    *
  547. *********************************************************************/ 
  548. int
  549. JoinSession (
  550.     PMCAST_CONTEXT  ctx
  551.     ) {
  552.     int                 err = 0;/* Error code */
  553.     struct sockaddr_in  addr;   /* Address structure for WinSock DLL calls */
  554.     int                 len;    /* Address length for WinSock DLL calls */
  555.  
  556.         /* Allocate UDP socket */
  557.     if (UseWSAJoin)
  558.         ctx->s = WSASocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP,
  559.                                 NULL,   /* Protocol Info */
  560.                                 0,      /* Group */
  561.                                 WSA_FLAG_OVERLAPPED
  562.                                     | WSA_FLAG_MULTIPOINT_C_LEAF
  563.                                     | WSA_FLAG_MULTIPOINT_D_LEAF
  564.                                 );
  565.     else
  566.         ctx->s = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  567.     if (ctx->s!=INVALID_SOCKET) {
  568.             /* Break out of this pseudo-loop in case something fails*/
  569.         do {
  570.  
  571.             if (ReuseAddress) {
  572.                 err = setsockopt (ctx->s,
  573.                                 SOL_SOCKET,
  574.                                 SO_REUSEADDR,
  575.                                 (char *)&ReuseAddress,
  576.                                 sizeof (ReuseAddress));
  577.                 if (err!=0) {
  578.                     err = WSAGetLastError ();
  579.                     _tprintf (
  580.                         TEXT ("Could not set reuse address option for %s (err: %d).\n"),
  581.                         ctx->str, err);
  582.                     break;
  583.                 }
  584.  
  585.             }
  586.  
  587.                 /* Bind to specified port on the interface of user's choice
  588.                    (can be INADDR_ANY - default interface - if user did not
  589.                    specify any) */
  590.             addr.sin_family = AF_INET;
  591.             addr.sin_port = ctx->port;
  592.             addr.sin_addr = ctx->ifad;
  593.             err = bind (ctx->s,
  594.                         (PSOCKADDR)&addr,
  595.                         sizeof (addr));
  596.             if (err!=0) {
  597.                 err = WSAGetLastError ();
  598.                 _tprintf (
  599.                     TEXT ("Could not bind to address %s (err: %d).\n"),
  600.                     ctx->ifstr, err);
  601.                 break;
  602.             }
  603.                 /* Get back address we actually bound to (if user did
  604.                    not specify the port, we'll learn the number assigned
  605.                    by the system) */
  606.             len = sizeof (addr);
  607.             err = getsockname (ctx->s,
  608.                 (PSOCKADDR)&addr,
  609.                 &len);
  610.             if (err!=0) {
  611.                 err = WSAGetLastError ();
  612.                 _tprintf (
  613.                     TEXT ("Could not recover actual socket address for %s (err: %d).\n"),
  614.                     ctx->str, err);
  615.                 break;
  616.             }
  617.             ctx->port = addr.sin_port;
  618.  
  619.  
  620.             if (UseWSAJoin) {
  621.                 DWORD   cbRet;
  622.                 SOCKET  s;
  623.  
  624.                     /* Use WinSock 2.0 specific API to join multicast session */
  625.                 if (McastTTL!=0) {
  626.                     err = WSAIoctl (ctx->s,                     /* socket */
  627.                                     SIO_MULTICAST_SCOPE,        /* code */
  628.                                     &McastTTL,                  /* input */
  629.                                     sizeof (McastTTL),          /* size */
  630.                                     NULL,                       /* output */
  631.                                     0,                          /* size */
  632.                                     &cbRet,                     /* bytes returned */
  633.                                     NULL,                       /* overlapped */
  634.                                     NULL                        /* completion routine */
  635.                                     );
  636.                     if (err!=0) {
  637.                         err = WSAGetLastError ();
  638.                         _tprintf (
  639.                             TEXT ("Could not set multicast TTL for %s (err: %d).\n"),
  640.                             ctx->str, err);
  641.                         break;
  642.                     }
  643.                 }
  644.  
  645.                     /* Setup address to join to (just the multicast address
  646.                        itself, the rest of the structure is OK as returned
  647.                        from getsockname) */
  648.                 addr.sin_addr = ctx->addr;
  649.                 s = WSAJoinLeaf (ctx->s,
  650.                                 (PSOCKADDR)&addr,
  651.                                 sizeof (addr),
  652.                                 NULL,
  653.                                 NULL,
  654.                                 NULL,
  655.                                 NULL,
  656.                                 JL_BOTH);
  657.                 if (s==INVALID_SOCKET) {
  658.                     err = WSAGetLastError ();
  659.                     _tprintf (
  660.                         TEXT ("Could not join multicast session %s (err: %d).\n"),
  661.                         ctx->str, err);
  662.                     break;
  663.                 }
  664.  
  665.             }
  666.             else {
  667.                 struct ip_mreq  mreq;
  668.                     /* Use socket options to join multicast session */
  669.                 if (McastTTL!=0) {
  670.                     err = setsockopt (ctx->s,
  671.                                     IPPROTO_IP, 
  672.                                     IP_MULTICAST_TTL, 
  673.                                     (char *)&McastTTL,
  674.                                     sizeof (McastTTL));
  675.                     if (err!=0) {
  676.                         err = WSAGetLastError ();
  677.                         _tprintf (
  678.                             TEXT ("Could not set multicast TTL for %s (err: %d).\n"),
  679.                             ctx->str, err);
  680.                         break;
  681.                     }
  682.                 }
  683.  
  684.  
  685.                 mreq.imr_interface = ctx->ifad;
  686.                 err = setsockopt (ctx->s,
  687.                                 IPPROTO_IP, 
  688.                                 IP_MULTICAST_IF, 
  689.                                 (char *)&mreq.imr_interface,
  690.                                 sizeof (mreq.imr_interface));
  691.                 if (err!=0) {
  692.                     err = WSAGetLastError ();
  693.                     _tprintf (
  694.                         TEXT ("Could not set default multicast interface %s (err: %d).\n"),
  695.                         ctx->str, err);
  696.                     break;
  697.                 }
  698.  
  699.                 mreq.imr_multiaddr = ctx->addr;
  700.                 mreq.imr_interface = ctx->ifad;
  701.                 err = setsockopt (ctx->s,
  702.                                 IPPROTO_IP, 
  703.                                 IP_ADD_MEMBERSHIP, 
  704.                                 (char *)&mreq,
  705.                                 sizeof (mreq));
  706.                 if (err!=0) {
  707.                     err = WSAGetLastError ();
  708.                     _tprintf (
  709.                         TEXT ("Could not join multicast session %s (err: %d).\n"),
  710.                         ctx->str, err);
  711.                     break;
  712.                 }
  713.             }
  714.                 /* If we got here, join succeded */
  715.             return 0;
  716.         }
  717.         while (FALSE);
  718.             /* Failed something, close the socket */
  719.         closesocket (ctx->s);
  720.     }
  721.     else {
  722.         err = WSAGetLastError ();
  723.         _tprintf (
  724.             TEXT ("Could not create socket for %s (err: %d)\n"),
  725.             ctx->str, err);
  726.     }
  727.     return err;
  728. }
  729.  
  730.  
  731. /*********************************************************************
  732. * FUNCTION: PostReceiveRequest                                       *
  733. *                                                                    *
  734. * PURPOSE:  Posts asynchronous receive request on the socket.        *
  735. *                                                                    * 
  736. * INPUT:    Multicast session context.                               *
  737. *                                                                    *
  738. * RETURNS:  0 if operation succeds,                                  *
  739. *           WinSock error code if it fails.                          *
  740. *                                                                    *
  741. *********************************************************************/ 
  742. int
  743. PostReceiveRequest (
  744.     PMCAST_CONTEXT  ctx
  745.     ) {
  746.     int     err=0;      /* Error code */
  747.     WSABUF  buf[1];     /* Receive buffer array */
  748.     ULONG   rcvd;       /* Number of bytes recived */
  749.     ULONG   flags=0;    /* Receive flags */
  750.         /* We use completion routing, so event field can
  751.            be utilized for additional parameter */
  752.     ctx->ovlp.hEvent = (HANDLE)ctx;
  753.     ctx->fromlen = sizeof (ctx->from);
  754.     buf[0].buf = &ctx->buf[0];
  755.     buf[0].len = sizeof (ctx->buf);
  756.     err = WSARecvFrom (ctx->s,
  757.                         buf,
  758.                         1,
  759.                         &rcvd,
  760.                         &flags,
  761.                         (PSOCKADDR)&ctx->from,
  762.                         &ctx->fromlen,
  763.                         &ctx->ovlp,
  764.                         RecvCompletion
  765.                         );
  766.     if ((err==0)
  767.             || ((err=WSAGetLastError ())==WSA_IO_PENDING)){
  768.             /* Increment counter of pending receives */
  769.         NumPendingRcvs += 1;
  770.         return 0;
  771.     }
  772.  
  773.     return err;
  774. }
  775.  
  776.  
  777.                             
  778. /*********************************************************************
  779. * FUNCTION: RecvCompletion                                           *
  780. *                                                                    *
  781. * PURPOSE:  Completion routine, called by the WinSock DLL once       *
  782. *           asynchronous receive operation completes.                *
  783. *                                                                    * 
  784. * INPUT:    dwError - operation status (0 - success),                *
  785. *           cbTransferred - number of bytes received),               *
  786. *           lpOverlapped - overlapped structure specifed when receive*
  787. *                          was initiated,                            *
  788. *           dwFlags - operation specific flags.                      *
  789. *                                                                    *
  790. * RETURNS:  nothing.                                                 *
  791. *                                                                    *
  792. *********************************************************************/ 
  793. void CALLBACK
  794. RecvCompletion(
  795.     IN DWORD            dwError, 
  796.     IN DWORD            cbTransferred, 
  797.     IN LPWSAOVERLAPPED  lpOverlapped, 
  798.     IN DWORD            dwFlags
  799.     ) {
  800.                     /* Session context */
  801.     PMCAST_CONTEXT  ctx = (PMCAST_CONTEXT)lpOverlapped->hEvent;
  802.     TCHAR           szAddr[128];    /* Peer address */
  803.     CHAR            buffer[1024];   /* Output buffer */
  804.     DWORD           len;            /* Address string length from WinSock */
  805.     int             err;            /* Error code */
  806.  
  807.         /* Decrement counter of pending receives */
  808.     NumPendingRcvs -= 1;
  809.  
  810.     switch (dwError) {
  811.         /* If we received any data, dump them */
  812.     case 0:
  813.     case WSAEMSGSIZE:
  814.             /* First convert peer address to string */
  815.         len = sizeof (szAddr)/sizeof (szAddr[0]);
  816.         err = WSAAddressToString (
  817.                 (PSOCKADDR)&ctx->from,
  818.                 ctx->fromlen,
  819.                 NULL,
  820.                 szAddr,
  821.                 &len);
  822.         if (err==0) {
  823. #if defined(UNICODE) || defined (_UNICODE)
  824.             len = sprintf (buffer, "From: %ls (%d bytes).",
  825.                                 szAddr, cbTransferred);
  826. #else
  827.             len = sprintf (buffer, "From: %s (%d bytes).",
  828.                                 szAddr, cbTransferred);
  829. #endif
  830.         }
  831.         else
  832.             len = sprintf (buffer, "From: ????(%d bytes).",
  833.                                 cbTransferred);
  834.             /* Write peer address */
  835.         WriteMiniConsoleA (&ctx->coord, buffer, len);
  836.             /* Write peer message */
  837.         WriteMiniConsoleA (&ctx->coord, ctx->buf, cbTransferred);
  838.  
  839.  
  840.     default:
  841.         if (dwError!=0) {
  842.             /* Write completion error message */
  843.             len = sprintf (buffer,
  844.                     "Receive completed with error %d.", dwError);
  845.             WriteMiniConsoleA (&ctx->coord, buffer, len);
  846.         }
  847.         err = PostReceiveRequest (ctx);
  848.         if (err!=0) {
  849.             /* Write post error message */
  850.             len = sprintf (buffer,
  851.                     "Could not repost receive request (err %d).", err);
  852.             WriteMiniConsoleA (&ctx->coord, buffer, len);
  853.         }
  854.         break;
  855.  
  856.         /* The following error codes indicate that we were interrupted
  857.            by the user and should just let it exit out */
  858.     case WSAENOTSOCK:
  859.     case WSAEINTR:
  860.     case WSA_OPERATION_ABORTED:
  861.         break;
  862.     }
  863. }
  864.  
  865. /*********************************************************************
  866. * FUNCTION: CtrlHandler                                              *
  867. *                                                                    *
  868. * PURPOSE:  Console control handler, called when user generates      *
  869. *           interrupt from the keyboard.                             *
  870. *                                                                    * 
  871. * INPUT:    Flags that indicate what kind of user action generated   *
  872. *           the interrupt.                                           *
  873. *                                                                    *
  874. * RETURNS:  TRUE - interrupt is handled by the program,              *
  875. *           FALSE - system should handle it.                         *
  876. *                                                                    *
  877. *********************************************************************/ 
  878. BOOL CALLBACK
  879. CtrlHandler(
  880.     DWORD   dwCtrlType
  881.     ) {
  882.     HANDLE            local;
  883.     INPUT_RECORD    Input;
  884.     DWORD            written;
  885.     if (HStdin!=INVALID_HANDLE_VALUE) {
  886.             /* Invalidate input handle */
  887.         local = HStdin;
  888.         HStdin = INVALID_HANDLE_VALUE;
  889.             /* Remove our control handler from the chain */
  890.         SetConsoleCtrlHandler (CtrlHandler, FALSE);
  891.             /* Disallow further control operations */
  892.         SetConsoleCtrlHandler (NULL, TRUE);
  893.             /* Expedite exiting */
  894.         SetConsoleMode (local, 0);
  895.             /* Write return */
  896.         Input.EventType = KEY_EVENT;
  897.         Input.Event.KeyEvent.bKeyDown = TRUE;
  898.         Input.Event.KeyEvent.wRepeatCount = 10;
  899.         Input.Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
  900.         Input.Event.KeyEvent.wVirtualScanCode = 1;
  901.         Input.Event.KeyEvent.uChar.AsciiChar = '\015';
  902.         Input.Event.KeyEvent.dwControlKeyState = 0;
  903.         WriteConsoleInput (local,
  904.                 &Input,
  905.                 1,
  906.                 &written);
  907.             /*Close the handle */
  908.         CloseHandle (local);
  909.             /* Tell that we are going to handle this ourselves */
  910.         return TRUE;
  911.     }
  912.     else
  913.         return FALSE;
  914. }
  915.  
  916.  
  917.  
  918.  
  919. /*********************************************************************
  920. * FUNCTION: WriteMiniConsoleA                                        *
  921. *                                                                    *
  922. * PURPOSE:  Writes output to mini-console reserved for each multicast*
  923. *           session and scrolls is as appropriate.                   *
  924. *                                                                    * 
  925. * INPUT:    coord - coordinates to start writing at (updated upon    *
  926. *                   completion,                                      *
  927. *           output - output string,                                  *
  928. *           len - length of output string.                           *
  929. *                                                                    *
  930. * RETURNS:  Nothing.                                                 *
  931. *                                                                    *
  932. *********************************************************************/ 
  933. VOID
  934. WriteMiniConsoleA (
  935.     IN OUT COORD    *coord,
  936.     LPSTR           output,
  937.     DWORD           len
  938.     ) {
  939.     DWORD           i;          /* Current position in output buffer */
  940.     DWORD           written;    /* Number of charactes written by
  941.                                    console output function */
  942.  
  943.     for (i=0; i<len; i+=MINI_COLS) {
  944.             /* Write as many as fits on one line of the mini-console */
  945.         WriteConsoleOutputCharacterA (HStdout,
  946.                     &output[i],
  947.                     (i+MINI_COLS<len) ? MINI_COLS : len-i,
  948.                     *coord,
  949.                     &written);
  950.             /* Scroll mini-console if we reached the last line */
  951.         if (coord->Y==StdScreen.dwSize.Y-INPUT_ROWS-1) {
  952.             SMALL_RECT                          scroll;
  953.             SMALL_RECT                          clip;
  954.             COORD                               origin;
  955.             CHAR_INFO                           fill;
  956.  
  957.             origin.X = scroll.Left = clip.Left = coord->X;
  958.             origin.Y = clip.Top = HEADER_ROWS;
  959.             scroll.Top = clip.Top+1;
  960.             scroll.Right = clip.Right = coord->X+MINI_COLS-1;
  961.             scroll.Bottom = clip.Bottom = StdScreen.dwSize.Y-INPUT_ROWS-1;
  962.             fill.Char.AsciiChar = ' ';
  963.             fill.Attributes = StdScreen.wAttributes;
  964.  
  965.             ScrollConsoleScreenBuffer (HStdout,
  966.                             &scroll,
  967.                             &clip,
  968.                             origin,
  969.                             &fill);
  970.         }
  971.         else /* Otherwise, advance to the next line */
  972.             coord->Y += 1;
  973.     }
  974. }
  975.  
  976. /*********************************************************************
  977. * FUNCTION: PaintScreen                                              *
  978. *                                                                    *
  979. * PURPOSE:  Draws headers of mini-consoles associated with each      * 
  980. *           multicast session.                                       *
  981. *                                                                    * 
  982. * INPUT:   None                                                      *
  983. *                                                                    *
  984. * RETURNS:  Nothing.                                                 *
  985. *                                                                    *
  986. *********************************************************************/ 
  987. VOID
  988. PaintScreen (
  989.     VOID
  990.     ) {
  991.     PMCAST_CONTEXT      ctx;    /* Current session context */
  992.     COORD               sz;     /* Screen size */
  993.     COORD               pos;    /* Current position on the screen */
  994.     struct sockaddr_in  addr;   /* Socket address structure for conversions */
  995.     TCHAR               szAddr[MINI_COLS+1];
  996.                                 /* Converted socket address string */
  997.     TCHAR               buffer[128];
  998.                                 /* Output string buffer */
  999.     DWORD               len;    /* Number of bytes written/converted */
  1000.     int                 err;    /* Error code */
  1001.  
  1002.          /* Setup the screen according to number of
  1003.            multicast sockets we have */
  1004.     sz.X = (MINI_COLS+1)*NumSessions;
  1005.  
  1006.         /* Can not be less than the current window size */
  1007.     if (sz.X<OldScreen.srWindow.Right-OldScreen.srWindow.Left+1)
  1008.         sz.X = OldScreen.srWindow.Right-OldScreen.srWindow.Left+1;
  1009.     sz.Y = OldScreen.srWindow.Bottom-OldScreen.srWindow.Top+1;
  1010.     SetConsoleScreenBufferSize (HStdout, sz);
  1011.     GetConsoleScreenBufferInfo (HStdout, &StdScreen);
  1012.  
  1013.         /* Clear the screen */
  1014.     FillConsoleOutputAttribute (HStdout,
  1015.                   StdScreen.wAttributes,
  1016.                   sz.X*sz.Y,
  1017.                   Zero,
  1018.                   &len);
  1019.     FillConsoleOutputCharacterA (HStdout,
  1020.                   ' ',
  1021.                   sz.X*sz.Y,
  1022.                   Zero,
  1023.                   &len);
  1024.  
  1025.         /* Paint header part of each mini console
  1026.            reserved for each of the addresses in 
  1027.            the list */
  1028.     addr.sin_family = AF_INET;
  1029.  
  1030.     for (ctx=CtxList, pos.X=0; ctx!=NULL; ctx=ctx->next, pos.X+=MINI_COLS+1) {
  1031.             /* Set initial coordinates of the mini-console */
  1032.         ctx->coord.Y = HEADER_ROWS;
  1033.         ctx->coord.X = pos.X;
  1034.             /* If mini-console fits on the screen */
  1035.         if (pos.X+MINI_COLS<StdScreen.dwSize.X) {
  1036.                 /* Convert multicast address to string */
  1037.             addr.sin_port = ctx->port;
  1038.             addr.sin_addr = ctx->addr;
  1039.             len = sizeof(szAddr);
  1040.             err = WSAAddressToString ((PSOCKADDR)&addr,
  1041.                                 sizeof (addr),
  1042.                                 NULL,
  1043.                                 szAddr,
  1044.                                 &len);
  1045.             if (err==0)
  1046.                 len = _stprintf (buffer,
  1047.                             TEXT("A:%*s"),
  1048.                             MINI_COLS-2, szAddr);
  1049.             else
  1050.                 len = _stprintf (buffer,
  1051.                             TEXT("A:??? (error %ld)"),
  1052.                             WSAGetLastError ());
  1053.                 /* Write it out */
  1054.             pos.Y = 0;
  1055.             WriteConsoleOutputCharacter (
  1056.                 HStdout,
  1057.                 buffer,
  1058.                 len,
  1059.                 pos,
  1060.                 &len);
  1061.  
  1062.                 /* Invert header color */
  1063.             FillConsoleOutputAttribute (
  1064.                 HStdout,
  1065.                 (WORD)(StdScreen.wAttributes 
  1066.                     ^ (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
  1067.                         | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)),
  1068.                 MINI_COLS,        
  1069.                 pos,
  1070.                 &len);
  1071.  
  1072.  
  1073.                 /* Convert multicast address to string (if it is not default)*/
  1074.             if (ctx->ifad.S_un.S_addr != INADDR_ANY) {
  1075.                 addr.sin_addr = ctx->ifad;
  1076.                 addr.sin_port = 0;  /* Remove port number from output */
  1077.                 len = sizeof(szAddr);
  1078.                 err = WSAAddressToString ((PSOCKADDR)&addr,
  1079.                                 sizeof (addr),
  1080.                                 NULL,
  1081.                                 szAddr,
  1082.                                 &len);
  1083.                 if (err==0)
  1084.                     len = _stprintf (buffer,
  1085.                                 TEXT("I-face:%*s"),
  1086.                                 MINI_COLS-7, szAddr);
  1087.                 else
  1088.                     len = _stprintf (buffer,
  1089.                                 TEXT("I-face:??? (error %ld)"),
  1090.                                 WSAGetLastError ());
  1091.             }
  1092.             else
  1093.                 len = _stprintf (buffer,
  1094.                                 TEXT("I-face: Default"));
  1095.                 /* Write it out */
  1096.             pos.Y = 1;
  1097.             WriteConsoleOutputCharacter (
  1098.                     HStdout,
  1099.                     buffer,
  1100.                     len,
  1101.                     pos,
  1102.                     &len);
  1103.  
  1104.                 /* Invert header color */
  1105.             FillConsoleOutputAttribute (
  1106.                 HStdout,
  1107.                 (WORD)(StdScreen.wAttributes 
  1108.                     ^ (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
  1109.                         | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)),
  1110.                 MINI_COLS,
  1111.                 pos,
  1112.                 &len);
  1113.  
  1114.         }
  1115.     }
  1116.  
  1117.         /* Clear and invert input row */
  1118.     pos.X = 0;
  1119.     pos.Y = StdScreen.dwSize.Y-INPUT_ROWS;
  1120.     FillConsoleOutputCharacterA (HStdout,
  1121.         ' ',
  1122.         StdScreen.dwSize.X,
  1123.         pos,
  1124.         &len);
  1125.  
  1126.     FillConsoleOutputAttribute (
  1127.             HStdout,
  1128.             (WORD)(StdScreen.wAttributes 
  1129.                 ^ (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
  1130.                     | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)),
  1131.             StdScreen.dwSize.X,
  1132.             pos,
  1133.             &len);
  1134.  
  1135.         /* Write help message in status line */
  1136.     pos.X = 0;
  1137.     pos.Y = StdScreen.dwSize.Y-INPUT_ROWS+1;
  1138.     WriteConsoleOutputCharacter (HStdout,
  1139.         HELP_MSG,
  1140.         (HELP_MSG_LEN<=StdScreen.dwSize.X)
  1141.             ? HELP_MSG_LEN
  1142.             : StdScreen.dwSize.X,
  1143.         pos,
  1144.         &len);
  1145. }
  1146.  
  1147.  
  1148. /*********************************************************************
  1149. * FUNCTION: InputThread                                              *
  1150. *                                                                    *
  1151. * PURPOSE:  Reads user message and sends it on all multicast sessions*
  1152. *                                                                    * 
  1153. * INPUT:   None                                                      *
  1154. *                                                                    *
  1155. * RETURNS:  0.                                                       *
  1156. *                                                                    *
  1157. *********************************************************************/
  1158. DWORD WINAPI
  1159. InputThread (
  1160.     LPVOID  param
  1161.     ) {
  1162.     CHAR            buffer[1600];   /* Input buffer */
  1163.     TCHAR           szStatus[128];  /* Status string */
  1164.     COORD           posInp, posStat;/* Input and status line coordinates */
  1165.     DWORD           written,read;   /* Number of bytes written to the screen */
  1166.     PMCAST_CONTEXT  ctx;            /* Current session context */
  1167.     struct sockaddr_in  addr;       /* Destination address */
  1168.     int             err;            /* Error code */
  1169.  
  1170.     PaintScreen ();
  1171.     addr.sin_family = AF_INET;
  1172.  
  1173.         /* Setup input row positions */
  1174.     posInp.X = 0;
  1175.     posInp.Y = StdScreen.dwSize.Y-INPUT_ROWS;
  1176.     posStat.X = 0;
  1177.     posStat.Y = StdScreen.dwSize.Y-INPUT_ROWS+1;
  1178.     
  1179.         /* Place cursor in the first input row */
  1180.     SetConsoleCursorPosition (HStdout, posInp);
  1181.  
  1182.         /* Read user input */
  1183.     while (ReadFile (HStdin, buffer, sizeof (buffer), &read, NULL)
  1184.             && (read>0)
  1185.             && (HStdin!=INVALID_HANDLE_VALUE)) {
  1186.             /* Cleanup status line */
  1187.         posStat.X = 0;
  1188.         FillConsoleOutputCharacterA (HStdout,
  1189.             ' ',
  1190.             StdScreen.dwSize.X,
  1191.             posStat,
  1192.             &written);
  1193.  
  1194.             /* Send message on all multicast sessions in the list */
  1195.         for (ctx=CtxList; ctx!=NULL; ctx=ctx->next, posStat.X+=MINI_COLS+1) {
  1196.             addr.sin_addr = ctx->addr;
  1197.             addr.sin_port = ctx->port;
  1198.             
  1199.             err = sendto (ctx->s,
  1200.                         buffer,
  1201.                         read,
  1202.                         0,
  1203.                         (PSOCKADDR)&addr,
  1204.                         sizeof (addr));
  1205.             if (err>=0)
  1206.                 written = _stprintf (szStatus,
  1207.                                 TEXT ("%d bytes sent."),
  1208.                                 err);
  1209.             else
  1210.                 written = _stprintf (szStatus,
  1211.                                 TEXT ("Send failed (err %d)."),
  1212.                                 err);
  1213.             WriteConsoleOutputCharacter (HStdout,
  1214.                 szStatus,
  1215.                 written,
  1216.                 posStat,
  1217.                 &written);
  1218.         }
  1219.  
  1220.             /* Cleanup input line again */
  1221.         FillConsoleOutputAttribute (HStdout,
  1222.             (WORD)(StdScreen.wAttributes 
  1223.                 ^ (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
  1224.                     | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)),
  1225.             StdScreen.dwSize.X,
  1226.             posInp,
  1227.             &written);
  1228.  
  1229.         FillConsoleOutputCharacterA (HStdout,
  1230.             ' ',
  1231.             StdScreen.dwSize.X,
  1232.             posInp,
  1233.             &written);
  1234.         SetConsoleCursorPosition (HStdout, posInp);
  1235.     }
  1236.         /* Cleanup status line befor exiting */
  1237.     posStat.X = 0;
  1238.     FillConsoleOutputCharacterA (HStdout,
  1239.         ' ',
  1240.         StdScreen.dwSize.X,
  1241.         posStat,
  1242.         &written);
  1243.     SetConsoleCursorPosition (HStdout, posInp);
  1244.  
  1245.     WSASetEvent (StopEvent);
  1246.     return 0;
  1247. }