NTService.cpp
上传用户:qdlutongda
上传日期:2007-01-14
资源大小:133k
文件大小:32k
源码类别:

Email客户端

开发平台:

Visual C++

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Copyright (C) 1997 by Joerg Koenig and the ADG mbH, Mannheim, Germany
  3. // All rights reserved
  4. //
  5. // Distribute freely, except: don't remove my name from the source or
  6. // documentation (don't take credit for my work), mark your changes (don't
  7. // get me blamed for your possible bugs), don't alter or remove this
  8. // notice.
  9. // No warrantee of any kind, express or implied, is included with this
  10. // software; use at your own risk, responsibility for damages (if any) to
  11. // anyone resulting from the use of this software rests entirely with the
  12. // user.
  13. //
  14. // Send bug reports, bug fixes, enhancements, requests, flames, etc., and
  15. // I'll try to keep a version up to date.  I can be reached as follows:
  16. //    J.Koenig@adg.de                 (company site)
  17. //    Joerg.Koenig@rhein-neckar.de    (private site)
  18. /////////////////////////////////////////////////////////////////////////////
  19. //
  20. // MODIFIED BY TODD C. WILSON FOR THE ROAD RUNNER NT LOGIN SERVICE.
  21. // HOWEVER, THESE MODIFICATIONS ARE BROADER IN SCOPE AND USAGE AND CAN BE USED
  22. // IN OTHER PROJECTS WITH NO CHANGES.
  23. // MODIFIED LINES FLAGGED/BRACKETED BY "//!! TCW MOD"
  24. //
  25. /////////////////////////////////////////////////////////////////////////////
  26. // last revised: $Date: 11.05.98 21:09 $, $Revision: 3 $
  27. /////////////////////////////////////////////////////////////////////////////
  28. // Acknoledgements:
  29. // o Thanks to Victor Vogelpoel (VictorV@Telic.nl) for his bug-fixes
  30. // and enhancements.
  31. // o Thanks to Todd C. Wilson (todd@mediatec.com) for the
  32. // "service" on Win95
  33. //
  34. // Changes:
  35. // 01/21/99
  36. // o Bug fixed in "DeregisterApplicationLog()"
  37. // thanks to Grahame Willis (grahamew@questsoftware.com.au):
  38. //
  39. // 04/30/98
  40. // o Added two more switches to handle command line arguments:
  41. // -e will force a running service to stop (corresponding
  42. // method in this class: virtual BOOL EndService();) and
  43. // -s will force the service to start (method:
  44. // virtual BOOL StartupService())
  45. //
  46. // 02/05/98
  47. // o Added the methods "RegisterApplicationLog()" and
  48. // "DeregisterApplicationLog()" (both virtual). The first one will be
  49. // called from "InstallService()" and creates some registry-entries
  50. // for a better event-log. The second one removes these entries when
  51. // the service will uninstall (see "RemoveService()")
  52. // o The service now obtains the security identifier of the current user
  53. // and uses this SID for event-logging.
  54. // o The memory allocated by "CommandLineToArgvW()" will now release
  55. // (UNICODE version only)
  56. // o The service now uses a simple message catalogue for a nicer
  57. // event logging
  58. #include <stdafx.h>
  59. ///#include <windows.h>
  60. #include <tchar.h>
  61. #include <stdio.h>
  62. #include <crtdbg.h>
  63. #include <io.h> //!! TCW MOD
  64. #include <fcntl.h> //!! TCW MOD
  65. #include "NTService.h"
  66. #include "NTServiceEventLogMsg.h"
  67. #ifndef RSP_SIMPLE_SERVICE
  68. #define RSP_SIMPLE_SERVICE 1
  69. #endif
  70. #ifndef RSP_UNREGISTER_SERVICE
  71. #define RSP_UNREGISTER_SERVICE 0
  72. #endif
  73. BOOL CNTService :: m_bInstance = FALSE;
  74. static CNTService * gpTheService = 0; // the one and only instance
  75. CNTService * AfxGetService() { return gpTheService; }
  76. static LPCTSTR gszAppRegKey = TEXT("SYSTEM\CurrentControlSet\Services\EventLog\Application\");
  77. static LPCTSTR gszWin95ServKey=TEXT("Software\Microsoft\Windows\CurrentVersion\RunServices"); //!! TCW MOD
  78. /////////////////////////////////////////////////////////////////////////////
  79. // class CNTService -- construction/destruction
  80. CNTService :: CNTService( LPCTSTR lpServiceName, LPCTSTR lpDisplayName )
  81. : m_lpServiceName(lpServiceName)
  82. , m_lpDisplayName(lpDisplayName ? lpDisplayName : lpServiceName)
  83. , m_dwCheckPoint(0)
  84. , m_dwErr(0)
  85. , m_bDebug(FALSE)
  86. , m_sshStatusHandle(0)
  87. , m_dwControlsAccepted(SERVICE_ACCEPT_STOP)
  88. , m_pUserSID(0)
  89. , m_fConsoleReady(FALSE)
  90. // parameters to the "CreateService()" function:
  91. , m_dwDesiredAccess(SERVICE_ALL_ACCESS)
  92. , m_dwServiceType(SERVICE_WIN32_OWN_PROCESS)
  93. , m_dwStartType(SERVICE_AUTO_START)
  94. , m_dwErrorControl(SERVICE_ERROR_NORMAL)
  95. , m_pszLoadOrderGroup(0)
  96. , m_dwTagID(0)
  97. , m_pszDependencies(0)
  98. , m_pszStartName(0)
  99. , m_pszPassword(0)
  100. {
  101. _ASSERTE( ! m_bInstance );
  102. OSVERSIONINFO vi;
  103. vi.dwOSVersionInfoSize=sizeof(vi);  // init this.
  104. GetVersionEx(&vi);      //lint !e534
  105. m_bWinNT = (vi.dwPlatformId == VER_PLATFORM_WIN32_NT);
  106. m_bInstance = TRUE;
  107. gpTheService = this;
  108. // SERVICE_STATUS members that rarely change
  109. m_ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  110. m_ssStatus.dwServiceSpecificExitCode = 0;
  111. if( m_bWinNT ) {
  112. /////////////////////////////////////////////////////////////////////////
  113. // Providing a SID (security identifier) was contributed by Victor
  114. // Vogelpoel (VictorV@Telic.nl).
  115. // The code from Victor was slightly modified.
  116. // Get security information of current user
  117. BYTE security_identifier_buffer[ 4096 ];
  118. DWORD dwSizeSecurityIdBuffer = sizeof( security_identifier_buffer );
  119. PSID user_security_identifier = NULL;
  120. TCHAR sUserName[ 256 ];
  121. DWORD dwSizeUserName  =  255;
  122. TCHAR sDomainName[ 256 ];
  123. DWORD dwSizeDomainName = 255;
  124. SID_NAME_USE sidTypeSecurityId;
  125. ::ZeroMemory( sUserName, sizeof( sUserName ) );
  126. ::ZeroMemory( sDomainName, sizeof( sDomainName ) );
  127. ::ZeroMemory( security_identifier_buffer, dwSizeSecurityIdBuffer );
  128. ::GetUserName( sUserName, &dwSizeUserName );
  129. if( ::LookupAccountName(
  130. 0,
  131. sUserName,
  132. &security_identifier_buffer,
  133. &dwSizeSecurityIdBuffer,
  134. sDomainName,
  135. &dwSizeDomainName,
  136. &sidTypeSecurityId
  137. )) {
  138. if( ::IsValidSid( PSID(security_identifier_buffer) ) ) {
  139. DWORD dwSidLen = ::GetLengthSid(PSID(security_identifier_buffer));
  140. m_pUserSID = PSID(new BYTE [dwSidLen]);
  141. ::CopySid(dwSidLen, m_pUserSID, security_identifier_buffer);
  142. _ASSERTE(::EqualSid(m_pUserSID, security_identifier_buffer));
  143. }
  144. }
  145. }
  146. /////////////////////////////////////////////////////////////////////////
  147. }
  148. CNTService :: ~CNTService() {
  149. _ASSERTE( m_bInstance );
  150. delete [] LPBYTE(m_pUserSID);
  151. m_bInstance = FALSE;
  152. gpTheService = 0;
  153. }
  154. /////////////////////////////////////////////////////////////////////////////
  155. // class CNTService -- overridables
  156. #define NEXT_ARG ((((*Argv)[2])==TEXT(''))?(--Argc,*++Argv):(*Argv)+2)
  157. BOOL CNTService :: RegisterService( int argc, char ** argv ) {
  158. BOOL (CNTService::* fnc)() = &CNTService::StartDispatcher;
  159.     DWORD Argc;
  160.     LPTSTR * Argv;
  161. #ifdef UNICODE
  162.     Argv = CommandLineToArgvW(GetCommandLineW(), &Argc );
  163. #else
  164.     Argc = (DWORD) argc;
  165.     Argv = argv;
  166. #endif
  167.     while( ++Argv, --Argc ) {
  168. if( Argv[0][0] == TEXT('-') ) {
  169. switch( Argv[0][1] ) {
  170. case TEXT('i'): // install the service
  171. fnc = &CNTService::InstallService;
  172. break;
  173. case TEXT('l'): // login-account (only useful with -i)
  174. m_pszStartName = NEXT_ARG;
  175. break;
  176. case TEXT('p'): // password (only useful with -i)
  177. m_pszPassword = NEXT_ARG;
  178. break;
  179. case TEXT('u'): // uninstall the service
  180. fnc = &CNTService::RemoveService;
  181. break;
  182. case TEXT('s'): // start the service
  183. fnc = &CNTService::StartupService;
  184. break;
  185. case TEXT('e'): // end the service
  186. fnc = &CNTService::EndService;
  187. break;
  188. case TEXT('d'): // debug the service
  189. case TEXT('f'): //!! TCW MOD faceless non-service (Win95) mode
  190. #ifdef UNICODE
  191. ::GlobalFree(HGLOBAL)Argv);
  192. #endif
  193. m_bDebug = TRUE;
  194. // pass original parameters to DebugService()
  195. return DebugService(argc, argv,(Argv[0][1]==TEXT('f'))); //!! TCW MOD faceless non-service (Win95) mode
  196. }
  197. }
  198. }
  199. #ifdef UNICODE
  200. ::GlobalFree(HGLOBAL)Argv);
  201. #endif
  202. //!! TCW MOD START - if Win95, run as faceless app.
  203. if( fnc == &CNTService::StartDispatcher && OsIsWin95() ) {
  204. // act as if -f was passed anyways.
  205. m_bDebug = TRUE;
  206. return DebugService(argc, argv, TRUE);
  207. }
  208. //!! TCW MOD END - if Win95, run as faceless app.
  209. return (this->*fnc)();
  210. }
  211. BOOL CNTService :: StartDispatcher() {
  212.     // Default implementation creates a single threaded service.
  213. // Override this method and provide more table entries for
  214. // a multithreaded service (one entry for each thread).
  215. SERVICE_TABLE_ENTRY dispatchTable[] =
  216.     {
  217.         { LPTSTR(m_lpServiceName), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
  218.         { 0, 0 }
  219.     };
  220. BOOL bRet = StartServiceCtrlDispatcher(dispatchTable);
  221. if( ! bRet ) {
  222. TCHAR szBuf[256];
  223.         AddToMessageLog(GetLastErrorText(szBuf,255));
  224. }
  225. return bRet;
  226. }
  227. BOOL CNTService :: InstallService() {
  228.     TCHAR szPath[1024];
  229. SetupConsole(); //!! TCW MOD - have to show the console here for the
  230. // diagnostic or error reason: orignal class assumed
  231. // that we were using _main for entry (a console app).
  232. // This particular usage is a Windows app (no console),
  233. // so we need to create it. Using SetupConsole with _main
  234. // is ok - does nothing, since you only get one console.
  235. if( GetModuleFileName( 0, szPath, 1023 ) == 0 ) {
  236. TCHAR szErr[256];
  237. _tprintf(TEXT("Unable to install %s - %sn"), m_lpDisplayName, GetLastErrorText(szErr, 256));
  238. return FALSE;
  239. }
  240. BOOL bRet = FALSE;
  241. if( OsIsWin95() ) { //!! TCW MOD - code added to install as Win95 service
  242. // Create a key for that application and insert values for
  243. // "EventMessageFile" and "TypesSupported"
  244. HKEY hKey = 0;
  245. LONG lRet = ERROR_SUCCESS;
  246. if( ::RegCreateKey(HKEY_LOCAL_MACHINE, gszWin95ServKey , &hKey) == ERROR_SUCCESS ) {
  247. lRet = ::RegSetValueEx(
  248. hKey, // handle of key to set value for
  249. m_lpServiceName, // address of value to set (NAME OF SERVICE)
  250. 0, // reserved
  251. REG_EXPAND_SZ, // flag for value type
  252. (CONST BYTE*)szPath,// address of value data
  253. _tcslen(szPath) + 1 // size of value data
  254. );
  255. ::RegCloseKey(hKey);
  256. bRet=TRUE;
  257. }
  258. } else {
  259. // Real NT services go here.
  260. SC_HANDLE schSCManager = OpenSCManager(
  261. 0, // machine (NULL == local)
  262. 0, // database (NULL == default)
  263. SC_MANAGER_ALL_ACCESS // access required
  264. );
  265. if( schSCManager ) {
  266. SC_HANDLE schService = CreateService(
  267. schSCManager,
  268. m_lpServiceName,
  269. m_lpDisplayName,
  270. m_dwDesiredAccess,
  271. m_dwServiceType,
  272. m_dwStartType,
  273. m_dwErrorControl,
  274. szPath,
  275. m_pszLoadOrderGroup,
  276. ((m_dwServiceType == SERVICE_KERNEL_DRIVER ||
  277.   m_dwServiceType == SERVICE_FILE_SYSTEM_DRIVER) &&
  278.  (m_dwStartType == SERVICE_BOOT_START ||
  279.   m_dwStartType == SERVICE_SYSTEM_START)) ?
  280. &m_dwTagID : 0,
  281. m_pszDependencies,
  282. m_pszStartName,
  283. m_pszPassword
  284. );
  285. if( schService ) {
  286. _tprintf(TEXT("%s installed.n"), m_lpDisplayName );
  287. CloseServiceHandle(schService);
  288. bRet = TRUE;
  289. } else {
  290. TCHAR szErr[256];
  291. _tprintf(TEXT("CreateService failed - %sn"), GetLastErrorText(szErr, 256));
  292. }
  293. CloseServiceHandle(schSCManager);
  294.  } else {
  295. TCHAR szErr[256];
  296. _tprintf(TEXT("OpenSCManager failed - %sn"), GetLastErrorText(szErr,256));
  297. }
  298. if( bRet ) {
  299. // installation succeeded. Now register the message file
  300. RegisterApplicationLog(
  301. szPath, // the path to the application itself
  302. EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE // supported types
  303. );
  304. AddToMessageLog(TEXT("Service installed"),EVENTLOG_INFORMATION_TYPE);
  305. }
  306. } //!! TCW MOD
  307. return bRet;
  308. }
  309. BOOL CNTService :: RemoveService() {
  310. BOOL bRet = FALSE;
  311. SetupConsole(); //!! TCW MOD - have to show the console here for the
  312. // diagnostic or error reason: orignal class assumed
  313. // that we were using _main for entry (a console app).
  314. // This particular usage is a Windows app (no console),
  315. // so we need to create it. Using SetupConsole with _main
  316. // is ok - does nothing, since you only get one console.
  317. if( OsIsWin95() ) { //!! TCW MOD - code added to install as Win95 service
  318. HKEY hKey = 0;
  319. LONG lRet = ERROR_SUCCESS;
  320. if( ::RegCreateKey(HKEY_LOCAL_MACHINE, gszWin95ServKey , &hKey) == ERROR_SUCCESS ) {
  321. lRet = ::RegDeleteValue(hKey, m_lpServiceName);
  322. ::RegCloseKey(hKey);
  323. bRet=TRUE;
  324. }
  325. } else {
  326. // Real NT services go here.
  327. SC_HANDLE schSCManager = OpenSCManager(
  328. 0, // machine (NULL == local)
  329. 0, // database (NULL == default)
  330. SC_MANAGER_ALL_ACCESS // access required
  331. );
  332. if( schSCManager ) {
  333. SC_HANDLE schService = OpenService(
  334. schSCManager,
  335. m_lpServiceName,
  336. SERVICE_ALL_ACCESS
  337. );
  338. if( schService ) {
  339. // try to stop the service
  340. if( ControlService(schService, SERVICE_CONTROL_STOP, &m_ssStatus) ) {
  341. _tprintf(TEXT("Stopping %s."), m_lpDisplayName);
  342. Sleep(1000);
  343. while( QueryServiceStatus(schService, &m_ssStatus) ) {
  344. if( m_ssStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
  345. _tprintf(TEXT("."));
  346. Sleep( 1000 );
  347. } else
  348. break;
  349. }
  350. if( m_ssStatus.dwCurrentState == SERVICE_STOPPED )
  351. _tprintf(TEXT("n%s stopped.n"), m_lpDisplayName);
  352.  else
  353.   _tprintf(TEXT("n%s failed to stop.n"), m_lpDisplayName);
  354. }
  355. // now remove the service
  356. if( DeleteService(schService) ) {
  357. _tprintf(TEXT("%s removed.n"), m_lpDisplayName);
  358. bRet = TRUE;
  359. } else {
  360. TCHAR szErr[256];
  361. _tprintf(TEXT("DeleteService failed - %sn"), GetLastErrorText(szErr,256));
  362. }
  363. CloseServiceHandle(schService);
  364. } else {
  365. TCHAR szErr[256];
  366. _tprintf(TEXT("OpenService failed - %sn"), GetLastErrorText(szErr,256));
  367. }
  368.   CloseServiceHandle(schSCManager);
  369.  } else {
  370. TCHAR szErr[256];
  371. _tprintf(TEXT("OpenSCManager failed - %sn"), GetLastErrorText(szErr,256));
  372. }
  373. if( bRet )
  374. DeregisterApplicationLog();
  375. }
  376. return bRet;
  377. }
  378. BOOL CNTService :: EndService() {
  379. BOOL bRet = FALSE;
  380. SC_HANDLE schSCManager = ::OpenSCManager(
  381. 0, // machine (NULL == local)
  382. 0, // database (NULL == default)
  383. SC_MANAGER_ALL_ACCESS // access required
  384. );
  385. if( schSCManager ) {
  386. SC_HANDLE schService = ::OpenService(
  387. schSCManager,
  388. m_lpServiceName,
  389. SERVICE_ALL_ACCESS
  390. );
  391. if( schService ) {
  392. // try to stop the service
  393. if( ::ControlService(schService, SERVICE_CONTROL_STOP, &m_ssStatus) ) {
  394. _tprintf(TEXT("Stopping %s."), m_lpDisplayName);
  395. ::Sleep(1000);
  396. while( ::QueryServiceStatus(schService, &m_ssStatus) ) {
  397. if( m_ssStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
  398. _tprintf(TEXT("."));
  399. ::Sleep( 1000 );
  400. } else
  401. break;
  402. }
  403. if( m_ssStatus.dwCurrentState == SERVICE_STOPPED )
  404. bRet = TRUE, _tprintf(TEXT("n%s stopped.n"), m_lpDisplayName);
  405.                 else
  406.                     _tprintf(TEXT("n%s failed to stop.n"), m_lpDisplayName);
  407. }
  408. ::CloseServiceHandle(schService);
  409. } else {
  410. TCHAR szErr[256];
  411. _tprintf(TEXT("OpenService failed - %sn"), GetLastErrorText(szErr,256));
  412. }
  413.         ::CloseServiceHandle(schSCManager);
  414.     } else {
  415. TCHAR szErr[256];
  416. _tprintf(TEXT("OpenSCManager failed - %sn"), GetLastErrorText(szErr,256));
  417. }
  418. return bRet;
  419. }
  420. BOOL CNTService :: StartupService() {
  421. BOOL bRet = FALSE;
  422. SC_HANDLE schSCManager = ::OpenSCManager(
  423. 0, // machine (NULL == local)
  424. 0, // database (NULL == default)
  425. SC_MANAGER_ALL_ACCESS // access required
  426. );
  427. if( schSCManager ) {
  428. SC_HANDLE schService = ::OpenService(
  429. schSCManager,
  430. m_lpServiceName,
  431. SERVICE_ALL_ACCESS
  432. );
  433. if( schService ) {
  434. // try to start the service
  435. _tprintf(TEXT("Starting up %s."), m_lpDisplayName);
  436. if( ::StartService(schService, 0, 0) ) {
  437. Sleep(1000);
  438. while( ::QueryServiceStatus(schService, &m_ssStatus) ) {
  439. if( m_ssStatus.dwCurrentState == SERVICE_START_PENDING ) {
  440. _tprintf(TEXT("."));
  441. Sleep( 1000 );
  442. } else
  443. break;
  444. }
  445. if( m_ssStatus.dwCurrentState == SERVICE_RUNNING )
  446. bRet = TRUE, _tprintf(TEXT("n%s started.n"), m_lpDisplayName);
  447.                 else
  448.                     _tprintf(TEXT("n%s failed to start.n"), m_lpDisplayName);
  449. } else {
  450. // StartService failed
  451. TCHAR szErr[256];
  452. _tprintf(TEXT("n%s failed to start: %sn"), m_lpDisplayName, GetLastErrorText(szErr,256));
  453. }
  454. ::CloseServiceHandle(schService);
  455. } else {
  456. TCHAR szErr[256];
  457. _tprintf(TEXT("OpenService failed - %sn"), GetLastErrorText(szErr,256));
  458. }
  459.         ::CloseServiceHandle(schSCManager);
  460.     } else {
  461. TCHAR szErr[256];
  462. _tprintf(TEXT("OpenSCManager failed - %sn"), GetLastErrorText(szErr,256));
  463. }
  464. return bRet;
  465. }
  466. ////////////////////////////////////////////////////////////////////////////
  467. //!! TCW MOD - faceless window procedure for usage within Win95 (mostly),
  468. // but can be invoked under NT by using -f
  469. LRESULT CALLBACK _FacelessWndProc_( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
  470. if (uMsg==WM_QUERYENDSESSION || uMsg==WM_ENDSESSION || uMsg==WM_QUIT) {
  471. if (lParam==NULL || uMsg==WM_QUIT) {
  472. DestroyWindow(hwnd); // kill me
  473. if (AfxGetService()!=NULL)
  474. AfxGetService()->Stop(); // stop me.
  475. return TRUE;
  476. }
  477. }
  478. return DefWindowProc(hwnd,uMsg,wParam,lParam);
  479. }
  480. ////////////////////////////////////////////////////////////////////////////
  481. BOOL CNTService :: DebugService(int argc, char ** argv, BOOL faceless) {
  482.     DWORD dwArgc;
  483.     LPTSTR *lpszArgv;
  484. #ifdef UNICODE
  485.     lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
  486. #else
  487.     dwArgc   = (DWORD) argc;
  488.     lpszArgv = argv;
  489. #endif
  490. if( !faceless ) { //!! TCW MOD - no faceless, so give it a face.
  491. SetupConsole(); //!! TCW MOD - make the console for debugging
  492.    _tprintf(TEXT("Debugging %s.n"), m_lpDisplayName);
  493. SetConsoleCtrlHandler(ControlHandler, TRUE);
  494. }
  495. //!! TCW MOD START - if Win95, register server
  496. typedef DWORD (WINAPI *fp_RegServProc)(DWORD dwProcessId,DWORD dwType);
  497. fp_RegServProc fncptr=NULL;
  498. if( faceless /*&& OsIsWin95()*/ ) {
  499. WNDCLASS wndclass;
  500. memset(&wndclass,0,sizeof(WNDCLASS));
  501. wndclass.lpfnWndProc = _FacelessWndProc_;
  502. wndclass.hInstance = HINSTANCE(::GetModuleHandle(0));
  503. wndclass.lpszClassName = TEXT("RRL__FacelessWndProc_");
  504. ATOM atom = ::RegisterClass(&wndclass);
  505. HWND hwnd = ::CreateWindow(wndclass.lpszClassName,TEXT(""),0,0,0,0,0,0,0,wndclass.hInstance,0);
  506. HMODULE hModule = ::GetModuleHandle(TEXT("kernel32.dll"));
  507. // punch F1 on "RegisterServiceProcess" for what it does and when to use it.
  508. fncptr=(fp_RegServProc)::GetProcAddress(hModule, "RegisterServiceProcess");
  509. if (fncptr!=NULL)
  510. (*fncptr)(0, RSP_SIMPLE_SERVICE);
  511. }
  512. //!! TCW MOD END - if Win95, register server
  513.     Run(dwArgc, lpszArgv);
  514. #ifdef UNICODE
  515. ::GlobalFree(HGLOBAL)lpszArgv);
  516. #endif
  517. if (fncptr!=NULL) //!! TCW MOD - if it's there, remove it: our run is over
  518. (*fncptr)(0, RSP_UNREGISTER_SERVICE);
  519. return TRUE;
  520. }
  521. void CNTService :: Pause() {
  522. }
  523. void CNTService :: Continue() {
  524. }
  525. void CNTService :: Shutdown() {
  526. }
  527. /////////////////////////////////////////////////////////////////////////////
  528. // class CNTService -- default handlers
  529. void WINAPI CNTService :: ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
  530. _ASSERTE( gpTheService != 0 );
  531. // register our service control handler:
  532. gpTheService->m_sshStatusHandle = RegisterServiceCtrlHandler(
  533. gpTheService->m_lpServiceName,
  534. CNTService::ServiceCtrl
  535. );
  536. if( gpTheService->m_sshStatusHandle )
  537. // report the status to the service control manager.
  538. if( gpTheService->ReportStatus(SERVICE_START_PENDING) ){
  539. gpTheService->Run( dwArgc, lpszArgv );}
  540. // try to report the stopped status to the service control manager.
  541. if( gpTheService->m_sshStatusHandle )
  542. gpTheService->ReportStatus(SERVICE_STOPPED);
  543. }
  544. void WINAPI CNTService :: ServiceCtrl(DWORD dwCtrlCode) {
  545. _ASSERTE( gpTheService != 0 );
  546. // Handle the requested control code.
  547. switch( dwCtrlCode ) {
  548. case SERVICE_CONTROL_STOP:
  549. // Stop the service.
  550. gpTheService->m_ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
  551. gpTheService->Stop();
  552. break;
  553. case SERVICE_CONTROL_PAUSE:
  554. gpTheService->m_ssStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
  555. gpTheService->Pause();
  556. break;
  557. case SERVICE_CONTROL_CONTINUE:
  558. gpTheService->m_ssStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
  559. gpTheService->Continue();
  560. break;
  561. case SERVICE_CONTROL_SHUTDOWN:
  562. gpTheService->Shutdown();
  563. break;
  564. case SERVICE_CONTROL_INTERROGATE:
  565. // Update the service status.
  566. gpTheService->ReportStatus(gpTheService->m_ssStatus.dwCurrentState);
  567. break;
  568. default:
  569. // invalid control code
  570. break;
  571. }
  572. }
  573. BOOL WINAPI CNTService :: ControlHandler(DWORD dwCtrlType) {
  574. _ASSERTE(gpTheService != 0);
  575. switch( dwCtrlType ) {
  576. case CTRL_BREAK_EVENT:  // use Ctrl+C or Ctrl+Break to simulate
  577. case CTRL_C_EVENT:      // SERVICE_CONTROL_STOP in debug mode
  578. _tprintf(TEXT("Stopping %s.n"), gpTheService->m_lpDisplayName);
  579. gpTheService->Stop();
  580. return TRUE;
  581. }
  582. return FALSE;
  583. }
  584. /////////////////////////////////////////////////////////////////////////////
  585. // class CNTService -- helpers
  586. //!! TCW MOD - added DWORD dwErrExit for error exit value. Defaults to zero
  587. BOOL CNTService :: ReportStatus(
  588. DWORD dwCurrentState,
  589. DWORD dwWaitHint,
  590. DWORD dwErrExit ) {
  591. BOOL fResult = TRUE;
  592. if( !m_bDebug ) { // when debugging we don't report to the SCM
  593.         if( dwCurrentState == SERVICE_START_PENDING)
  594.             m_ssStatus.dwControlsAccepted = 0;
  595.         else
  596.             m_ssStatus.dwControlsAccepted = m_dwControlsAccepted;
  597.         m_ssStatus.dwCurrentState = dwCurrentState;
  598.         m_ssStatus.dwWin32ExitCode = NO_ERROR;
  599.         m_ssStatus.dwWaitHint = dwWaitHint;
  600. //!! TCW MOD START - added code to support error exiting
  601. m_ssStatus.dwServiceSpecificExitCode = dwErrExit;
  602. if (dwErrExit!=0)
  603. m_ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  604. //!! TCW MOD END - added code to support error exiting
  605.         if( dwCurrentState == SERVICE_RUNNING ||
  606.             dwCurrentState == SERVICE_STOPPED )
  607.             m_ssStatus.dwCheckPoint = 0;
  608.         else
  609.             m_ssStatus.dwCheckPoint = ++m_dwCheckPoint;
  610.         // Report the status of the service to the service control manager.
  611.         if (!(fResult = SetServiceStatus( m_sshStatusHandle, &m_ssStatus))) {
  612.             AddToMessageLog(TEXT("SetServiceStatus() failed"));
  613.         }
  614.     }
  615.     return fResult;
  616. }
  617. void CNTService :: AddToMessageLog(LPTSTR lpszMsg, WORD wEventType, DWORD dwEventID) {
  618. m_dwErr = GetLastError();
  619. // use default message-IDs
  620. if( dwEventID == DWORD(-1) ) {
  621. switch( wEventType ) {
  622. case EVENTLOG_ERROR_TYPE:
  623. dwEventID = MSG_ERROR_1;
  624. break;
  625. case EVENTLOG_WARNING_TYPE:
  626. dwEventID = MSG_WARNING_1;
  627. break;
  628. case EVENTLOG_INFORMATION_TYPE:
  629. dwEventID = MSG_INFO_1;
  630. break;
  631. case EVENTLOG_AUDIT_SUCCESS:
  632. dwEventID = MSG_INFO_1;
  633. break;
  634. case EVENTLOG_AUDIT_FAILURE:
  635. dwEventID = MSG_INFO_1;
  636. break;
  637. default:
  638. dwEventID = MSG_INFO_1;
  639. break;
  640. }
  641. }
  642. // Use event logging to log the error.
  643. HANDLE hEventSource = RegisterEventSource(0, m_lpServiceName);
  644. if( hEventSource != 0 ) {
  645. LPCTSTR lpszMessage = lpszMsg;
  646. ReportEvent(
  647. hEventSource, // handle of event source
  648. wEventType, // event type
  649. 0, // event category
  650. dwEventID, // event ID
  651. m_pUserSID, // current user's SID
  652. 1, // strings in lpszStrings
  653. 0, // no bytes of raw data
  654. &lpszMessage, // array of error strings
  655. 0 // no raw data
  656. );
  657. ::DeregisterEventSource(hEventSource);
  658.     }
  659. }
  660. LPTSTR CNTService :: GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ) {
  661.     LPTSTR lpszTemp = 0;
  662.     DWORD dwRet = ::FormatMessage(
  663. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
  664. 0,
  665. GetLastError(),
  666. LANG_NEUTRAL,
  667. (LPTSTR)&lpszTemp,
  668. 0,
  669. 0
  670. );
  671.     if( !dwRet || (dwSize < dwRet+14) )
  672.         lpszBuf[0] = TEXT('');
  673.     else {
  674.         lpszTemp[_tcsclen(lpszTemp)-2] = TEXT('');  //remove cr/nl characters
  675.         _tcscpy(lpszBuf, lpszTemp);
  676.     }
  677.     if( lpszTemp )
  678.         LocalFree(HLOCAL(lpszTemp));
  679.     return lpszBuf;
  680. }
  681. /////////////////////////////////////////////////////////////////////////////
  682. // class CNTService -- implementation
  683. void CNTService :: RegisterApplicationLog( LPCTSTR lpszFileName, DWORD dwTypes ) {
  684. TCHAR szKey[256];
  685. _tcscpy(szKey, gszAppRegKey);
  686. _tcscat(szKey, m_lpServiceName);
  687. HKEY hKey = 0;
  688. LONG lRet = ERROR_SUCCESS;
  689. // Create a key for that application and insert values for
  690. // "EventMessageFile" and "TypesSupported"
  691. if( ::RegCreateKey(HKEY_LOCAL_MACHINE, szKey, &hKey) == ERROR_SUCCESS ) {
  692. lRet = ::RegSetValueEx(
  693. hKey, // handle of key to set value for
  694. TEXT("EventMessageFile"), // address of value to set
  695. 0, // reserved
  696. REG_EXPAND_SZ, // flag for value type
  697. (CONST BYTE*)lpszFileName, // address of value data
  698. _tcslen(lpszFileName) + 1 // size of value data
  699. );
  700. // Set the supported types flags.
  701. lRet = ::RegSetValueEx(
  702. hKey, // handle of key to set value for
  703. TEXT("TypesSupported"), // address of value to set
  704. 0, // reserved
  705. REG_DWORD, // flag for value type
  706. (CONST BYTE*)&dwTypes, // address of value data
  707. sizeof(DWORD) // size of value data
  708. );
  709. ::RegCloseKey(hKey);
  710. }
  711. // Add the service to the "Sources" value
  712. lRet = ::RegOpenKeyEx( 
  713. HKEY_LOCAL_MACHINE, // handle of open key 
  714. gszAppRegKey, // address of name of subkey to open 
  715. 0, // reserved 
  716. KEY_ALL_ACCESS, // security access mask 
  717. &hKey // address of handle of open key 
  718. );
  719. if( lRet == ERROR_SUCCESS ) {
  720. DWORD dwSize;
  721. // retrieve the size of the needed value
  722. lRet = ::RegQueryValueEx(
  723. hKey, // handle of key to query 
  724. TEXT("Sources"),// address of name of value to query 
  725. 0, // reserved 
  726. 0, // address of buffer for value type 
  727. 0, // address of data buffer 
  728. &dwSize // address of data buffer size 
  729. );
  730.   if( lRet == ERROR_SUCCESS ) {
  731. DWORD dwType;
  732. DWORD dwNewSize = dwSize+_tcslen(m_lpServiceName)+1;
  733. LPBYTE Buffer = LPBYTE(::GlobalAlloc(GPTR, dwNewSize));
  734. lRet = ::RegQueryValueEx(
  735. hKey, // handle of key to query 
  736. TEXT("Sources"),// address of name of value to query 
  737. 0, // reserved 
  738. &dwType, // address of buffer for value type 
  739. Buffer, // address of data buffer 
  740. &dwSize // address of data buffer size 
  741. );
  742. if( lRet == ERROR_SUCCESS ) {
  743. _ASSERTE(dwType == REG_MULTI_SZ);
  744. // check whether this service is already a known source
  745. register LPTSTR p = LPTSTR(Buffer);
  746. for(; *p; p += _tcslen(p)+1 ) {
  747. if( _tcscmp(p, m_lpServiceName) == 0 )
  748. break;
  749. }
  750. if( ! * p ) {
  751. // We're standing at the end of the stringarray
  752. // and the service does still not exist in the "Sources".
  753. // Now insert it at this point.
  754. // Note that we have already enough memory allocated
  755. // (see GlobalAlloc() above). We also don't need to append
  756. // an additional ''. This is done in GlobalAlloc() above
  757. // too.
  758. _tcscpy(p, m_lpServiceName);
  759. // OK - now store the modified value back into the
  760. // registry.
  761. lRet = ::RegSetValueEx(
  762. hKey, // handle of key to set value for
  763. TEXT("Sources"),// address of value to set
  764. 0, // reserved
  765. dwType, // flag for value type
  766. Buffer, // address of value data
  767. dwNewSize // size of value data
  768. );
  769. }
  770. }
  771. ::GlobalFree(HGLOBAL(Buffer));
  772. }
  773. ::RegCloseKey(hKey);
  774. }
  775. }
  776. void CNTService :: DeregisterApplicationLog() {
  777. TCHAR szKey[256];
  778. _tcscpy(szKey, gszAppRegKey);
  779. _tcscat(szKey, m_lpServiceName);
  780. HKEY hKey = 0;
  781. LONG lRet = ERROR_SUCCESS;
  782. lRet = ::RegDeleteKey(HKEY_LOCAL_MACHINE, szKey);
  783. // now we have to delete the application from the "Sources" value too.
  784. lRet = ::RegOpenKeyEx( 
  785. HKEY_LOCAL_MACHINE, // handle of open key 
  786. gszAppRegKey, // address of name of subkey to open 
  787. 0, // reserved 
  788. KEY_ALL_ACCESS, // security access mask 
  789. &hKey // address of handle of open key 
  790. );
  791. if( lRet == ERROR_SUCCESS ) {
  792. DWORD dwSize;
  793. // retrieve the size of the needed value
  794. lRet = ::RegQueryValueEx(
  795. hKey, // handle of key to query 
  796. TEXT("Sources"),// address of name of value to query 
  797. 0, // reserved 
  798. 0, // address of buffer for value type 
  799. 0, // address of data buffer 
  800. &dwSize // address of data buffer size 
  801. );
  802.   if( lRet == ERROR_SUCCESS ) {
  803. DWORD dwType;
  804. LPBYTE Buffer = LPBYTE(::GlobalAlloc(GPTR, dwSize));
  805. LPBYTE NewBuffer = LPBYTE(::GlobalAlloc(GPTR, dwSize));
  806. lRet = ::RegQueryValueEx(
  807. hKey, // handle of key to query 
  808. TEXT("Sources"),// address of name of value to query 
  809. 0, // reserved 
  810. &dwType, // address of buffer for value type 
  811. Buffer, // address of data buffer 
  812. &dwSize // address of data buffer size 
  813. );
  814. if( lRet == ERROR_SUCCESS ) {
  815. _ASSERTE(dwType == REG_MULTI_SZ);
  816. // check whether this service is already a known source
  817. register LPTSTR p = LPTSTR(Buffer);
  818. register LPTSTR pNew = LPTSTR(NewBuffer);
  819. BOOL bNeedSave = FALSE; // assume the value is already correct
  820. for(; *p; p += _tcslen(p)+1) {
  821. // except ourself: copy the source string into the destination
  822. if( _tcscmp(p, m_lpServiceName) != 0 ) {
  823. _tcscpy(pNew, p);
  824. pNew += _tcslen(pNew)+1;
  825. } else {
  826. bNeedSave = TRUE; // *this* application found
  827. dwSize -= _tcslen(p)+1; // new size of value
  828. }
  829. }
  830. if( bNeedSave ) {
  831. // OK - now store the modified value back into the
  832. // registry.
  833. lRet = ::RegSetValueEx(
  834. hKey, // handle of key to set value for
  835. TEXT("Sources"),// address of value to set
  836. 0, // reserved
  837. dwType, // flag for value type
  838. NewBuffer, // address of value data
  839. dwSize // size of value data
  840. );
  841. }
  842. }
  843. ::GlobalFree(HGLOBAL(Buffer));
  844. ::GlobalFree(HGLOBAL(NewBuffer));
  845. }
  846. ::RegCloseKey(hKey);
  847. }
  848. }
  849. ////////////////////////////////////////////////////////
  850. //!! TCW MOD - function to create console for faceless apps if not already there
  851. void CNTService::SetupConsole() {
  852. if( !m_fConsoleReady ) {
  853. AllocConsole(); // you only get 1 console.
  854. // lovely hack to get the standard io (printf, getc, etc) to the new console. Pretty much does what the
  855. // C lib does for us, but when we want it, and inside of a Window'd app.
  856. // The ugly look of this is due to the error checking (bad return values. Remove the if xxx checks if you like it that way.
  857. DWORD astds[3]={STD_OUTPUT_HANDLE,STD_ERROR_HANDLE,STD_INPUT_HANDLE};
  858. FILE *atrgs[3]={stdout,stderr,stdin};
  859. for( register int i=0; i<3; i++ ) {
  860. long hand=(long)GetStdHandle(astds[i]);
  861. if( hand!=(long)INVALID_HANDLE_VALUE ) {
  862. int osf=_open_osfhandle(hand,_O_TEXT);
  863. if( osf!=-1 ) {
  864. FILE *fp=_fdopen(osf,(astds[i]==STD_INPUT_HANDLE) ? "r" : "w");
  865. if( fp!=NULL ) {
  866. *(atrgs[i])=*fp;
  867. setvbuf(fp,NULL,_IONBF,0);
  868. }
  869. }
  870. }
  871. }
  872. m_fConsoleReady=TRUE;
  873. }
  874. }