svcproc.cxx
上传用户:hzhsqp
上传日期:2007-01-06
资源大小:1600k
文件大小:48k
源码类别:

IP电话/视频会议

开发平台:

Visual C++

  1. /*
  2.  * svcproc.cxx
  3.  *
  4.  * Service process implementation for Win95 and WinNT
  5.  *
  6.  * Portable Windows Library
  7.  *
  8.  * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
  9.  *
  10.  * The contents of this file are subject to the Mozilla Public License
  11.  * Version 1.0 (the "License"); you may not use this file except in
  12.  * compliance with the License. You may obtain a copy of the License at
  13.  * http://www.mozilla.org/MPL/
  14.  *
  15.  * Software distributed under the License is distributed on an "AS IS"
  16.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  17.  * the License for the specific language governing rights and limitations
  18.  * under the License.
  19.  *
  20.  * The Original Code is Portable Windows Library.
  21.  *
  22.  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
  23.  *
  24.  * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
  25.  * All Rights Reserved.
  26.  *
  27.  * Contributor(s): ______________________________________.
  28.  *
  29.  * $Log: svcproc.cxx,v $
  30.  * Revision 1.57  2000/05/02 03:16:46  robertj
  31.  * Added display of thread name in SystemLog, thanks Ashley Unitt.
  32.  *
  33.  * Revision 1.56  1999/09/13 13:15:08  robertj
  34.  * Changed PTRACE so will output to system log in PServiceProcess applications.
  35.  *
  36.  * Revision 1.55  1999/08/07 01:43:41  robertj
  37.  * Added "NoWin" option to prevent display of window in command line commands.
  38.  *
  39.  * Revision 1.54  1999/07/16 03:22:16  robertj
  40.  * Fixed tray icon version command so does not ask question.
  41.  *
  42.  * Revision 1.53  1999/06/14 07:59:39  robertj
  43.  * Enhanced tracing again to add options to trace output (timestamps etc).
  44.  *
  45.  * Revision 1.52  1999/04/21 01:57:09  robertj
  46.  * Added confirmation dialog to menu commands.
  47.  *
  48.  * Revision 1.51  1999/03/09 10:30:19  robertj
  49.  * Fixed ability to have PMEMORY_CHECK on/off on both debug/release versions.
  50.  *
  51.  * Revision 1.50  1999/02/16 08:08:07  robertj
  52.  * MSVC 6.0 compatibility changes.
  53.  *
  54.  * Revision 1.49  1999/01/29 12:20:19  robertj
  55.  * Changed service process to output trace info to the Win32 debug output.
  56.  *
  57.  * Revision 1.48  1998/12/04 10:10:45  robertj
  58.  * Added virtual for determining if process is a service. Fixes linkage problem.
  59.  *
  60.  * Revision 1.47  1998/11/30 04:50:17  robertj
  61.  * New directory structure
  62.  *
  63.  * Revision 1.46  1998/10/18 14:28:34  robertj
  64.  * Renamed argv/argc to eliminate accidental usage.
  65.  * Fixed strange problem withs etting debug window tabstops in optimised version.
  66.  *
  67.  * Revision 1.45  1998/10/13 14:14:09  robertj
  68.  * Added thread ID to log.
  69.  * Added heap debug display to service menus.
  70.  *
  71.  * Revision 1.44  1998/09/24 03:30:57  robertj
  72.  * Added open software license.
  73.  *
  74.  * Revision 1.43  1998/08/20 06:06:03  robertj
  75.  * Fixed bug where web page can be asked for when service not running.
  76.  *
  77.  * Revision 1.42  1998/05/21 04:29:44  robertj
  78.  * Fixed "Proxies stopped" dialog appearing when shutting down windows.
  79.  *
  80.  * Revision 1.41  1998/05/07 05:21:38  robertj
  81.  * Improved formatting of debug window, adding tabs and tab stops.
  82.  *
  83.  * Revision 1.40  1998/04/07 13:32:14  robertj
  84.  * Changed startup code to support PApplication class.
  85.  *
  86.  * Revision 1.39  1998/04/01 01:52:53  robertj
  87.  * Fixed problem with NoAutoDelete threads.
  88.  *
  89.  * Revision 1.38  1998/03/29 06:16:53  robertj
  90.  * Rearranged initialisation sequence so PProcess descendent constructors can do "things".
  91.  *
  92.  * Revision 1.37  1998/03/20 03:20:45  robertj
  93.  * Lined up debug output.
  94.  *
  95.  * Revision 1.36  1998/03/05 12:49:55  robertj
  96.  * MemCheck fixes.
  97.  *
  98.  * Revision 1.35  1998/02/20 23:01:10  robertj
  99.  * Fixed bug where application exits on log out in win95.
  100.  *
  101.  * Revision 1.34  1998/02/16 01:43:57  robertj
  102.  * Really fixed spurious error display on install/start/stop etc
  103.  *
  104.  * Revision 1.33  1998/02/16 00:12:22  robertj
  105.  * Added tray icon support.
  106.  * Fixed problem with services and directory paths with spaces in them.
  107.  *
  108.  * Revision 1.32  1998/02/03 06:16:31  robertj
  109.  * Added extra log levels.
  110.  * Fixed bug where window disappears after debug service termination.
  111.  *
  112.  * Revision 1.31  1998/01/26 00:56:11  robertj
  113.  * Changed ServiceProcess to exclusively use named event to detect running process.
  114.  *
  115.  * Revision 1.30  1997/12/18 05:05:45  robertj
  116.  * Added Edit menu.
  117.  *
  118.  * Revision 1.29  1997/11/04 06:01:45  robertj
  119.  * Fix of "service hung at startup" message for NT service.
  120.  *
  121.  * Revision 1.28  1997/10/30 10:17:10  robertj
  122.  * Fixed bug in detection of running service.
  123.  *
  124.  * Revision 1.27  1997/10/03 15:14:17  robertj
  125.  * Fixed crash on exit.
  126.  *
  127.  * Revision 1.26  1997/08/28 12:50:32  robertj
  128.  * Fixed race condition in cleaning up threads on application termination.
  129.  *
  130.  * Revision 1.25  1997/07/17 12:43:29  robertj
  131.  * Fixed bug for auto-start of service under '95.
  132.  *
  133.  * Revision 1.24  1997/07/14 11:47:20  robertj
  134.  * Added "const" to numerous variables.
  135.  *
  136.  * Revision 1.23  1997/07/08 13:00:30  robertj
  137.  * DLL support.
  138.  * Fixed '95 support so service runs without logging in.
  139.  *
  140.  * Revision 1.22  1997/04/27 05:50:27  robertj
  141.  * DLL support.
  142.  *
  143.  * Revision 1.21  1997/03/18 21:23:27  robertj
  144.  * Fix service manager falsely accusing app of crashing if OnStart() is slow.
  145.  *
  146.  * Revision 1.20  1997/02/05 11:50:40  robertj
  147.  * Changed current process function to return reference and validate objects descendancy.
  148.  * Changed log file name calculation to occur only once.
  149.  * Added some MSVC memory debugging functions.
  150.  *
  151.  * Revision 1.19  1996/12/05 11:53:49  craigs
  152.  * Fixed failure to output PError to debug window if CRLF pairs used
  153.  *
  154.  * Revision 1.18  1996/11/30 12:07:19  robertj
  155.  * Changed service creation for NT so is auto-start,
  156.  *
  157.  * Revision 1.17  1996/11/18 11:32:04  robertj
  158.  * Fixed bug in doing a "stop" command closing ALL instances of service.
  159.  *
  160.  * Revision 1.16  1996/11/12 10:15:16  robertj
  161.  * Fixed bug in NT 3.51 locking up when needs to output to window.
  162.  *
  163.  * Revision 1.15  1996/11/10 21:04:32  robertj
  164.  * Added category names to event log.
  165.  * Fixed menu enables for debug and command modes.
  166.  *
  167.  * Revision 1.14  1996/11/04 03:39:13  robertj
  168.  * Improved detection of running service so debug mode cannot run.
  169.  *
  170.  * Revision 1.13  1996/10/31 12:54:01  robertj
  171.  * Fixed bug in window not being displayed when command line used.
  172.  *
  173.  * Revision 1.12  1996/10/18 11:22:14  robertj
  174.  * Fixed problems with window not being shown under NT.
  175.  *
  176.  * Revision 1.11  1996/10/14 03:09:58  robertj
  177.  * Fixed major bug in debug outpuit locking up (infinite loop)
  178.  * Changed menus so cannot start service if in debug mode
  179.  *
  180.  * Revision 1.10  1996/10/08 13:04:43  robertj
  181.  * Rewrite to use standard window isntead of console window.
  182.  *
  183.  * Revision 1.9  1996/09/16 12:56:27  robertj
  184.  * DLL support
  185.  *
  186.  * Revision 1.8  1996/09/14 12:34:23  robertj
  187.  * Fixed problem with spontaneous exit from app under Win95.
  188.  *
  189.  * Revision 1.7  1996/08/19 13:36:03  robertj
  190.  * Added "Debug" level to system log.
  191.  *
  192.  * Revision 1.6  1996/07/30 12:23:32  robertj
  193.  * Added better service running test.
  194.  * Changed SIGINTR handler to just set termination event.
  195.  *
  196.  * Revision 1.5  1996/07/27 04:07:57  robertj
  197.  * Changed thread creation to use C library function instead of direct WIN32.
  198.  * Changed SystemLog to be stream based rather than printf based.
  199.  * Fixed Win95 support for service start/stop and prevent multiple starts.
  200.  *
  201.  * Revision 1.4  1996/06/10 09:54:08  robertj
  202.  * Fixed Win95 service install bug (typo!)
  203.  *
  204.  * Revision 1.3  1996/05/30 11:49:10  robertj
  205.  * Fixed crash on exit bug.
  206.  *
  207.  * Revision 1.2  1996/05/23 10:03:21  robertj
  208.  * Windows 95 support.
  209.  *
  210.  * Revision 1.1  1996/05/15 21:11:51  robertj
  211.  * Initial revision
  212.  *
  213.  */
  214. #include <ptlib.h>
  215. #include <winuser.h>
  216. #include <winnls.h>
  217. #include <shellapi.h>
  218. #include <process.h>
  219. #include <fstream.h>
  220. #include <signal.h>
  221. #include <fcntl.h>
  222. #include <io.h>
  223. #include <ptlib/svcproc.h>
  224. #include <ptlib/debstrm.h>
  225. #define UWM_SYSTRAY (WM_USER + 1)
  226. #define ICON_RESID 1
  227. #define SYSTRAY_ICON_ID 1
  228. static HINSTANCE hInstance;
  229. #define DATE_WIDTH    72
  230. #define THREAD_WIDTH  48
  231. #define LEVEL_WIDTH   32
  232. #define PROTO_WIDTH   40
  233. #define ACTION_WIDTH  48
  234. enum {
  235.   SvcCmdTray,
  236.   SvcCmdNoTray,
  237.   SvcCmdVersion,
  238.   SvcCmdInstall,
  239.   SvcCmdRemove,
  240.   SvcCmdStart,
  241.   SvcCmdStop,
  242.   SvcCmdPause,
  243.   SvcCmdResume,
  244.   SvcCmdDeinstall,
  245.   NumSvcCmds
  246. };
  247. static const char * const ServiceCommandNames[NumSvcCmds] = {
  248.   "Tray",
  249.   "NoTray",
  250.   "Version",
  251.   "Install",
  252.   "Remove",
  253.   "Start",
  254.   "Stop",
  255.   "Pause",
  256.   "Resume",
  257.   "Deinstall"
  258. };
  259. class PNotifyIconData : public NOTIFYICONDATA {
  260.   public:
  261.     PNotifyIconData(HWND hWnd, UINT flags, const char * tip = NULL);
  262.     void Add()    { Shell_NotifyIcon(NIM_ADD,    this); }
  263.     void Delete() { Shell_NotifyIcon(NIM_DELETE, this); }
  264.     void Modify() { Shell_NotifyIcon(NIM_MODIFY, this); }
  265. };
  266. PNotifyIconData::PNotifyIconData(HWND window, UINT flags, const char * tip)
  267. {
  268.   cbSize = sizeof(NOTIFYICONDATA);
  269.   hWnd   = window;
  270.   uID    = SYSTRAY_ICON_ID;
  271.   uFlags = flags;
  272.   if (tip != NULL) {
  273.     strncpy(szTip, tip, sizeof(szTip)-1);
  274.     szTip[sizeof(szTip)-1] = '';
  275.     uFlags |= NIF_TIP;
  276.   }
  277. }
  278. enum TrayIconRegistryCommand {
  279.   AddTrayIcon,
  280.   DelTrayIcon,
  281.   CheckTrayIcon
  282. };
  283. static BOOL TrayIconRegistry(PServiceProcess * svc, TrayIconRegistryCommand cmd)
  284. {
  285.   HKEY key;
  286.   if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  287.                    "SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
  288.                    0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS)
  289.     return FALSE;
  290.   DWORD err = 1;
  291.   DWORD type;
  292.   DWORD len;
  293.   PString str;
  294.   switch (cmd) {
  295.     case CheckTrayIcon :
  296.       err = RegQueryValueEx(key, svc->GetName(), 0, &type, NULL, &len);
  297.       break;
  298.     case AddTrayIcon :
  299.       str = """ + svc->GetFile() + "" Tray";
  300.       err = RegSetValueEx(key, svc->GetName(), 0, REG_SZ,
  301.                          (LPBYTE)(const char *)str, str.GetLength() + 1);
  302.       break;
  303.     case DelTrayIcon :
  304.       err = RegDeleteValue(key, (char *)(const char *)svc->GetName());
  305.   }
  306.   RegCloseKey(key);
  307.   return err == ERROR_SUCCESS;
  308. }
  309. ///////////////////////////////////////////////////////////////////////////////
  310. // PSystemLog
  311. static PString CreateLogFileName(const PString & processName)
  312. {
  313.   PString dir;
  314.   GetWindowsDirectory(dir.GetPointer(256), 255);
  315.   return dir + "\" + processName + " Log.TXT";
  316. }
  317. void PSystemLog::Output(Level level, const char * msg)
  318. {
  319.   PServiceProcess & process = PServiceProcess::Current();
  320.   if (level > process.GetLogLevel())
  321.     return;
  322.   DWORD err = ::GetLastError();
  323.   if (process.isWin95 || process.debugWindow != NULL) {
  324.     static HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
  325.     WaitForSingleObject(mutex, INFINITE);
  326.     ostream * out;
  327.     if (process.debugWindow != NULL)
  328.       out = new PStringStream;
  329.     else {
  330.       static PString logFileName = CreateLogFileName(process.GetName());
  331.       out = new ofstream(logFileName, ios::app);
  332.     }
  333.     static const char * const levelName[NumLogLevels+1] = {
  334.       "Message",
  335.       "Fatal error",
  336.       "Error",
  337.       "Warning",
  338.       "Info",
  339.       "Debug1",
  340.       "Debug2",
  341.       "Debug3"
  342.     };
  343.     PTime now;
  344.     *out << now.AsString("yyyy/MM/dd hh:mm:sst");
  345.     PString threadName = PThread::Current()->GetThreadName();
  346.     if (!threadName)
  347.       *out << setw(15) << threadName.Left(15);
  348.     else
  349.       *out << (void *)PThread::Current();
  350.     *out << 't'
  351.          << levelName[level+1] << 't' << msg;
  352.     if (level < Info && err != 0)
  353.       *out << " - error = " << err << endl;
  354.     else if (msg[0] == '' || msg[strlen(msg)-1] != 'n')
  355.       *out << endl;
  356.     process.DebugOutput(*(PStringStream*)out);
  357.     delete out;
  358.     ReleaseMutex(mutex);
  359.     SetLastError(0);
  360.   }
  361.   else {
  362.     // Use event logging to log the error.
  363.     HANDLE hEventSource = RegisterEventSource(NULL, process.GetName());
  364.     if (hEventSource == NULL)
  365.       return;
  366.     char errbuf[25];
  367.     if (level > StdError && level < Info && err != 0)
  368.       ::sprintf(errbuf, "nError code = %d", err);
  369.     else
  370.       errbuf[0] = '';
  371.     LPCTSTR strings[3];
  372.     strings[0] = msg;
  373.     strings[1] = errbuf;
  374.     strings[2] = level != Fatal ? "" : " Program aborted.";
  375.     static const WORD levelType[NumLogLevels+1] = {
  376.       EVENTLOG_INFORMATION_TYPE,
  377.       EVENTLOG_ERROR_TYPE,
  378.       EVENTLOG_ERROR_TYPE,
  379.       EVENTLOG_WARNING_TYPE,
  380.       EVENTLOG_INFORMATION_TYPE,
  381.       EVENTLOG_INFORMATION_TYPE,
  382.       EVENTLOG_INFORMATION_TYPE,
  383.       EVENTLOG_INFORMATION_TYPE
  384.     };
  385.     ReportEvent(hEventSource, // handle of event source
  386.                 levelType[level+1],   // event type
  387.                 (WORD)(level+1),      // event category
  388.                 0x1000,               // event ID
  389.                 NULL,                 // current user's SID
  390.                 PARRAYSIZE(strings),  // number of strings
  391.                 0,                    // no bytes of raw data
  392.                 strings,              // array of error strings
  393.                 NULL);                // no raw data
  394.     DeregisterEventSource(hEventSource);
  395.   }
  396. }
  397. int PSystemLog::Buffer::overflow(int c)
  398. {
  399.   if (pptr() >= epptr()) {
  400.     int ppos = pptr() - pbase();
  401.     char * newptr = string.GetPointer(string.GetSize() + 10);
  402.     setp(newptr, newptr + string.GetSize() - 1);
  403.     pbump(ppos);
  404.   }
  405.   if (c != EOF) {
  406.     *pptr() = (char)c;
  407.     pbump(1);
  408.   }
  409.   return 0;
  410. }
  411. int PSystemLog::Buffer::underflow()
  412. {
  413.   return EOF;
  414. }
  415. int PSystemLog::Buffer::sync()
  416. {
  417.   PSystemLog::Output(log->logLevel, string);
  418.   string.SetSize(10);
  419.   char * base = string.GetPointer();
  420.   *base = '';
  421.   setp(base, base + string.GetSize() - 1);
  422.   return 0;
  423. }
  424. ///////////////////////////////////////////////////////////////////////////////
  425. // PServiceProcess
  426. PServiceProcess::PServiceProcess(const char * manuf, const char * name,
  427.                            WORD major, WORD minor, CodeStatus stat, WORD build)
  428.   : PProcess(manuf, name, major, minor, stat, build)
  429. {
  430.   controlWindow = debugWindow = NULL;
  431.   currentLogLevel = PSystemLog::Warning;
  432. }
  433. PServiceProcess & PServiceProcess::Current()
  434. {
  435.   PServiceProcess & process = (PServiceProcess &)PProcess::Current();
  436.   PAssert(process.IsDescendant(PServiceProcess::Class()), "Not a service!");
  437.   return process;
  438. }
  439. BOOL PServiceProcess::IsServiceProcess() const
  440. {
  441.   return TRUE;
  442. }
  443. static BOOL IsServiceRunning(PServiceProcess * svc)
  444. {
  445.   HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, svc->GetName());
  446.   if (hEvent == NULL)
  447.     return ::GetLastError() == ERROR_ACCESS_DENIED;
  448.   CloseHandle(hEvent);
  449.   return TRUE;
  450. }
  451. int PServiceProcess::_main(void * arg)
  452. {
  453. #if PMEMORY_CHECK
  454.   PMemoryHeap::SetIgnoreAllocations(TRUE);
  455. #endif
  456.   PSetErrorStream(new PSystemLog(PSystemLog::StdError));
  457.   PTrace::SetStream(new PSystemLog(PSystemLog::Debug3));
  458.   PTrace::ClearOptions(PTrace::FileAndLine);
  459.   PTrace::SetOptions(PTrace::SystemLogStream);
  460.   PTrace::SetLevel(4);
  461. #if PMEMORY_CHECK
  462.   PMemoryHeap::SetIgnoreAllocations(FALSE);
  463. #endif
  464.   hInstance = (HINSTANCE)arg;
  465.   isWin95 = GetOSName() == "95";
  466.   debugMode = arguments.GetCount() > 0 && stricmp(arguments[0], "Debug") == 0;
  467.   currentLogLevel = debugMode ? PSystemLog::Info : PSystemLog::Warning;
  468.   if (!debugMode && arguments.GetCount() > 0) {
  469.     if (stricmp(arguments[0], "NoWin") == 0)
  470.       arguments.Shift(1);
  471.     else {
  472.       if (!CreateControlWindow(TRUE))
  473.         return 1;
  474.     }
  475.     for (PINDEX a = 0; a < arguments.GetCount(); a++)
  476.       ProcessCommand(arguments[a]);
  477.     if (controlWindow == NULL)
  478.       return GetTerminationValue();
  479.     if (debugWindow != NULL && debugWindow != (HWND)-1) {
  480.       ::SetLastError(0);
  481.       PError << "Close window or select another command from the Control menu.n" << endl;
  482.     }
  483.     MSG msg;
  484.     while (GetMessage(&msg, NULL, 0, 0) != 0) {
  485.       TranslateMessage(&msg);
  486.       DispatchMessage(&msg);
  487.     }
  488.     return GetTerminationValue();
  489.   }
  490.   if (!debugMode && !isWin95) {
  491.     static SERVICE_TABLE_ENTRY dispatchTable[] = {
  492.       { "", PServiceProcess::StaticMainEntry },
  493.       { NULL, NULL }
  494.     };
  495.     dispatchTable[0].lpServiceName = (char *)(const char *)GetName();
  496.     if (StartServiceCtrlDispatcher(dispatchTable))
  497.       return GetTerminationValue();
  498.     PSystemLog::Output(PSystemLog::Fatal, "StartServiceCtrlDispatcher failed.");
  499.     MessageBox(NULL, "Not run as a service!", GetName(), MB_TASKMODAL);
  500.     return 1;
  501.   }
  502.   if (!CreateControlWindow(debugMode))
  503.     return 1;
  504.   if (IsServiceRunning(this)) {
  505.     MessageBox(NULL, "Service already running", GetName(), MB_TASKMODAL);
  506.     return 3;
  507.   }
  508.   if (debugMode) {
  509.     ::SetLastError(0);
  510.     PError << "Service simulation started for "" << GetName() << "".n"
  511.               "Close window to terminate.n" << endl;
  512.   }
  513.   terminationEvent = CreateEvent(NULL, TRUE, FALSE, GetName());
  514.   PAssertOS(terminationEvent != NULL);
  515.   threadHandle = (HANDLE)_beginthread(StaticThreadEntry, 0, this);
  516.   PAssertOS(threadHandle != (HANDLE)-1);
  517.   SetTerminationValue(0);
  518.   MSG msg;
  519.   msg.message = WM_QUIT+1; //Want somethingthat is not WM_QUIT
  520.   do {
  521.     switch (MsgWaitForMultipleObjects(1, &terminationEvent,
  522.                                       FALSE, INFINITE, QS_ALLINPUT)) {
  523.       case WAIT_OBJECT_0+1 :
  524.         while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  525.           if (msg.message != WM_QUIT) {
  526.             TranslateMessage(&msg);
  527.             DispatchMessage(&msg);
  528.           }
  529.         }
  530.         break;
  531.       default :
  532.         // This is a work around for '95 coming up with an erroneous error
  533.         if (::GetLastError() == ERROR_INVALID_HANDLE &&
  534.                           WaitForSingleObject(terminationEvent, 0) == WAIT_TIMEOUT)
  535.           break;
  536.         // Else fall into next case
  537.       case WAIT_OBJECT_0 :
  538.         if (!debugMode || controlWindow == NULL)
  539.           msg.message = WM_QUIT;
  540.         else {
  541.           PError << "nService simulation stopped for "" << GetName() << "".nn"
  542.                     "Close window to terminate.n" << endl;
  543.           ResetEvent(terminationEvent);
  544.         }
  545.     }
  546.   } while (msg.message != WM_QUIT);
  547.   if (controlWindow != NULL)
  548.     DestroyWindow(controlWindow);
  549.   // Set thread ID for process to this thread
  550.   activeThreadMutex.Wait();
  551.   activeThreads.SetAt(threadId, NULL);
  552.   threadId = GetCurrentThreadId();
  553.   threadHandle = GetCurrentThread();
  554.   activeThreads.SetAt(threadId, this);
  555.   activeThreadMutex.Signal();
  556.   OnStop();
  557.   return GetTerminationValue();
  558. }
  559. enum {
  560.   ExitMenuID = 100,
  561.   HideMenuID,
  562.   ControlMenuID,
  563.   CopyMenuID,
  564.   CutMenuID,
  565.   DeleteMenuID,
  566.   SelectAllMenuID,
  567. #ifdef _DEBUG
  568.   MarkMenuID,
  569.   DumpMenuID,
  570.   StatsMenuID,
  571. #endif
  572.   SvcCmdBaseMenuID = 1000,
  573.   LogLevelBaseMenuID = 2000
  574. };
  575. BOOL PServiceProcess::CreateControlWindow(BOOL createDebugWindow)
  576. {
  577.   if (controlWindow != NULL)
  578.     return TRUE;
  579.   WNDCLASS wclass;
  580.   wclass.style = CS_HREDRAW|CS_VREDRAW;
  581.   wclass.lpfnWndProc = (WNDPROC)StaticWndProc;
  582.   wclass.cbClsExtra = 0;
  583.   wclass.cbWndExtra = 0;
  584.   wclass.hInstance = hInstance;
  585.   wclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ICON_RESID));
  586.   wclass.hCursor = NULL;
  587.   wclass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  588.   wclass.lpszMenuName = NULL;
  589.   wclass.lpszClassName = GetName();
  590.   if (RegisterClass(&wclass) == 0)
  591.     return FALSE;
  592.   HMENU menubar = CreateMenu();
  593.   HMENU menu = CreatePopupMenu();
  594.   AppendMenu(menu, MF_STRING, ControlMenuID, "&Control");
  595.   AppendMenu(menu, MF_STRING, HideMenuID, "&Hide");
  596.   AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdVersion, "&Version");
  597.   AppendMenu(menu, MF_SEPARATOR, 0, NULL);
  598. #ifdef _DEBUG
  599.   AppendMenu(menu, MF_STRING, MarkMenuID, "&Mark Memory");
  600.   AppendMenu(menu, MF_STRING, DumpMenuID, "&Dump Memory");
  601.   AppendMenu(menu, MF_STRING, StatsMenuID, "&Statistics");
  602.   AppendMenu(menu, MF_SEPARATOR, 0, NULL);
  603. #endif
  604.   AppendMenu(menu, MF_STRING, ExitMenuID, "E&xit");
  605.   AppendMenu(menubar, MF_POPUP, (UINT)menu, "&File");
  606.   menu = CreatePopupMenu();
  607.   AppendMenu(menu, MF_STRING, CopyMenuID, "&Copy");
  608.   AppendMenu(menu, MF_STRING, CutMenuID, "C&ut");
  609.   AppendMenu(menu, MF_STRING, DeleteMenuID, "&Delete");
  610.   AppendMenu(menu, MF_SEPARATOR, 0, NULL);
  611.   AppendMenu(menu, MF_STRING, SelectAllMenuID, "&Select All");
  612.   AppendMenu(menubar, MF_POPUP, (UINT)menu, "&Edit");
  613.   menu = CreatePopupMenu();
  614.   AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdInstall, "&Install");
  615.   AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdRemove, "&Remove");
  616.   AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdDeinstall, "&Deinstall");
  617.   AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdStart, "&Start");
  618.   AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdStop, "S&top");
  619.   AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdPause, "&Pause");
  620.   AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdResume, "R&esume");
  621.   AppendMenu(menubar, MF_POPUP, (UINT)menu, "&Control");
  622.   menu = CreatePopupMenu();
  623.   AppendMenu(menu, MF_STRING, LogLevelBaseMenuID+PSystemLog::Fatal,   "&Fatal Error");
  624.   AppendMenu(menu, MF_STRING, LogLevelBaseMenuID+PSystemLog::Error,   "&Error");
  625.   AppendMenu(menu, MF_STRING, LogLevelBaseMenuID+PSystemLog::Warning, "&Warning");
  626.   AppendMenu(menu, MF_STRING, LogLevelBaseMenuID+PSystemLog::Info,    "&Information");
  627.   AppendMenu(menu, MF_STRING, LogLevelBaseMenuID+PSystemLog::Debug,   "&Debug");
  628.   AppendMenu(menu, MF_STRING, LogLevelBaseMenuID+PSystemLog::Debug2,  "Debug &2");
  629.   AppendMenu(menu, MF_STRING, LogLevelBaseMenuID+PSystemLog::Debug3,  "Debug &3");
  630.   AppendMenu(menubar, MF_POPUP, (UINT)menu, "&Log Level");
  631.   if (CreateWindow(GetName(),
  632.                    GetName(),
  633.                    WS_OVERLAPPEDWINDOW,
  634.                    CW_USEDEFAULT, CW_USEDEFAULT,
  635.                    CW_USEDEFAULT, CW_USEDEFAULT, 
  636.                    NULL,
  637.                    menubar,
  638.                    hInstance,
  639.                    NULL) == NULL)
  640.     return FALSE;
  641.   if (createDebugWindow && debugWindow == NULL) {
  642.     debugWindow = CreateWindow("edit",
  643.                                "",
  644.                                WS_CHILD|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE|WS_BORDER|
  645.                                       ES_MULTILINE|ES_READONLY,
  646.                                0, 0, 0, 0,
  647.                                controlWindow,
  648.                                (HMENU)10,
  649.                                hInstance,
  650.                                NULL);
  651.     SendMessage(debugWindow, EM_SETLIMITTEXT, isWin95 ? 32000 : 128000, 0);
  652.     DWORD TabStops[] = {
  653.       DATE_WIDTH,
  654.       DATE_WIDTH+THREAD_WIDTH,
  655.       DATE_WIDTH+THREAD_WIDTH+LEVEL_WIDTH,
  656.       DATE_WIDTH+THREAD_WIDTH+LEVEL_WIDTH+PROTO_WIDTH,
  657.       DATE_WIDTH+THREAD_WIDTH+LEVEL_WIDTH+PROTO_WIDTH+ACTION_WIDTH,
  658.       DATE_WIDTH+THREAD_WIDTH+LEVEL_WIDTH+PROTO_WIDTH+ACTION_WIDTH+32  // Standard tab width
  659.     };
  660.     SendMessage(debugWindow, EM_SETTABSTOPS, PARRAYSIZE(TabStops), (LPARAM)(LPDWORD)TabStops);
  661.   }
  662.   return TRUE;
  663. }
  664. LPARAM WINAPI PServiceProcess::StaticWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  665. {
  666.   return Current().WndProc(hWnd, msg, wParam, lParam);
  667. }
  668. LPARAM PServiceProcess::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  669. {
  670. #ifdef _DEBUG
  671.   static DWORD allocationNumber;
  672. #endif
  673.   switch (msg) {
  674.     case WM_CREATE :
  675.       controlWindow = hWnd;
  676.       break;
  677.     case WM_DESTROY :
  678.       if (debugWindow == (HWND)-1) {
  679.         PNotifyIconData nid(hWnd, NIF_TIP);
  680.         nid.Delete(); // This removes the systray icon
  681.       }
  682.       controlWindow = debugWindow = NULL;
  683.       PostQuitMessage(0);
  684.       break;
  685.     case WM_ENDSESSION :
  686.       if (wParam && (debugMode || lParam != ENDSESSION_LOGOFF) && debugWindow != (HWND)-1)
  687.         OnStop();
  688.       return 0;
  689.     case WM_SIZE :
  690.       if (debugWindow != NULL && debugWindow != (HWND)-1)
  691.         MoveWindow(debugWindow, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
  692.       break;
  693.     case WM_INITMENUPOPUP :
  694.     {
  695.       int enableItems = MF_BYCOMMAND|(debugMode ? MF_ENABLED : MF_GRAYED);
  696.       for (int i = PSystemLog::Fatal; i < PSystemLog::NumLogLevels; i++) {
  697.         CheckMenuItem((HMENU)wParam, LogLevelBaseMenuID+i, MF_BYCOMMAND|MF_UNCHECKED);
  698.         EnableMenuItem((HMENU)wParam, LogLevelBaseMenuID+i, enableItems);
  699.       }
  700.       CheckMenuItem((HMENU)wParam, LogLevelBaseMenuID+GetLogLevel(), MF_BYCOMMAND|MF_CHECKED);
  701.       enableItems = MF_BYCOMMAND|(debugMode ? MF_GRAYED : MF_ENABLED);
  702.       EnableMenuItem((HMENU)wParam, SvcCmdBaseMenuID+SvcCmdStart, enableItems);
  703.       EnableMenuItem((HMENU)wParam, SvcCmdBaseMenuID+SvcCmdStop, enableItems);
  704.       EnableMenuItem((HMENU)wParam, SvcCmdBaseMenuID+SvcCmdPause, enableItems);
  705.       EnableMenuItem((HMENU)wParam, SvcCmdBaseMenuID+SvcCmdResume, enableItems);
  706.       DWORD start, finish;
  707.       if (debugWindow != NULL && debugWindow != (HWND)-1)
  708.         SendMessage(debugWindow, EM_GETSEL, (WPARAM)&start, (LPARAM)&finish);
  709.       else
  710.         start = finish = 0;
  711.       enableItems = MF_BYCOMMAND|(start == finish ? MF_GRAYED : MF_ENABLED);
  712.       EnableMenuItem((HMENU)wParam, CopyMenuID, enableItems);
  713.       EnableMenuItem((HMENU)wParam, CutMenuID, enableItems);
  714.       EnableMenuItem((HMENU)wParam, DeleteMenuID, enableItems);
  715.       enableItems = MF_BYCOMMAND|(IsServiceRunning(this) ? MF_ENABLED : MF_GRAYED);
  716.       EnableMenuItem((HMENU)wParam, ControlMenuID, enableItems);
  717.       break;
  718.     }
  719.     case WM_COMMAND :
  720.       switch (wParam) {
  721.         case ExitMenuID :
  722.           DestroyWindow(hWnd);
  723.           break;
  724.         case ControlMenuID :
  725.           if (IsServiceRunning(this))
  726.             OnControl();
  727.           break;
  728.         case HideMenuID :
  729.           ShowWindow(hWnd, SW_HIDE);
  730.           break;
  731. #if PMEMORY_CHECK
  732.         case MarkMenuID :
  733.           allocationNumber = PMemoryHeap::GetAllocationRequest();
  734.           break;
  735.         case DumpMenuID :
  736.           PMemoryHeap::DumpObjectsSince(allocationNumber);
  737.           break;
  738.         case StatsMenuID :
  739.           PMemoryHeap::DumpStatistics();
  740.           break;
  741. #endif
  742.         case CopyMenuID :
  743.           if (debugWindow != NULL && debugWindow != (HWND)-1)
  744.             SendMessage(debugWindow, WM_COPY, 0, 0);
  745.           break;
  746.         case CutMenuID :
  747.           if (debugWindow != NULL && debugWindow != (HWND)-1)
  748.             SendMessage(debugWindow, WM_CUT, 0, 0);
  749.           break;
  750.         case DeleteMenuID :
  751.           if (debugWindow != NULL && debugWindow != (HWND)-1)
  752.             SendMessage(debugWindow, WM_CLEAR, 0, 0);
  753.           break;
  754.         case SelectAllMenuID :
  755.           if (debugWindow != NULL && debugWindow != (HWND)-1)
  756.             SendMessage(debugWindow, EM_SETSEL, 0, -1);
  757.           break;
  758.         default :
  759.           if (wParam >= LogLevelBaseMenuID+PSystemLog::Fatal && wParam < LogLevelBaseMenuID+PSystemLog::NumLogLevels)
  760.             SetLogLevel((PSystemLog::Level)(wParam-LogLevelBaseMenuID));
  761.           else if (wParam >= SvcCmdBaseMenuID && wParam < SvcCmdBaseMenuID+NumSvcCmds) {
  762.             const char * cmdname = ServiceCommandNames[wParam-SvcCmdBaseMenuID];
  763.             if (wParam == SvcCmdBaseMenuID+SvcCmdVersion ||
  764.                 MessageBox(hWnd, cmdname & GetName() & "?", GetName(),
  765.                            MB_ICONQUESTION|MB_YESNO) == IDYES)
  766.               ProcessCommand(cmdname);
  767.           }
  768.       }
  769.       break;
  770.     // Notification of event over sysTray icon
  771.     case UWM_SYSTRAY :
  772.       switch (lParam) {
  773.         case WM_MOUSEMOVE :
  774.           // update status of process for tool tips if no buttons down
  775.           if (wParam == SYSTRAY_ICON_ID) {
  776.             PNotifyIconData nid(hWnd, NIF_TIP,
  777.                           GetName() & (IsServiceRunning(this) ? "is" : "not") & "running.");
  778.             nid.Modify(); // Modify tooltip
  779.           }
  780.           break;
  781.         // Click on icon - display message
  782.         case WM_LBUTTONDBLCLK :
  783.           if (IsServiceRunning(this))
  784.             OnControl();
  785.           else {
  786.             SetForegroundWindow(hWnd); // Our MessageBox pops up in front
  787.             MessageBox(hWnd, "Service is not running!", GetName(), MB_TASKMODAL);
  788.           }
  789.           break;
  790.         // Popup menu
  791.         case WM_RBUTTONUP :
  792.           POINT pt;
  793.           GetCursorPos(&pt);
  794.           HMENU menu = CreatePopupMenu();
  795.           AppendMenu(menu, MF_STRING, ControlMenuID, "&Open Properties");
  796.           AppendMenu(menu, MF_SEPARATOR, 0, NULL);
  797.           AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdVersion, "&Version");
  798.           if (IsServiceRunning(this)) {
  799.             MENUITEMINFO inf;
  800.             inf.cbSize = sizeof(inf);
  801.             inf.fMask = MIIM_STATE;
  802.             inf.fState = MFS_DEFAULT;
  803.             SetMenuItemInfo(menu, ControlMenuID, FALSE, &inf);
  804.             AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdStop, "&Stop Service");
  805.           }
  806.           else {
  807.             EnableMenuItem(menu, ControlMenuID, MF_GRAYED);
  808.             AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdStart, "&Start Service");
  809.           }
  810.           AppendMenu(menu, MF_STRING, SvcCmdBaseMenuID+SvcCmdNoTray, "&Tray Icon");
  811.           CheckMenuItem(menu, SvcCmdBaseMenuID+SvcCmdNoTray,
  812.                         TrayIconRegistry(this, CheckTrayIcon) ? MF_CHECKED : MF_UNCHECKED);
  813.           AppendMenu(menu, MF_SEPARATOR, 0, NULL);
  814.           AppendMenu(menu, MF_STRING, ExitMenuID, "&Close");
  815.           /* SetForegroundWindow and the ensuing null PostMessage is a
  816.              workaround for a Windows 95 bug (see MSKB article Q135788,
  817.              http://www.microsoft.com/kb/articles/q135/7/88.htm, I think).
  818.              In typical Microsoft style this bug is listed as "by design".
  819.              SetForegroundWindow also causes our MessageBox to pop up in front
  820.              of any other application's windows. */
  821.           SetForegroundWindow(hWnd);
  822.           /* We specifiy TPM_RETURNCMD, so TrackPopupMenu returns the menu
  823.              selection instead of returning immediately and our getting a
  824.              WM_COMMAND with the selection. You don't have to do it this way.
  825.           */
  826.           WndProc(hWnd, WM_COMMAND, TrackPopupMenu(menu,            // Popup menu to track
  827.                                                    TPM_RETURNCMD |  // Return menu code
  828.                                                    TPM_RIGHTBUTTON, // Track right mouse button?
  829.                                                    pt.x, pt.y,      // screen coordinates
  830.                                                    0,               // reserved
  831.                                                    hWnd,            // owner
  832.                                                    NULL),           // LPRECT user can click in without dismissing menu
  833.                                                    0);
  834.           PostMessage(hWnd, 0, 0, 0); // see above
  835.           DestroyMenu(menu); // Delete loaded menu and reclaim its resources
  836.           break;
  837.       }
  838.   }
  839.   return DefWindowProc(hWnd, msg, wParam, lParam);
  840. }
  841. void PServiceProcess::DebugOutput(const char * out)
  842. {
  843.   if (controlWindow == NULL || debugWindow == NULL)
  844.     return;
  845.   if (debugWindow == (HWND)-1) {
  846.     MessageBox(controlWindow, out, GetName(), MB_TASKMODAL);
  847.     return;
  848.   }
  849.   if (!IsWindowVisible(controlWindow))
  850.     ShowWindow(controlWindow, SW_SHOWDEFAULT);
  851.   int len = strlen(out);
  852.   int max = isWin95 ? 32000 : 128000;
  853.   while (GetWindowTextLength(debugWindow)+len >= max) {
  854.     SendMessage(debugWindow, WM_SETREDRAW, FALSE, 0);
  855.     DWORD start, finish;
  856.     SendMessage(debugWindow, EM_GETSEL, (WPARAM)&start, (LPARAM)&finish);
  857.     SendMessage(debugWindow, EM_SETSEL, 0,
  858.                 SendMessage(debugWindow, EM_LINEINDEX, 1, 0));
  859.     SendMessage(debugWindow, EM_REPLACESEL, FALSE, (DWORD)"");
  860.     SendMessage(debugWindow, EM_SETSEL, start, finish);
  861.     SendMessage(debugWindow, WM_SETREDRAW, TRUE, 0);
  862.   }
  863.   SendMessage(debugWindow, EM_SETSEL, max, max);
  864.   char * lf;
  865.   const char * prev = out;
  866.   while ((lf = strchr(prev, 'n')) != NULL) {
  867.     if (*(lf-1) == 'r')
  868.       prev = lf+1;
  869.     else {
  870.       *lf++ = '';
  871.       SendMessage(debugWindow, EM_REPLACESEL, FALSE, (DWORD)out);
  872.       SendMessage(debugWindow, EM_REPLACESEL, FALSE, (DWORD)"rn");
  873.       prev = out = lf;
  874.     }
  875.   }
  876.   if (*out != '')
  877.     SendMessage(debugWindow, EM_REPLACESEL, FALSE, (DWORD)out);
  878. }
  879. void PServiceProcess::StaticMainEntry(DWORD argc, LPTSTR * argv)
  880. {
  881.   Current().MainEntry(argc, argv);
  882. }
  883. void PServiceProcess::MainEntry(DWORD argc, LPTSTR * argv)
  884. {
  885.   // SERVICE_STATUS members that don't change
  886.   status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  887.   status.dwServiceSpecificExitCode = 0;
  888.   // register our service control handler:
  889.   statusHandle = RegisterServiceCtrlHandler(GetName(), StaticControlEntry);
  890.   if (statusHandle == NULL)
  891.     return;
  892.   // report the status to Service Control Manager.
  893.   if (!ReportStatus(SERVICE_START_PENDING, NO_ERROR, 1, 20000))
  894.     return;
  895.   // create the stop event object. The control handler function signals
  896.   // this event when it receives the "stop" control code.
  897.   terminationEvent = CreateEvent(NULL, TRUE, FALSE, (const char *)GetName());
  898.   if (terminationEvent == NULL)
  899.     return;
  900.   startedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  901.   if (startedEvent == NULL)
  902.     return;
  903.   GetArguments().SetArgs(argc, argv);
  904.   // start the thread that performs the work of the service.
  905.   threadHandle = (HANDLE)_beginthread(StaticThreadEntry, 0, this);
  906.   if (threadHandle != (HANDLE)-1) {
  907.     while (WaitForSingleObject(startedEvent, 10000) == WAIT_TIMEOUT) {
  908.       if (!ReportStatus(SERVICE_START_PENDING, NO_ERROR, 1, 20000))
  909.         return;
  910.     }
  911.     // Wait here for the end
  912.     WaitForSingleObject(terminationEvent, INFINITE);
  913.   }
  914.   CloseHandle(startedEvent);
  915.   CloseHandle(terminationEvent);
  916.   ReportStatus(SERVICE_STOPPED, 0);
  917. }
  918. void PServiceProcess::StaticThreadEntry(void * arg)
  919. {
  920.   ((PServiceProcess *)arg)->ThreadEntry();
  921. }
  922. void PServiceProcess::ThreadEntry()
  923. {
  924.   activeThreadMutex.Wait();
  925.   threadId = GetCurrentThreadId();
  926.   threadHandle = GetCurrentThread();
  927.   activeThreads.SetAt(threadId, this);
  928.   activeThreadMutex.Signal();
  929.   SetTerminationValue(1);
  930.   if (OnStart()) {
  931.     if (!debugMode)
  932.       SetEvent(startedEvent);
  933.     ReportStatus(SERVICE_RUNNING);
  934.     SetTerminationValue(0);
  935.     Main();
  936.     ReportStatus(SERVICE_STOP_PENDING, NO_ERROR, 1, 30000);
  937.   }
  938.   SetEvent(terminationEvent);
  939. }
  940. void PServiceProcess::StaticControlEntry(DWORD code)
  941. {
  942.   Current().ControlEntry(code);
  943. }
  944. void PServiceProcess::ControlEntry(DWORD code)
  945. {
  946.   switch (code) {
  947.     case SERVICE_CONTROL_PAUSE : // Pause the service if it is running.
  948.       if (status.dwCurrentState != SERVICE_RUNNING)
  949.         ReportStatus(status.dwCurrentState);
  950.       else {
  951.         if (OnPause())
  952.           ReportStatus(SERVICE_PAUSED);
  953.       }
  954.       break;
  955.     case SERVICE_CONTROL_CONTINUE : // Resume the paused service.
  956.       if (status.dwCurrentState == SERVICE_PAUSED)
  957.         OnContinue();
  958.       ReportStatus(status.dwCurrentState);
  959.       break;
  960.     case SERVICE_CONTROL_STOP : // Stop the service.
  961.       // Report the status, specifying the checkpoint and waithint, before
  962.       // setting the termination event.
  963.       ReportStatus(SERVICE_STOP_PENDING, NO_ERROR, 1, 30000);
  964.       OnStop();
  965.       SetEvent(terminationEvent);
  966.       break;
  967.     case SERVICE_CONTROL_INTERROGATE : // Update the service status.
  968.     default :
  969.       ReportStatus(status.dwCurrentState);
  970.   }
  971. }
  972. BOOL PServiceProcess::ReportStatus(DWORD dwCurrentState,
  973.                                    DWORD dwWin32ExitCode,
  974.                                    DWORD dwCheckPoint,
  975.                                    DWORD dwWaitHint)
  976. {
  977.   // Disable control requests until the service is started.
  978.   if (dwCurrentState == SERVICE_START_PENDING)
  979.     status.dwControlsAccepted = 0;
  980.   else
  981.     status.dwControlsAccepted =
  982.                            SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
  983.   // These SERVICE_STATUS members are set from parameters.
  984.   status.dwCurrentState = dwCurrentState;
  985.   status.dwWin32ExitCode = dwWin32ExitCode;
  986.   status.dwCheckPoint = dwCheckPoint;
  987.   status.dwWaitHint = dwWaitHint;
  988.   if (debugMode || isWin95)
  989.     return TRUE;
  990.   // Report the status of the service to the service control manager.
  991.   if (SetServiceStatus(statusHandle, &status))
  992.     return TRUE;
  993.   // If an error occurs, stop the service.
  994.   PSystemLog::Output(PSystemLog::Error, "SetServiceStatus failed");
  995.   return FALSE;
  996. }
  997. void PServiceProcess::OnStop()
  998. {
  999. }
  1000. BOOL PServiceProcess::OnPause()
  1001. {
  1002.   SuspendThread(threadHandle);
  1003.   return TRUE;
  1004. }
  1005. void PServiceProcess::OnContinue()
  1006. {
  1007.   ResumeThread(threadHandle);
  1008. }
  1009. void PServiceProcess::OnControl()
  1010. {
  1011. }
  1012. class ServiceManager
  1013. {
  1014.   public:
  1015.     ServiceManager()  { error = 0; }
  1016.     virtual BOOL Create(PServiceProcess * svc) = 0;
  1017.     virtual BOOL Delete(PServiceProcess * svc) = 0;
  1018.     virtual BOOL Start(PServiceProcess * svc) = 0;
  1019.     virtual BOOL Stop(PServiceProcess * svc) = 0;
  1020.     virtual BOOL Pause(PServiceProcess * svc) = 0;
  1021.     virtual BOOL Resume(PServiceProcess * svc) = 0;
  1022.     DWORD GetError() const { return error; }
  1023.   protected:
  1024.     DWORD error;
  1025. };
  1026. class Win95_ServiceManager : public ServiceManager
  1027. {
  1028.   public:
  1029.     virtual BOOL Create(PServiceProcess * svc);
  1030.     virtual BOOL Delete(PServiceProcess * svc);
  1031.     virtual BOOL Start(PServiceProcess * svc);
  1032.     virtual BOOL Stop(PServiceProcess * svc);
  1033.     virtual BOOL Pause(PServiceProcess * svc);
  1034.     virtual BOOL Resume(PServiceProcess * svc);
  1035. };
  1036. BOOL Win95_ServiceManager::Create(PServiceProcess * svc)
  1037. {
  1038.   HKEY key;
  1039.   if (RegCreateKey(HKEY_LOCAL_MACHINE,
  1040.                    "SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
  1041.                    &key) == ERROR_SUCCESS) {
  1042.     RegDeleteValue(key, (char *)(const char *)svc->GetName());
  1043.     RegCloseKey(key);
  1044.   }
  1045.   if ((error = RegCreateKey(HKEY_LOCAL_MACHINE,
  1046.                             "SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices",
  1047.                             &key)) != ERROR_SUCCESS)
  1048.     return FALSE;
  1049.   PString cmd = """ + svc->GetFile() + """;
  1050.   error = RegSetValueEx(key, svc->GetName(), 0, REG_SZ,
  1051.                         (LPBYTE)(const char *)cmd, cmd.GetLength() + 1);
  1052.   RegCloseKey(key);
  1053.   return error == ERROR_SUCCESS;
  1054. }
  1055. BOOL Win95_ServiceManager::Delete(PServiceProcess * svc)
  1056. {
  1057.   HKEY key;
  1058.   if (RegCreateKey(HKEY_LOCAL_MACHINE,
  1059.                    "SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
  1060.                    &key) == ERROR_SUCCESS) {
  1061.     RegDeleteValue(key, (char *)(const char *)svc->GetName());
  1062.     RegCloseKey(key);
  1063.   }
  1064.   if ((error = RegCreateKey(HKEY_LOCAL_MACHINE,
  1065.                             "SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices",
  1066.                             &key)) != ERROR_SUCCESS)
  1067.     return FALSE;
  1068.   error = RegDeleteValue(key, (char *)(const char *)svc->GetName());
  1069.   RegCloseKey(key);
  1070.   return error == ERROR_SUCCESS;
  1071. }
  1072. BOOL Win95_ServiceManager::Start(PServiceProcess * service)
  1073. {
  1074.   if (IsServiceRunning(service)) {
  1075.     PError << "Service already running" << endl;
  1076.     error = 1;
  1077.     return FALSE;
  1078.   }
  1079.   BOOL ok = _spawnl(_P_DETACH, service->GetFile(), service->GetFile(), NULL) >= 0;
  1080.   error = errno;
  1081.   return ok;
  1082. }
  1083. BOOL Win95_ServiceManager::Stop(PServiceProcess * service)
  1084. {
  1085.   HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, service->GetName());
  1086.   if (hEvent == NULL) {
  1087.     error = ::GetLastError();
  1088.     PError << "Service is not running" << endl;
  1089.     return FALSE;
  1090.   }
  1091.   SetEvent(hEvent);
  1092.   CloseHandle(hEvent);
  1093.   return TRUE;
  1094. }
  1095. BOOL Win95_ServiceManager::Pause(PServiceProcess *)
  1096. {
  1097.   PError << "Cannot pause service under Windows 95" << endl;
  1098.   error = 1;
  1099.   return FALSE;
  1100. }
  1101. BOOL Win95_ServiceManager::Resume(PServiceProcess *)
  1102. {
  1103.   PError << "Cannot resume service under Windows 95" << endl;
  1104.   error = 1;
  1105.   return FALSE;
  1106. }
  1107. class NT_ServiceManager : public ServiceManager
  1108. {
  1109.   public:
  1110.     NT_ServiceManager()  { schSCManager = schService = NULL; }
  1111.     ~NT_ServiceManager();
  1112.     BOOL Create(PServiceProcess * svc);
  1113.     BOOL Delete(PServiceProcess * svc);
  1114.     BOOL Start(PServiceProcess * svc);
  1115.     BOOL Stop(PServiceProcess * svc)
  1116.       { return Control(svc, SERVICE_CONTROL_STOP); }
  1117.     BOOL Pause(PServiceProcess * svc)
  1118.       { return Control(svc, SERVICE_CONTROL_PAUSE); }
  1119.     BOOL Resume(PServiceProcess * svc)
  1120.       { return Control(svc, SERVICE_CONTROL_CONTINUE); }
  1121.     DWORD GetError() const { return error; }
  1122.   private:
  1123.     BOOL OpenManager();
  1124.     BOOL Open(PServiceProcess * svc);
  1125.     BOOL Control(PServiceProcess * svc, DWORD command);
  1126.     SC_HANDLE schSCManager, schService;
  1127. };
  1128. NT_ServiceManager::~NT_ServiceManager()
  1129. {
  1130.   if (schService != NULL)
  1131.     CloseServiceHandle(schService);
  1132.   if (schSCManager != NULL)
  1133.     CloseServiceHandle(schSCManager);
  1134. }
  1135. BOOL NT_ServiceManager::OpenManager()
  1136. {
  1137.   schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  1138.   if (schSCManager != NULL)
  1139.     return TRUE;
  1140.   error = ::GetLastError();
  1141.   PError << "Could not open Service Manager." << endl;
  1142.   return FALSE;
  1143. }
  1144. BOOL NT_ServiceManager::Open(PServiceProcess * svc)
  1145. {
  1146.   if (!OpenManager())
  1147.     return FALSE;
  1148.   schService = OpenService(schSCManager, svc->GetName(), SERVICE_ALL_ACCESS);
  1149.   if (schService != NULL)
  1150.     return TRUE;
  1151.   error = ::GetLastError();
  1152.   PError << "Service is not installed." << endl;
  1153.   return FALSE;
  1154. }
  1155. BOOL NT_ServiceManager::Create(PServiceProcess * svc)
  1156. {
  1157.   if (!OpenManager())
  1158.     return FALSE;
  1159.   schService = OpenService(schSCManager, svc->GetName(), SERVICE_ALL_ACCESS);
  1160.   if (schService != NULL) {
  1161.     PError << "Service is already installed." << endl;
  1162.     return FALSE;
  1163.   }
  1164.   PString binaryFilename;
  1165.   GetShortPathName(svc->GetFile(), binaryFilename.GetPointer(_MAX_PATH), _MAX_PATH);
  1166.   schService = CreateService(
  1167.                     schSCManager,                   // SCManager database
  1168.                     svc->GetName(),                 // name of service
  1169.                     svc->GetName(),                 // name to display
  1170.                     SERVICE_ALL_ACCESS,             // desired access
  1171.                     SERVICE_WIN32_OWN_PROCESS,      // service type
  1172.                     SERVICE_AUTO_START,             // start type
  1173.                     SERVICE_ERROR_NORMAL,           // error control type
  1174.                     binaryFilename,                 // service's binary
  1175.                     NULL,                           // no load ordering group
  1176.                     NULL,                           // no tag identifier
  1177.                     svc->GetServiceDependencies(),  // no dependencies
  1178.                     NULL,                           // LocalSystem account
  1179.                     NULL);                          // no password
  1180.   if (schService == NULL) {
  1181.     error = ::GetLastError();
  1182.     return FALSE;
  1183.   }
  1184.   HKEY key;
  1185.   if ((error = RegCreateKey(HKEY_LOCAL_MACHINE,
  1186.              "SYSTEM\CurrentControlSet\Services\EventLog\Application\" +
  1187.                                        svc->GetName(), &key)) != ERROR_SUCCESS)
  1188.     return FALSE;
  1189.   LPBYTE fn = (LPBYTE)(const char *)binaryFilename;
  1190.   PINDEX fnlen = binaryFilename.GetLength()+1;
  1191.   if ((error = RegSetValueEx(key, "EventMessageFile",
  1192.                              0, REG_EXPAND_SZ, fn, fnlen)) == ERROR_SUCCESS &&
  1193.       (error = RegSetValueEx(key, "CategoryMessageFile",
  1194.                              0, REG_EXPAND_SZ, fn, fnlen)) == ERROR_SUCCESS) {
  1195.     DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
  1196.     if ((error = RegSetValueEx(key, "TypesSupported",
  1197.                                0, REG_DWORD, (LPBYTE)&dwData, sizeof(DWORD))) == ERROR_SUCCESS) {
  1198.       dwData = PSystemLog::NumLogLevels;
  1199.       error = RegSetValueEx(key, "CategoryCount", 0, REG_DWORD, (LPBYTE)&dwData, sizeof(DWORD));
  1200.     }
  1201.   }
  1202.   RegCloseKey(key);
  1203.   return error == ERROR_SUCCESS;
  1204. }
  1205. BOOL NT_ServiceManager::Delete(PServiceProcess * svc)
  1206. {
  1207.   if (!Open(svc))
  1208.     return FALSE;
  1209.   PString name = "SYSTEM\CurrentControlSet\Services\EventLog\Application\" + svc->GetName();
  1210.   error = ::RegDeleteKey(HKEY_LOCAL_MACHINE, (char *)(const char *)name);
  1211.   if (!::DeleteService(schService))
  1212.     error = ::GetLastError();
  1213.   return error == ERROR_SUCCESS;
  1214. }
  1215. BOOL NT_ServiceManager::Start(PServiceProcess * svc)
  1216. {
  1217.   if (!Open(svc))
  1218.     return FALSE;
  1219.   BOOL ok = ::StartService(schService, 0, NULL);
  1220.   error = ::GetLastError();
  1221.   return ok;
  1222. }
  1223. BOOL NT_ServiceManager::Control(PServiceProcess * svc, DWORD command)
  1224. {
  1225.   if (!Open(svc))
  1226.     return FALSE;
  1227.   SERVICE_STATUS status;
  1228.   BOOL ok = ::ControlService(schService, command, &status);
  1229.   error = ::GetLastError();
  1230.   return ok;
  1231. }
  1232. BOOL PServiceProcess::ProcessCommand(const char * cmd)
  1233. {
  1234.   PINDEX cmdNum = 0;
  1235.   while (stricmp(cmd, ServiceCommandNames[cmdNum]) != 0) {
  1236.     if (++cmdNum >= NumSvcCmds) {
  1237.       if (!CreateControlWindow(TRUE))
  1238.         return FALSE;
  1239.       if (*cmd != '')
  1240.         PError << "Unknown command "" << cmd << "".n";
  1241.       else
  1242.         PError << "Could not start service.n";
  1243.       PError << "usage: " << GetName() << " [ ";
  1244.       for (cmdNum = 0; cmdNum < NumSvcCmds-1; cmdNum++)
  1245.         PError << ServiceCommandNames[cmdNum] << " | ";
  1246.       PError << ServiceCommandNames[cmdNum] << " ]" << endl;
  1247.       return FALSE;
  1248.     }
  1249.   }
  1250.   NT_ServiceManager nt;
  1251.   Win95_ServiceManager win95;
  1252.   ServiceManager * svcManager;
  1253.   if (isWin95)
  1254.     svcManager = &win95;
  1255.   else
  1256.     svcManager = &nt;
  1257.   BOOL good = FALSE;
  1258.   switch (cmdNum) {
  1259.     case SvcCmdTray :
  1260.     {
  1261.       PNotifyIconData nid(controlWindow, NIF_MESSAGE|NIF_ICON, GetName());
  1262.       nid.hIcon = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(ICON_RESID), IMAGE_ICON, // 16x16 icon
  1263.                              GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
  1264.       nid.uCallbackMessage = UWM_SYSTRAY; // message sent to nid.hWnd
  1265.       nid.Add();    // This adds the icon
  1266.       debugWindow = (HWND)-1;
  1267.       return TRUE;
  1268.     }
  1269.     case SvcCmdNoTray :
  1270.       if (TrayIconRegistry(this, CheckTrayIcon)) {
  1271.         TrayIconRegistry(this, DelTrayIcon);
  1272.         PError << "Tray icon removed.";
  1273.       }
  1274.       else {
  1275.         TrayIconRegistry(this, AddTrayIcon);
  1276.         PError << "Tray icon installed.";
  1277.       }
  1278.       return TRUE;
  1279.     case SvcCmdVersion : // Version command
  1280.       ::SetLastError(0);
  1281.       PError << GetName() << ' '
  1282.              << GetOSClass() << '/' << GetOSName()
  1283.              << " Version " << GetVersion(TRUE) << endl;
  1284.       return TRUE;
  1285.     case SvcCmdInstall : // install
  1286.       good = svcManager->Create(this);
  1287.       TrayIconRegistry(this, AddTrayIcon);
  1288.       break;
  1289.     case SvcCmdRemove : // remove
  1290.       good = svcManager->Delete(this);
  1291.       TrayIconRegistry(this, DelTrayIcon);
  1292.       break;
  1293.     case SvcCmdStart : // start
  1294.       good = svcManager->Start(this);
  1295.       break;
  1296.     case SvcCmdStop : // stop
  1297.       good = svcManager->Stop(this);
  1298.       break;
  1299.     case SvcCmdPause : // pause
  1300.       good = svcManager->Pause(this);
  1301.       break;
  1302.     case SvcCmdResume : // resume
  1303.       good = svcManager->Resume(this);
  1304.       break;
  1305.     case SvcCmdDeinstall : // deinstall
  1306.       svcManager->Delete(this);
  1307.       TrayIconRegistry(this, DelTrayIcon);
  1308.       PConfig cfg;
  1309.       PStringList sections = cfg.GetSections();
  1310.       PINDEX i;
  1311.       for (i = 0; i < sections.GetSize(); i++)
  1312.         cfg.DeleteSection(sections[i]);
  1313.       good = TRUE;
  1314.       break;
  1315.   }
  1316.   SetLastError(0);
  1317.   PError << "Service command "" << ServiceCommandNames[cmdNum] << "" ";
  1318.   if (good)
  1319.     PError << "successful.";
  1320.   else {
  1321.     PError << "failed - ";
  1322.     switch (svcManager->GetError()) {
  1323.       case ERROR_ACCESS_DENIED :
  1324.         PError << "Access denied";
  1325.         break;
  1326.       default :
  1327.         PError << "error code = " << svcManager->GetError();
  1328.     }
  1329.   }
  1330.   PError << endl;
  1331.   return TRUE;
  1332. }
  1333. // End Of File ///////////////////////////////////////////////////////////////