/*****************************************************************************
Module :     MSJTimeSrv.c
Notices:     Written 1997 by Jeffrey Richter
Description: Minimal Service Template
*****************************************************************************/
#define STRICT
#define UNICODE
#include <Windows.h>
//////////////////////////////////////////////////////////////////////////////
#define dimof(A)  (sizeof(A) / sizeof(A[0]))
//////////////////////////////////////////////////////////////////////////////
WCHAR g_szAppName[] = L"MSJ Time Service";
//////////////////////////////////////////////////////////////////////////////
HANDLE g_hIOCP = NULL;
// The completion port wakes for 1 of 2 reasons:
enum COMPKEY { 
   CK_SERVICECONTROL,   // A service control code
   CK_PIPE              // A client connects to our pipe
};
//////////////////////////////////////////////////////////////////////////////
void WINAPI TimeServiceHandler(DWORD fdwControl) {
   // The Handler thread is very simple and executes very quickly because
   // it just passes the control code off to the ServiceMain thread.
   PostQueuedCompletionStatus(g_hIOCP, fdwControl, CK_SERVICECONTROL, NULL);
}
//////////////////////////////////////////////////////////////////////////////
#define SERVICE_CONTROL_RUN            0x00000000
DWORD dwSrvCtrlToPend[256] = {   // 255 is max user-defined code
   /* 0: SERVICE_CONTROL_RUN         */ SERVICE_START_PENDING, 
   /* 1: SERVICE_CONTROL_STOP        */ SERVICE_STOP_PENDING,
   /* 2: SERVICE_CONTROL_PAUSE       */ SERVICE_PAUSE_PENDING,
   /* 3: SERVICE_CONTROL_CONTINUE    */ SERVICE_CONTINUE_PENDING,
   /* 4: SERVICE_CONTROL_INTERROGATE */ 0, 
   /* 5: SERVICE_CONTROL_SHUTDOWN    */ SERVICE_STOP_PENDING,
   /* 6 - 255: User-defined codes    */ 0
};
DWORD dwSrvPendToState[] = { 
   /* 0: Undefined                */ 0,
   /* 1: SERVICE_STOPPED          */ 0,
   /* 2: SERVICE_START_PENDING    */ SERVICE_RUNNING,
   /* 3: SERVICE_STOP_PENDING     */ SERVICE_STOPPED, 
   /* 4: SERVICE_RUNNING          */ 0,
   /* 5: SERVICE_CONTINUE_PENDING */ SERVICE_RUNNING,
   /* 6: SERVICE_PAUSE_PENDING    */ SERVICE_PAUSED,
   /* 7: SERVICE_PAUSED           */ 0
};
//////////////////////////////////////////////////////////////////////////////
void WINAPI TimeServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
   DWORD dwCompKey  = CK_SERVICECONTROL;
   DWORD fdwControl = SERVICE_CONTROL_RUN;
   DWORD dwBytesTransferred;
   SYSTEMTIME st;
   HANDLE hpipe;
   OVERLAPPED o, *po;
   SERVICE_STATUS ss;
   SERVICE_STATUS_HANDLE hSS;   // Create the completion port and save its handle in a global
   // variable so that the Handler function can access it.
   g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, CK_PIPE, 0);   // Give SCM the address of this service's Handler
   // NOTE: hSS does not have to be closed.
   hSS = RegisterServiceCtrlHandler(g_szAppName, TimeServiceHandler);   // Do what the service should do.
   // Initialize the members that never change
   ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 
   ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | 
      SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
   
   do {
      switch (dwCompKey) {
      case CK_SERVICECONTROL:
         // We got a new control code
         ss.dwWin32ExitCode = NO_ERROR; 
         ss.dwServiceSpecificExitCode = 0; 
         ss.dwCheckPoint = 0; 
         ss.dwWaitHint = 0;         if (fdwControl == SERVICE_CONTROL_INTERROGATE) {
            SetServiceStatus(hSS, &ss);
            break;
         }         // Determine which PENDING state to return
         if (dwSrvCtrlToPend[fdwControl] != 0) {
            ss.dwCurrentState = dwSrvCtrlToPend[fdwControl]; 
            ss.dwCheckPoint = 0;
            ss.dwWaitHint = 500;   // half a second
            SetServiceStatus(hSS, &ss);
         }         switch (fdwControl) {
            case SERVICE_CONTROL_RUN:
            case SERVICE_CONTROL_CONTINUE:
               // While running, create a pipe that clients can connect to.
               hpipe = CreateNamedPipe(L"\\\\.\\pipe\\MSJTime", 
                  PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
                  PIPE_TYPE_BYTE, 1, sizeof(st), sizeof(st), 1000, NULL);               // Associate the pipe with the completion port
               CreateIoCompletionPort(hpipe, g_hIOCP, CK_PIPE, 0);                // Pend an asynchronous connect against the pipe
               ZeroMemory(&o, sizeof(o));
               ConnectNamedPipe(hpipe, &o);
               break;            case SERVICE_CONTROL_PAUSE:
            case SERVICE_CONTROL_STOP:
            case SERVICE_CONTROL_SHUTDOWN:
               // When not running, close the pipe so clients can't connect
               CloseHandle(hpipe);
               break;            case 128:   // User-defined control (demonstration purposes)
               MessageBox(NULL, L"Got control code 128", g_szAppName, MB_OK);
               break;
         }         // Determine which complete state to return
         if (dwSrvPendToState[ss.dwCurrentState] != 0) {
            ss.dwCurrentState = dwSrvPendToState[ss.dwCurrentState]; 
            ss.dwCheckPoint = ss.dwWaitHint = 0;
            SetServiceStatus(hSS, &ss);
         }
         break;      case CK_PIPE:
         // We got a client request: Send our current time to the client
         GetSystemTime(&st);
         WriteFile(hpipe, &st, sizeof(st), &dwBytesTransferred, NULL);
         FlushFileBuffers(hpipe);
         DisconnectNamedPipe(hpipe);         // Allow another client to connect 
         ZeroMemory(&o, sizeof(o));
         ConnectNamedPipe(hpipe, &o);
      }      if (ss.dwCurrentState != SERVICE_STOPPED) {
         // Sleep until a control code comes in or a client connects
         GetQueuedCompletionStatus(g_hIOCP, &dwBytesTransferred, 
            &dwCompKey, &po, INFINITE);
         fdwControl = dwBytesTransferred;
      }
   } while (ss.dwCurrentState != SERVICE_STOPPED);   // Cleanup and stop this service
   CloseHandle(g_hIOCP);   
}
//////////////////////////////////////////////////////////////////////////////
void InstallService() {
   TCHAR szModulePathname[_MAX_PATH];
   SC_HANDLE hService;   // Open the SCM on this machine.
   SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);   // Get our full pathname
   GetModuleFileName(NULL, szModulePathname, dimof(szModulePathname));   // Add this service to the SCM's database.
   hService = CreateService(hSCM, g_szAppName, g_szAppName, 0,
      SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, 
      szModulePathname, NULL, NULL, NULL, NULL, NULL);   // Close the service and the SCM
   CloseServiceHandle(hService);
   CloseServiceHandle(hSCM);
}
//////////////////////////////////////////////////////////////////////////////
void RemoveService() {
   // Open the SCM on this machine.
   SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);   // Open this service for DELETE access
   SC_HANDLE hService = OpenService(hSCM, g_szAppName, DELETE);   // Remove this service from the SCM's database.
   DeleteService(hService);   // Close the service and the SCM
   CloseServiceHandle(hService);
   CloseServiceHandle(hSCM);
}
//////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstExePrev, 
   LPSTR pszCmdLine, int nCmdShow) {   int nArgc = __argc;
#ifdef UNICODE
   LPCTSTR *ppArgv = (LPCTSTR*) CommandLineToArgvW(GetCommandLine(), &nArgc);
#else
   LPCTSTR *ppArgv = (LPCTSTR*) __argv;
#endif   BOOL fStartService = (nArgc < 2), fDebug = FALSE;
   int i;   for (i = 1; i < nArgc; i++) {
      if ((ppArgv[i][0] == __TEXT('-')) || (ppArgv[i][0] == __TEXT('/'))) {
         // Command line switch
         if (lstrcmpi(&ppArgv[i][1], __TEXT("install")) == 0) 
            InstallService();         if (lstrcmpi(&ppArgv[i][1], __TEXT("remove"))  == 0)
            RemoveService();         if (lstrcmpi(&ppArgv[i][1], __TEXT("debug"))   == 0)
            fDebug = TRUE;
      }
   }
InstallService();#ifdef UNICODE
   HeapFree(GetProcessHeap(), 0, (PVOID) ppArgv);
#endif   if (fDebug) {
      // Running as EXE not as service, just run the service for debugging
      TimeServiceMain(0, NULL);
   }   if (fStartService) {
      SERVICE_TABLE_ENTRY ServiceTable[] = {
         { g_szAppName, TimeServiceMain },
         { NULL,        NULL }   // End of list
      };
      StartServiceCtrlDispatcher(ServiceTable);
   }   return(0);
}
//////////////////////////////// End Of File /////////////////////////////////