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 / aremote.c < prev    next >
Text File  |  1997-04-09  |  20KB  |  605 lines

  1. /*
  2. Copyright (c) 1994, 1993 Microsoft Corporation
  3.  
  4. Module Name:
  5.     Remote.c
  6.  
  7. Abstract:
  8.     This module contains the main() entry point for Remote.
  9.     Calls the Server or the Client depending on the first parameter.
  10.  
  11. Author:
  12.     Rajivendra Nath (rajnath) 2-Jan-1993
  13.  
  14. Environment:
  15.     Console App. User mode.
  16.  
  17. Revision History:
  18.     Alex Wetmore (t-alexwe) 6-Jun-1994
  19.         - converted remote to use APPC with Windows SNA Server instead of named
  20.           pipes
  21. */
  22.  
  23. #include <windows.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <winappc.h>
  27. #include "appclib.h"
  28. #include "aremote.h"
  29.  
  30. TCHAR   HostName[HOSTNAMELEN];
  31. TCHAR   *ChildCmd;
  32. TCHAR   *TPName = TEXT("AREMOTE");
  33. TCHAR   *locTPName = TEXT("AREMOTC");
  34. TCHAR   *LocalLU = TEXT("AREMOTE");
  35. TCHAR   *ServerLU;
  36. TCHAR   *mode_name = TEXT("#INTER");
  37. HANDLE MyOutHandle;
  38. BOOL   IsAdvertise=TRUE;
  39. DWORD  ClientToServerFlag;
  40. BOOL   AutoStarted;
  41.  
  42.  
  43. TCHAR    ChildCmdBuffer[1024];
  44.  
  45. TCHAR* ColorList[]={TEXT("black") ,TEXT("blue") ,TEXT("green") ,TEXT("cyan") ,TEXT("red") ,TEXT("purple") ,TEXT("yellow") ,TEXT("white"),
  46.                    TEXT("lblack"),TEXT("lblue"),TEXT("lgreen"),TEXT("lcyan"),TEXT("lred"),TEXT("lpurple"),TEXT("lyellow"),TEXT("lwhite")};
  47.  
  48. int __cdecl main(void);
  49.  
  50. WORD GetColorNum(TCHAR* color);
  51. VOID SetColor(WORD attr);
  52.  
  53. CONSOLE_SCREEN_BUFFER_INFO csbiOriginal;
  54.  
  55. /*************************************************************/
  56. VOID ErrorExit(TCHAR *str) {
  57.     WRITEF((VBuff,TEXT("Error-%d:%s\n"),GetLastError(),str));
  58.     ExitProcess(1);
  59. }
  60. /*************************************************************/
  61. DWORD ReadFixBytes(HANDLE hRead, TCHAR*  Buffer, DWORD  ToRead,
  62.     DWORD  TimeOut) {   //ignore timeout for timebeing
  63.     DWORD xyzBytesRead=0;
  64.     DWORD xyzBytesToRead=ToRead;
  65.     TCHAR* xyzbuff=Buffer;
  66.  
  67.     while(xyzBytesToRead!=0)
  68.     {
  69.         if (!ReadFile(hRead,xyzbuff,xyzBytesToRead,&xyzBytesRead,NULL))
  70.         {
  71.             return(xyzBytesToRead);
  72.         }
  73.  
  74.         xyzBytesToRead-=xyzBytesRead;
  75.         xyzbuff+=xyzBytesRead;
  76.     }
  77.     return(0);
  78.  
  79. }
  80. /*************************************************************/
  81. VOID DisplayClientHlp() {
  82.     WRITEF((VBuff,TEXT("\n   To Start the CLIENT end of AREMOTE\n")));
  83.     WRITEF((VBuff,TEXT("   ---------------------------------\n")));
  84.     WRITEF((VBuff,TEXT("   Syntax : AREMOTE /C <ServerLU> [/T <TPName>] [/P <TPName>] [/L <LocalLU>]\n")));
  85.     WRITEF((VBuff,TEXT("                                  [/N <# lines>] [/M <Modename>]\n")));
  86.     WRITEF((VBuff,TEXT("                                  [/F <Color>] [/B <Color>]\n")));
  87.     WRITEF((VBuff,TEXT("\n")));
  88.  
  89.     WRITEF((VBuff,TEXT("   <ServerLU>      SNA LU for connecting to Server\n")));
  90.  
  91.     WRITEF((VBuff,TEXT("   [/T <TPName>]   TP name that server is using (default is \"%s\")\n"), TPName));
  92.     WRITEF((VBuff,TEXT("   [/P <TPName>]   TP name that client is using (default is \"%s\")\n"), locTPName));
  93.     WRITEF((VBuff,TEXT("   [/M <Modename>] The mode by which the Local LU and Server LU are partnered\n")));
  94.     WRITEF((VBuff,TEXT("                   (defaults to \"%s\")\n"), mode_name));
  95.     WRITEF((VBuff,TEXT("   [/L <LocalLU>]  LU name for local TP to use (default is \"%s\")\n"), LocalLU));
  96.     WRITEF((VBuff,TEXT("   [/N <# lines>]  Number of Lines to Get.\n")));
  97.     WRITEF((VBuff,TEXT("   [/F <color>]    Foreground color eg blue, red, etc.\n")));
  98.     WRITEF((VBuff,TEXT("   [/B <color>]    Background color eg blue, lwhite,etc.\n")));
  99.     WRITEF((VBuff,TEXT("\n")));
  100.     WRITEF((VBuff,TEXT("   Example: aremote /C serverlu\n")));
  101.     WRITEF((VBuff,TEXT("\n")));
  102.     WRITEF((VBuff,TEXT("   To Exit: %cQ (Leaves the Remote Server Running)\n"), COMMANDCHAR));
  103.     WRITEF((VBuff,TEXT("\n")));
  104. }
  105. /*************************************************************/
  106.  
  107. VOID DisplayServerHlp() {
  108.     WRITEF((VBuff,TEXT("\n   To Start the SERVER end of AREMOTE\n")));
  109.     WRITEF((VBuff,TEXT("   ---------------------------------\n")));
  110.     WRITEF((VBuff,TEXT("   Syntax : AREMOTE /S \"<Cmd>\" [/T <TPName>] [/F <color>] [/B <color>]\n")));
  111.     WRITEF((VBuff,TEXT("\n")));
  112.  
  113.     WRITEF((VBuff,TEXT("   \"<Cmd>\"         A Text-Mode program that you want to control\n")));
  114.     WRITEF((VBuff,TEXT("                   from another computer.\n")));
  115.  
  116.     WRITEF((VBuff,TEXT("   [/T <TPName>]   TP name that server is using (default is \"%s\")\n"), TPName));
  117.     WRITEF((VBuff,TEXT("   [/F <color>]    Foreground color eg blue, red, etc.\n")));
  118.     WRITEF((VBuff,TEXT("   [/B <color>]    Background color eg blue, lwhite, etc.\n")));
  119.  
  120.     WRITEF((VBuff,TEXT("\n")));
  121.     WRITEF((VBuff,TEXT("   Example: aremote /S \"cmd\"\n")));
  122.     WRITEF((VBuff,TEXT("\n")));
  123.     WRITEF((VBuff,TEXT("   To Exit: %cK from Client\n"), COMMANDCHAR));
  124.     WRITEF((VBuff,TEXT("\n")));
  125.  
  126. }
  127.  
  128. WORD GetColorNum(TCHAR *color) {
  129.     int i;
  130.  
  131.     _wcslwr(color);
  132.     for (i=0;i<16;i++)
  133.     {
  134.         if (lstrcmp(ColorList[i],color)==0)
  135.         {
  136.             return(i);
  137.         }
  138.     }
  139.     return 0xffff;
  140. }
  141.  
  142. VOID
  143. SetColor(
  144.     WORD attr
  145.     )
  146. {
  147.     COORD  origin={0,0};
  148.     DWORD  dwrite;
  149.     FillConsoleOutputAttribute
  150.     (
  151.         MyOutHandle,attr,csbiOriginal.dwSize.
  152.         X*csbiOriginal.dwSize.Y,origin,&dwrite
  153.     );
  154.     SetConsoleTextAttribute(MyOutHandle,attr);
  155. }
  156.  
  157. /*************************************************************/
  158. VOID
  159. Errormsg(
  160.     TCHAR* str
  161.     )
  162. {
  163.     WRITEF((VBuff,TEXT("Error (%d) - %s\n"),GetLastError(),str));
  164. }
  165.  
  166. /*************************************************************/
  167.  
  168. /*
  169.  * this main procedure gets called by the appropriate main depending on
  170.  * if AREMOTE is setup as a service or not.
  171.  */
  172. int remote_main(int argc, TCHAR **argv) {
  173.     WORD  RunType;              // Server or Client end of Remote
  174.     DWORD len=HOSTNAMELEN-1;
  175.     int   i;
  176.  
  177.     TCHAR  sTitle[100];          // New Title
  178.     TCHAR  orgTitle[100];        // Old Title
  179.     BOOL  bSetAttrib=FALSE;     // Change Console Attributes
  180.     WORD  wAttrib;              // Console Attributes
  181.     char  abuff[1024];        // temporary ansi buffer
  182.  
  183.     GetComputerName((LPTSTR)HostName,&len);
  184.  
  185.     MyOutHandle=GetStdHandle(STD_OUTPUT_HANDLE);
  186.  
  187.     GetConsoleScreenBufferInfo(MyOutHandle,&csbiOriginal);
  188.  
  189.     // Parameter Processing
  190.     //
  191.     // For Server:
  192.     // Remote /S <Executable> [Optional Params]
  193.     //
  194.     // For AutoStarted TP:
  195.     // Remote /A <Executable> [Optional Params]
  196.     //   - this case is handled in main()
  197.     //
  198.     // For Client:
  199.     // Remote /C <Server LU> [Optional Params]
  200.  
  201.     if ((argc < 2)||((argv[1][0] != TEXT('/'))&&(argv[1][0] != TEXT('-')))) {
  202.         DisplayServerHlp();
  203.         DisplayClientHlp();
  204.         WRITEF((VBuff, TEXT("\nFor server help only: aremote /s")));
  205.         WRITEF((VBuff, TEXT("\nFor client help only: aremote /c")));
  206.         return(1);
  207.     }
  208.  
  209.     switch(tolower(argv[1][1])) {
  210.         case TEXT('c'):
  211.             // Is Client End of Remote
  212.             if ((argc < 3) || ((argv[1][0] != TEXT('/')) && (argv[1][0] != TEXT('-')))) {
  213.                 DisplayClientHlp();
  214.                 return 1;
  215.             }
  216.  
  217.             ServerLU = _wcsupr(argv[2]);
  218.  
  219.             RunType=REMOTE_CLIENT;
  220.             break;
  221.  
  222.         case TEXT('s'):
  223.             // Is Server End of Remote
  224.             if ((argc < 3) || ((argv[1][0] != TEXT('/')) && (argv[1][0] != TEXT('-')))) {
  225.                 DisplayServerHlp();
  226.                 return 1;
  227.             }
  228.  
  229.             ChildCmd = argv[2];
  230.  
  231.             RunType = REMOTE_SERVER;
  232.             break;
  233.  
  234.         default:
  235.             DisplayServerHlp();
  236.             DisplayClientHlp();
  237.             WRITEF((VBuff, TEXT("\nFor server help only: aremote /s")));
  238.             WRITEF((VBuff, TEXT("\nFor client help only: aremote /c")));
  239.              return(1);
  240.     }
  241.  
  242.     //
  243.     // Save Existing Values
  244.     //
  245.  
  246.     //
  247.     //Colors /f   <ForeGround> /b <BackGround>
  248.     //
  249.  
  250.     wAttrib = csbiOriginal.wAttributes;
  251.  
  252.     GetConsoleTitle(orgTitle,sizeof(orgTitle));
  253.  
  254.     if (RunType == REMOTE_SERVER) {
  255.         //
  256.         // Base Name of Executable
  257.         // For setting the title
  258.         //
  259.  
  260.         TCHAR *tcmd=ChildCmd;
  261.  
  262.         while ((*tcmd!=TEXT(' '))    &&(*tcmd!=0))   tcmd++;
  263.         while ((tcmd!=ChildCmd)&&(*tcmd!=TEXT('\\')))tcmd--;
  264.  
  265.         wsprintf(sTitle,TEXT("%8.8s [Remote /C %s %s]"),tcmd,HostName,TPName);
  266.     }
  267.  
  268.     //
  269.     //Process Common (Optional) Parameters
  270.     //
  271.  
  272.     for (i = 3; i < argc; i++) {
  273.  
  274.         if ((argv[i][0] != TEXT('/')) && (argv[i][0] != TEXT('-'))) {
  275.             WRITEF((VBuff,TEXT("Invalid parameter %s:Ignoring\n"),argv[i]));
  276.             continue;
  277.         }
  278.  
  279.         switch(tolower(argv[i][1])) {
  280.             case TEXT('n'):   // Only Valid for client End, max number of lines to get from server
  281.                 i++;
  282.                 if (i >= argc) {
  283.                     WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
  284.                     break;
  285.                 }
  286.                 wcstombs(abuff, argv[i], 1024);
  287.                 LinesToSend=(DWORD)atoi(abuff)+1;
  288.                 break;
  289.  
  290.             case TEXT('l'):    // name of LU for client to use
  291.                 i++;
  292.                 if (i >= argc) {
  293.                     WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
  294.                     break;
  295.                 }
  296.                 LocalLU = _wcsupr(argv[i]);
  297.                 break;
  298.             
  299.             case TEXT('m'):    // name of LU-LU connection mode
  300.                 i++;
  301.                 if (i >= argc) {
  302.                     WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
  303.                     break;
  304.                 }
  305.                 mode_name = _wcsupr(argv[i]);
  306.                 break;
  307.             
  308.             case TEXT('t'):    // name of tp
  309.                 i++;
  310.                 if (i >= argc) {
  311.                     WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
  312.                     break;
  313.                 }
  314.                 TPName = _wcsupr(argv[i]);
  315.                 break;
  316.             
  317.                case TEXT('p'):    // name of tp
  318.                 i++;
  319.                 if (i >= argc) {
  320.                     WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
  321.                     break;
  322.                 }
  323.                 locTPName = _wcsupr(argv[i]);
  324.                 break;
  325.             
  326.             case TEXT('b'):   // Background color
  327.                 i++;
  328.                 if (i >= argc) {
  329.                     WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
  330.                     break;
  331.                 }
  332.                 {
  333.                     WORD col = GetColorNum(argv[i]);
  334.                     if (col != 0xffff) {
  335.                         bSetAttrib = TRUE;
  336.                         wAttrib = col << 4 | (wAttrib & 0x000f);
  337.                     }
  338.                     break;
  339.                 }
  340.  
  341.             case TEXT('f'):   // Foreground color
  342.                 i++;
  343.                 if (i>=argc) {
  344.                     WRITEF((VBuff,TEXT("Incomplete Param %s..Ignoring\n"),argv[i-1]));
  345.                     break;
  346.                 }
  347.                 {
  348.                     WORD col = GetColorNum(argv[i]);
  349.                     if (col != 0xffff) {
  350.                     bSetAttrib = TRUE;
  351.                     wAttrib = col | (wAttrib & 0x00f0);
  352.                 }
  353.                 break;
  354.             }
  355.  
  356.             case TEXT('q'):
  357.                 IsAdvertise = FALSE;
  358.                 ClientToServerFlag |= 0x80000000;
  359.                 break;
  360.  
  361.             default:
  362.                 WRITEF((VBuff,TEXT("Unknown Parameter=%s %s\n"),argv[i-1],argv[i]));
  363.                 break;
  364.  
  365.         }
  366.     }
  367.  
  368.     //
  369.     //Now Set various Parameters
  370.     //
  371.  
  372.     //
  373.     //Colors
  374.     //
  375.  
  376.     SetColor(wAttrib);
  377.  
  378.     if (RunType==REMOTE_CLIENT) {
  379.         // Start Client (Client.C)
  380.         Client(LocalLU, ServerLU, TPName, locTPName, mode_name);
  381.     }
  382.  
  383.     if (RunType==REMOTE_SERVER) {
  384.         // Start Server (Server.C)
  385.         Server(ChildCmd, TPName);
  386.     }
  387.  
  388.     //
  389.     //Reset Colors
  390.     //
  391.     SetColor(csbiOriginal.wAttributes);
  392.  
  393.     ExitProcess(0);
  394.  
  395.     return    0;        // satisfy the ppc compiler; statement never reached
  396. }
  397.  
  398. /*****************************************************************************/
  399. /* The following code makes this TP invokable as an NT service. There are 3  */
  400. /* routines.                                                                 */
  401. /*                                                                           */
  402. /* 1. main. This is the entry point for the process, it sets up a service    */
  403. /*          table entry and then calls StartServiceCtrlDispatcher. This call */
  404. /*          doesn't return, but uses the thread which called it as a         */
  405. /*          control dispatcher for all the services implemented by this      */
  406. /*          process (in this case, just the TP itself).                      */
  407. /*                                                                           */
  408. /* 2. ServiceMain. This is the main entry point for the service itself, the  */
  409. /*          service control dispatcher creates a thread to start at this     */
  410. /*          routine. It must register a service control handler for the      */
  411. /*          service which will be called by the control dispatcher when it   */
  412. /*          has control instructions for the service. It then informs the    */
  413. /*          service control manager that the service is running and finally  */
  414. /*          calls the start of the TP itself. This routine should not return */
  415. /*          until the service is ready to die.                               */
  416. /*                                                                           */
  417. /* 3. ControlHandler. This routine is called by the control dispatcher when  */
  418. /*          it has instructions for the service. We do not respond to any    */
  419. /*          of the instructions as this service should be transitory and not */
  420. /*          actually run for more than a few seconds so we don't need to do  */
  421. /*          anything with the STOP or SHUTDOWN requests.                     */
  422. /*          Note that we MUST call SetServiceStatus, even if the status      */
  423. /*          hasn't changed.                                                  */
  424. /*****************************************************************************/
  425.  
  426. VOID WINAPI ServiceMain(DWORD dwNumServiceArgs, LPTSTR * lpServiceArgs);
  427. VOID WINAPI ControlHandler(DWORD dwControl);
  428. SERVICE_STATUS_HANDLE stat_hand;
  429. SERVICE_STATUS servstat;
  430.  
  431. int __cdecl main(void) {
  432.     SERVICE_TABLE_ENTRY stab[2];
  433.     int     argc, i;
  434.     TCHAR     *argv[50];
  435.     TCHAR    cmdline[1024];
  436.  
  437.     // read the command line and break it out into argc/argv.  We have to do
  438.     // this because the standard command line passed into main isn't in Unicode
  439.     lstrcpy(cmdline, GetCommandLine());
  440.     i = 0; argc = 0;
  441.     argv[argc] = &(cmdline[i]);
  442.     while (cmdline[i] != 0) {
  443.         if (cmdline[i] == TEXT(' ')) {
  444.             cmdline[i++] = 0;
  445.             argv[++argc] = &(cmdline[i]);
  446.         }
  447.         i++;
  448.     }
  449.     argc++;
  450.  
  451.     if ((argc >= 3) &&
  452.         (tolower(argv[1][1]) == TEXT('a')) &&
  453.         ((argv[1][0] == TEXT('/')) || (argv[1][0] == TEXT('-')))) {
  454.  
  455.         AutoStarted = TRUE;
  456.  
  457.         //
  458.         // hack to fix NET START AREMOTE bug. Since we're not autostarted
  459.         // by an SNA Server component we have no control over the service
  460.         // cmd line.  Therefore we copy out the 3rd parameter passed to the
  461.         // service process and use it if and only there are less than 3 params
  462.         // passed to StartService
  463.         //
  464.         lstrcpy( ChildCmdBuffer, argv[2] );
  465.  
  466.         //
  467.         //
  468.         // This is being run as a service
  469.         //
  470.         //
  471.         // Start the control dispatcher. This call gives the SCManager this
  472.         // thread for the entire period that this service is running, so that
  473.         // it can call us back with service controls. It will spawn a new
  474.         // thread to run the service itself, starting at entrypoint ServiceMain
  475.         //
  476.         stab[0].lpServiceName = TEXT("AREMOTE\0");
  477.         stab[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION) ServiceMain;
  478.  
  479.         stab[1].lpServiceName = NULL;
  480.         stab[1].lpServiceProc = NULL;
  481.  
  482.         if (!StartServiceCtrlDispatcher(stab)) {
  483.             WRITEF((VBuff, TEXT("Remote: This was run as an service\n\n")));
  484.             WRITEF((VBuff, TEXT("For command line usage type \"Remote\"\n")));
  485.         }
  486.     } else {
  487.         //
  488.         // This is being run as a user-invoked program
  489.         //
  490.         AutoStarted = FALSE;
  491.         remote_main(argc, argv);
  492.     }
  493.  
  494.     return 0;
  495. }
  496.  
  497.  
  498. /*****************************************************************************/
  499. /* This routine is the entry-point for the service itself the service        */
  500. /* control dispatcher creates a thread to start here when we issue           */
  501. /* StartServiceControlDispatcher.                                            */
  502. /*                                                                           */
  503. /* Inputs:  number of arguments to services, array of strings.               */
  504. /*                                                                           */
  505. /* Outputs: none                                                             */
  506. /*                                                                           */
  507. /*****************************************************************************/
  508. VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) {
  509.     DWORD rc;
  510.  
  511.     stat_hand = RegisterServiceCtrlHandler(TEXT("AREMOTE\0"),
  512.         (LPHANDLER_FUNCTION) ControlHandler);
  513.  
  514.     if (stat_hand == (SERVICE_STATUS_HANDLE)NULL) {
  515.         rc = GetLastError();
  516.         DebugBreak();
  517.     }
  518.  
  519.     /*************************************************************************/
  520.     /* Let the SCManager know that we are running.                           */
  521.     /*************************************************************************/
  522.     servstat.dwServiceType              = SERVICE_WIN32;
  523.     servstat.dwCurrentState             = SERVICE_RUNNING;
  524.     servstat.dwControlsAccepted            = SERVICE_ACCEPT_STOP |
  525.                                             SERVICE_ACCEPT_SHUTDOWN;
  526.     servstat.dwWin32ExitCode            = NO_ERROR;
  527.     servstat.dwServiceSpecificExitCode  = NO_ERROR;
  528.     servstat.dwCheckPoint               = 0;
  529.     servstat.dwWaitHint                 = 0;
  530.  
  531.     rc = SetServiceStatus(stat_hand, &servstat);
  532.  
  533.     if (!rc) {
  534.         rc = GetLastError();
  535.         DebugBreak();
  536.     }
  537.  
  538.     {
  539.         int new_argc = 3;
  540.         TCHAR *new_argv[3];
  541.  
  542.         new_argv[0] = argv[0];
  543.         new_argv[1] = (TCHAR *) LocalAlloc(0, 16);
  544.         lstrcpy(new_argv[1], TEXT("/s"));
  545.  
  546.         if ( argc < 3 )
  547.         {
  548.             //
  549.             // hack to fix NET START AREMOTE bug. Since we're not autostarted
  550.             // by an SNA Server component we have no control over the service
  551.             // cmd line.  Therefore we copy out the 3rd parameter passed to the
  552.             // service process and use it if and only there are less than 3 params
  553.             // passed to StartService
  554.             //
  555.             new_argv[2] = ChildCmdBuffer;
  556.         }
  557.         else
  558.         {
  559.             new_argv[2] = argv[2];
  560.         }
  561.  
  562.  
  563.         remote_main(new_argc, (TCHAR **) new_argv);
  564.  
  565.         LocalFree(new_argv[1]);
  566.     }
  567. }
  568.  
  569. /***************************************************************************/
  570. /* This routine is the callback from the SCManager to handle specific      */
  571. /* service control requests. It MUST call SetServiceStatus before it       */
  572. /* returns, regardless of whether the status has changed.                  */
  573. /*                                                                         */
  574. /* Inputs: service control requested                                       */
  575. /*                                                                         */
  576. /* Outputs: none                                                           */
  577. /*                                                                         */
  578. /***************************************************************************/
  579. VOID WINAPI ControlHandler(DWORD dwControl) {
  580.     DWORD rc;
  581.  
  582.     switch (dwControl) {
  583.         case SERVICE_CONTROL_STOP :
  584.             servstat.dwCurrentState = SERVICE_STOP_PENDING;
  585.             servstat.dwWaitHint     = 24000;
  586.             break;
  587.  
  588.         case SERVICE_CONTROL_PAUSE :
  589.         case SERVICE_CONTROL_CONTINUE :
  590.         case SERVICE_CONTROL_INTERROGATE :
  591.             servstat.dwWaitHint     = 0;
  592.             break;
  593.  
  594.         case SERVICE_CONTROL_SHUTDOWN:
  595.             servstat.dwCurrentState = SERVICE_STOP_PENDING;
  596.             servstat.dwWaitHint     = 10000;
  597.             break;
  598.     }
  599.  
  600.     rc=SetServiceStatus(stat_hand, &servstat);
  601.     if (!rc) {
  602.         rc=GetLastError();
  603.     }
  604. }
  605.