Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

700 lines
21 KiB

  1. // mpsmpssv.cpp
  2. //
  3. // This is the main file containing the entry points.
  4. #include "NTServApp.h"
  5. #include "PMSPservice.h"
  6. #include <nserror.h>
  7. #include "svchost.h"
  8. #include <Sddl.h>
  9. #include <aclapi.h>
  10. #include <crtdbg.h>
  11. #include <wmsstd.h>
  12. HRESULT AddToSvcHostGroup();
  13. BOOL UnregisterOldServer( SC_HANDLE hSCM );
  14. STDAPI DllUnregisterServer(void);
  15. #define SVCHOST_SUBKEY "netsvcs"
  16. #define SVCHOST_SUBKEYW L"netsvcs"
  17. //#define DEBUG_STOP { _asm { int 3 }; }
  18. #define DEBUG_STOP
  19. HMODULE g_hDll = NULL;
  20. BOOL APIENTRY DllMain( HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
  21. {
  22. switch (ul_reason_for_call)
  23. {
  24. case DLL_PROCESS_ATTACH:
  25. g_hDll = (HMODULE)hModule;
  26. InitializeCriticalSection (&g_csLock);
  27. DisableThreadLibraryCalls (hModule);
  28. break;
  29. case DLL_PROCESS_DETACH:
  30. DeleteCriticalSection (&g_csLock);
  31. break;
  32. case DLL_THREAD_ATTACH:
  33. case DLL_THREAD_DETACH:
  34. _ASSERTE(0);
  35. break;
  36. }
  37. return TRUE;
  38. }
  39. // Main entry point to start service
  40. void ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
  41. {
  42. // We grab the lock so that any attempt to stop the service while
  43. // the object is being constructed or registered will be pended.
  44. EnterCriticalSection (&g_csLock);
  45. _ASSERTE(g_pService == NULL);
  46. DEBUG_STOP
  47. CNTService::DebugMsg("Entering CNTService::ServiceMain()");
  48. DWORD dwLastError;
  49. // Allocate this on the heap rather than the stack so that
  50. // we have a chance to call its destructor if the service
  51. // terminates ungracefully.
  52. CPMSPService* pService = new CPMSPService(dwLastError);
  53. if (pService == NULL)
  54. {
  55. LeaveCriticalSection (&g_csLock);
  56. dwLastError = ERROR_NOT_ENOUGH_MEMORY;
  57. // @@@@: What message do we log here
  58. // CNTService::LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_CTRLHANDLERNOTINSTALLED);
  59. CNTService::DebugMsg("Leaving CNTService::ServiceMain() CPMSPService constructor failed - last error %u", dwLastError);
  60. return;
  61. }
  62. CPMSPService& service = *pService;
  63. if (dwLastError != ERROR_SUCCESS)
  64. {
  65. LeaveCriticalSection (&g_csLock);
  66. // @@@@: What message do we log here
  67. // CNTService::LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_CTRLHANDLERNOTINSTALLED);
  68. CNTService::DebugMsg("Leaving CNTService::ServiceMain() CPMSPService constructor failed - last error %u", dwLastError);
  69. delete pService;
  70. return;
  71. }
  72. // Register the control request handler
  73. service.m_hServiceStatus = RegisterServiceCtrlHandler( SERVICE_NAME,
  74. CNTService::Handler );
  75. if (service.m_hServiceStatus == NULL)
  76. {
  77. LeaveCriticalSection (&g_csLock);
  78. CNTService::LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_CTRLHANDLERNOTINSTALLED);
  79. CNTService::DebugMsg("Leaving CNTService::ServiceMain() RegisterServiceCtrlHandler failed");
  80. delete pService;
  81. return;
  82. }
  83. service.SetStatus(SERVICE_START_PENDING);
  84. // Start the initialisation
  85. __try
  86. {
  87. g_pService = &service; // The Handler method will need to get a hold of this object
  88. LeaveCriticalSection (&g_csLock);
  89. if (service.Initialize()) {
  90. // Do the real work.
  91. // When the Run function returns, the service has stopped.
  92. service.m_bIsRunning = TRUE;
  93. service.Run();
  94. }
  95. }
  96. __finally
  97. {
  98. // Tell the service manager we are stopped and reset g_pService.
  99. // Note that we hold the crit sect while calling SetStatus so that
  100. // we have the final say on the status reported to the SCM.
  101. // Note: If the thread dies (e.g., av's), we clean up, but svchost
  102. // does not, so it is not possible to re-start the service. Consider
  103. // adding our own exception handler.
  104. EnterCriticalSection (&g_csLock);
  105. service.SetStatus(SERVICE_STOPPED);
  106. g_pService = NULL;
  107. LeaveCriticalSection (&g_csLock);
  108. CNTService::DebugMsg("Leaving CNTService::ServiceMain()");
  109. delete pService;
  110. }
  111. }
  112. HRESULT ModifySD(SC_HANDLE hService)
  113. {
  114. PACL pDacl = NULL;
  115. PACL pNewDacl = NULL;
  116. PSECURITY_DESCRIPTOR pSD = NULL;
  117. DWORD Err = ERROR_SUCCESS;
  118. PSID pAuthenUserSid = NULL;
  119. __try
  120. {
  121. //
  122. // Get DACL for the service object.
  123. //
  124. Err = GetSecurityInfo(hService, SE_SERVICE, DACL_SECURITY_INFORMATION,
  125. NULL, NULL, &pDacl, NULL, &pSD
  126. );
  127. if(Err != ERROR_SUCCESS)
  128. {
  129. __leave;
  130. }
  131. SID_IDENTIFIER_AUTHORITY Auth = SECURITY_NT_AUTHORITY;
  132. if(0 == AllocateAndInitializeSid(&Auth, 1, SECURITY_INTERACTIVE_RID,
  133. 0, 0, 0, 0, 0, 0, 0, &pAuthenUserSid)
  134. )
  135. {
  136. Err = GetLastError();
  137. __leave;
  138. }
  139. //
  140. // Initialize an EXPLICIT_ACCESS structure for the new ACE. The new ACE allows
  141. // authenticated users to start/stop our service.
  142. //
  143. EXPLICIT_ACCESS ExpAccess;
  144. ZeroMemory(&ExpAccess, sizeof(EXPLICIT_ACCESS));
  145. ExpAccess.grfAccessPermissions = SERVICE_START; // | SERVICE_STOP ;
  146. ExpAccess.grfAccessMode = GRANT_ACCESS;
  147. ExpAccess.grfInheritance = NO_INHERITANCE;
  148. ExpAccess.Trustee.pMultipleTrustee = NULL;
  149. ExpAccess.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
  150. ExpAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID;
  151. ExpAccess.Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
  152. ExpAccess.Trustee.ptstrName = (LPTSTR)pAuthenUserSid;
  153. //Create new DACL
  154. Err = SetEntriesInAcl(1, &ExpAccess, pDacl, &pNewDacl) ;
  155. if(ERROR_SUCCESS == Err)
  156. {
  157. // Update the security descriptor on the service
  158. Err = SetSecurityInfo(hService, SE_SERVICE, DACL_SECURITY_INFORMATION, NULL, NULL,
  159. pNewDacl, NULL);
  160. }
  161. }
  162. __finally
  163. {
  164. if(pSD)
  165. {
  166. LocalFree(pSD);
  167. }
  168. if(pAuthenUserSid){
  169. FreeSid(pAuthenUserSid);
  170. }
  171. if(pNewDacl)
  172. {
  173. LocalFree(pNewDacl);
  174. }
  175. }
  176. return HRESULT_FROM_WIN32(Err);
  177. }
  178. // Install and start service
  179. STDAPI DllRegisterServer(void)
  180. {
  181. HRESULT hr = E_FAIL;
  182. SC_HANDLE hSCM = NULL;
  183. SC_HANDLE hService = NULL;
  184. TCHAR pszDisplayName[256];
  185. char szKey[256];
  186. HKEY hKey = NULL;
  187. DEBUG_STOP;
  188. // Already installed?
  189. if( CNTService::IsInstalled() )
  190. {
  191. hr = DllUnregisterServer();
  192. if( !SUCCEEDED(hr) )
  193. {
  194. return hr;
  195. }
  196. }
  197. if( g_hDll == NULL )
  198. {
  199. return E_FAIL;
  200. }
  201. // Open the Service Control Manager
  202. hSCM = ::OpenSCManager( NULL, // local machine
  203. NULL, // ServicesActive database
  204. SC_MANAGER_ALL_ACCESS); // full access
  205. if (!hSCM)
  206. {
  207. hr = HRESULT_FROM_WIN32(GetLastError());
  208. goto Error;
  209. }
  210. //
  211. // On Win2k we have this service running as separate process which should
  212. // be uninstalled.
  213. //
  214. UnregisterOldServer( hSCM );
  215. // Get the path of this dll
  216. char szFilePath[MAX_PATH];
  217. if (::GetModuleFileName( g_hDll, szFilePath, ARRAYSIZE(szFilePath)) == 0)
  218. {
  219. hr = HRESULT_FROM_WIN32(GetLastError());
  220. goto Error;
  221. }
  222. // Create the service
  223. if (FormatMessage( FORMAT_MESSAGE_FROM_HMODULE, g_hDll, EVMSG_DISPLAYNAME,
  224. 0, pszDisplayName, ARRAYSIZE(pszDisplayName), NULL ) == 0)
  225. {
  226. hr = HRESULT_FROM_WIN32(GetLastError());
  227. goto Error;
  228. }
  229. hService = ::CreateService( hSCM,
  230. SERVICE_NAME,
  231. pszDisplayName,
  232. SERVICE_ALL_ACCESS,
  233. SERVICE_WIN32_SHARE_PROCESS,
  234. SERVICE_DEMAND_START,
  235. SERVICE_ERROR_NORMAL,
  236. "%SystemRoot%\\System32\\svchost.exe -k " SVCHOST_SUBKEY,
  237. NULL,
  238. NULL,
  239. NULL,
  240. NULL,
  241. NULL);
  242. if (!hService)
  243. {
  244. hr = HRESULT_FROM_WIN32(GetLastError());
  245. goto Error;
  246. }
  247. //
  248. // Modify the security descriptor on the created service so that
  249. // Authenticated Users can start/stop services. By default only admins can start/stop
  250. // services
  251. //
  252. hr = ModifySD(hService);
  253. if(!SUCCEEDED(hr))
  254. {
  255. goto Error;
  256. }
  257. // Set description of service, method only avalible for OS >= Win2K so we
  258. // need to load the dll, method in runtime.
  259. {
  260. typedef BOOL (WINAPI *funCSC2)(SC_HANDLE, DWORD, LPVOID );
  261. funCSC2 pChangeServiceConfig2 = NULL;
  262. HINSTANCE hDll = NULL;
  263. hDll = ::LoadLibraryExA( "advapi32.dll", NULL, 0 );
  264. if( hDll != NULL )
  265. {
  266. pChangeServiceConfig2 = (funCSC2)GetProcAddress( hDll, "ChangeServiceConfig2W");
  267. if( pChangeServiceConfig2 )
  268. {
  269. WCHAR pszDescription[1024];
  270. int iCharsLoaded = 0;
  271. SERVICE_DESCRIPTIONW sd;
  272. iCharsLoaded = FormatMessageW( FORMAT_MESSAGE_FROM_HMODULE, g_hDll, EVMSG_DESCRIPTION,
  273. 0, pszDescription, sizeof(pszDescription)/sizeof(pszDescription[0]), NULL );
  274. if( iCharsLoaded )
  275. {
  276. sd.lpDescription = pszDescription;
  277. pChangeServiceConfig2( hService,
  278. SERVICE_CONFIG_DESCRIPTION,
  279. &sd);
  280. }
  281. }
  282. FreeLibrary( hDll );
  283. }
  284. }
  285. // Add parameters subkey
  286. {
  287. strcpy(szKey, "SYSTEM\\CurrentControlSet\\Services\\");
  288. strcat(szKey, SERVICE_NAME);
  289. strcat(szKey, "\\Parameters");
  290. hr = ::RegCreateKey(HKEY_LOCAL_MACHINE, szKey, &hKey);
  291. if( hr != ERROR_SUCCESS)
  292. {
  293. hr = HRESULT_FROM_WIN32(hr);
  294. goto Error;
  295. }
  296. // Add the Event ID message-file name to the 'EventMessageFile' subkey.
  297. hr = ::RegSetValueEx(hKey,
  298. "ServiceDll",
  299. 0,
  300. REG_EXPAND_SZ,
  301. (CONST BYTE*)szFilePath,
  302. strlen(szFilePath) + 1);
  303. if( hr != ERROR_SUCCESS)
  304. {
  305. hr = HRESULT_FROM_WIN32(hr);
  306. ::RegCloseKey(hKey);
  307. goto Error;
  308. }
  309. ::RegCloseKey(hKey);
  310. }
  311. hr = AddToSvcHostGroup();
  312. if( FAILED(hr) ) goto Error;
  313. // make registry entries to support logging messages
  314. // Add the source name as a subkey under the Application
  315. // key in the EventLog service portion of the registry.
  316. {
  317. strcpy(szKey, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
  318. strcat(szKey, SERVICE_NAME);
  319. hr = ::RegCreateKey(HKEY_LOCAL_MACHINE, szKey, &hKey);
  320. if( hr != ERROR_SUCCESS)
  321. {
  322. hr = HRESULT_FROM_WIN32(hr);
  323. goto Error;
  324. }
  325. // Add the Event ID message-file name to the 'EventMessageFile' subkey.
  326. hr = ::RegSetValueEx(hKey,
  327. "EventMessageFile",
  328. 0,
  329. REG_EXPAND_SZ,
  330. (CONST BYTE*)szFilePath,
  331. strlen(szFilePath) + 1);
  332. if( hr != ERROR_SUCCESS)
  333. {
  334. hr = HRESULT_FROM_WIN32(hr);
  335. ::RegCloseKey(hKey);
  336. goto Error;
  337. }
  338. // Set the supported types flags.
  339. DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
  340. hr = ::RegSetValueEx(hKey,
  341. "TypesSupported",
  342. 0,
  343. REG_DWORD,
  344. (CONST BYTE*)&dwData,
  345. sizeof(DWORD));
  346. if( hr != ERROR_SUCCESS)
  347. {
  348. hr = HRESULT_FROM_WIN32(hr);
  349. ::RegCloseKey(hKey);
  350. goto Error;
  351. }
  352. ::RegCloseKey(hKey);
  353. }
  354. #if 0
  355. // Start service
  356. {
  357. SERVICE_STATUS ServiceStatus;
  358. if( !QueryServiceStatus( hService, &ServiceStatus ) )
  359. {
  360. hr = HRESULT_FROM_WIN32(GetLastError());
  361. goto Error;
  362. }
  363. if( ServiceStatus.dwCurrentState != SERVICE_RUNNING )
  364. {
  365. // start the service
  366. BOOL bStarted;
  367. bStarted = StartService(hService, 0, NULL);
  368. if( !bStarted )
  369. {
  370. hr = HRESULT_FROM_WIN32(GetLastError());
  371. // The service can not be started if it just was added to the svchost group.
  372. // The svchost needs to be restarted first.
  373. // (The svchost only reads it's service array at startup)
  374. if( hr == HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_IN_EXE) )
  375. {
  376. // This error code will be handled by the installer
  377. hr = NS_S_REBOOT_REQUIRED; // 0x000D2AF9L
  378. }
  379. goto Error;
  380. }
  381. }
  382. }
  383. #endif
  384. CNTService::LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_INSTALLED, SERVICE_NAME);
  385. hr = S_OK;
  386. Error:
  387. if( hService ) ::CloseServiceHandle(hService);
  388. if( hSCM ) ::CloseServiceHandle(hSCM);
  389. //
  390. // Check: should we return here NS_S_REBOOT_REQUIRED, if the service is installed.
  391. return hr;
  392. }
  393. // Stop and Uninstall service
  394. STDAPI DllUnregisterServer(void)
  395. {
  396. HRESULT hr = E_FAIL;
  397. char szKey[256];
  398. HKEY hKey = NULL;
  399. SC_HANDLE hSCM = NULL;
  400. SC_HANDLE hService = NULL;
  401. DEBUG_STOP
  402. // Not installed ?
  403. if( !CNTService::IsInstalled() )
  404. {
  405. return S_FALSE;
  406. }
  407. // Open the Service Control Manager
  408. hSCM = ::OpenSCManager( NULL, // local machine
  409. NULL, // ServicesActive database
  410. SC_MANAGER_ALL_ACCESS); // full access
  411. if (!hSCM)
  412. {
  413. hr = HRESULT_FROM_WIN32(GetLastError());
  414. goto Error;
  415. }
  416. hService = ::OpenService( hSCM,
  417. SERVICE_NAME,
  418. SERVICE_ALL_ACCESS);
  419. // Remove service
  420. if (hService)
  421. {
  422. // Stop service
  423. {
  424. SERVICE_STATUS ServiceStatus;
  425. if( !QueryServiceStatus( hService, &ServiceStatus ) )
  426. {
  427. hr = HRESULT_FROM_WIN32(GetLastError());
  428. goto Error;
  429. }
  430. if( ServiceStatus.dwCurrentState != SERVICE_STOPPED )
  431. {
  432. // start the service
  433. SERVICE_STATUS ss;
  434. BOOL bStopped;
  435. bStopped = ControlService( hService,
  436. SERVICE_CONTROL_STOP,
  437. &ss);
  438. if( !bStopped )
  439. {
  440. hr = HRESULT_FROM_WIN32(GetLastError());
  441. goto Error;
  442. }
  443. }
  444. }
  445. if (::DeleteService(hService))
  446. {
  447. CNTService::LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_REMOVED, SERVICE_NAME);
  448. hr = S_OK;
  449. }
  450. else
  451. {
  452. CNTService::LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_NOTREMOVED, SERVICE_NAME);
  453. hr = HRESULT_FROM_WIN32(GetLastError());
  454. // Do not delete eventlog related registry keys unless the service has been deleted
  455. goto Error;
  456. }
  457. }
  458. else
  459. {
  460. hr = HRESULT_FROM_WIN32(GetLastError());
  461. goto Error;
  462. }
  463. // Delete EventLog entry in registry
  464. strcpy(szKey, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
  465. strcat(szKey, SERVICE_NAME);
  466. RegDeleteKey( HKEY_LOCAL_MACHINE, szKey );
  467. Error:
  468. if(hSCM) ::CloseServiceHandle(hSCM);
  469. if(hService) ::CloseServiceHandle(hService);
  470. return hr;
  471. }
  472. // Add entry to the right svchost group, (netsvcs)
  473. HRESULT AddToSvcHostGroup()
  474. {
  475. HRESULT hr = S_OK;
  476. DWORD dwOrgSize;
  477. DWORD dwDestSize;
  478. long lResult;
  479. DWORD dwStrIndex;
  480. DWORD dwType;
  481. HKEY hKey = NULL;
  482. WCHAR* pwszStringOrg = NULL;
  483. WCHAR* pwszStringDest = NULL;
  484. DEBUG_STOP
  485. lResult = RegCreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost", &hKey);
  486. if( lResult != ERROR_SUCCESS )
  487. {
  488. hr = E_FAIL;
  489. goto Error;
  490. }
  491. lResult = RegQueryValueExW( hKey,
  492. SVCHOST_SUBKEYW, // subkey name
  493. NULL,
  494. &dwType,
  495. NULL, // string buffer
  496. &dwOrgSize ); // size of returned string
  497. if( lResult != ERROR_SUCCESS )
  498. {
  499. hr = E_FAIL;
  500. goto Error;
  501. }
  502. if (dwType != REG_SZ && dwType != REG_MULTI_SZ && dwType != REG_EXPAND_SZ)
  503. {
  504. hr = E_FAIL;
  505. goto Error;
  506. }
  507. dwDestSize = dwOrgSize + (wcslen( SERVICE_NAMEW ) +1)*sizeof(WCHAR);
  508. pwszStringOrg = (WCHAR*)new BYTE[dwOrgSize];
  509. pwszStringDest = (WCHAR*)new BYTE[dwDestSize];
  510. if( pwszStringOrg == NULL || pwszStringDest == NULL )
  511. {
  512. hr = E_OUTOFMEMORY;
  513. goto Error;
  514. }
  515. lResult = RegQueryValueExW( hKey,
  516. SVCHOST_SUBKEYW, // subkey name
  517. NULL,
  518. &dwType,
  519. (BYTE*)pwszStringOrg, // string buffer
  520. &dwOrgSize ); // size of returned string
  521. if( lResult != ERROR_SUCCESS )
  522. {
  523. hr = E_FAIL;
  524. goto Error;
  525. }
  526. if (dwType != REG_SZ && dwType != REG_MULTI_SZ && dwType != REG_EXPAND_SZ)
  527. {
  528. hr = E_FAIL;
  529. goto Error;
  530. }
  531. // Copy the org string to the dest, check to see if our string is already there
  532. memset( pwszStringDest, 0, dwDestSize );
  533. for( dwStrIndex = 0;
  534. (dwStrIndex*sizeof(WCHAR) < dwOrgSize) && ((pwszStringOrg)[dwStrIndex] != '\0');
  535. dwStrIndex += wcslen( &((WCHAR*)pwszStringOrg)[dwStrIndex] ) +1 )
  536. {
  537. // Check this string in the [array] of strings
  538. if( wcscmp( &((WCHAR*)pwszStringOrg)[dwStrIndex], SERVICE_NAMEW ) == 0 )
  539. {
  540. hr = S_OK; // String already added
  541. goto Error;
  542. }
  543. wcscpy( &pwszStringDest[dwStrIndex], &pwszStringOrg[dwStrIndex] );
  544. }
  545. // Add this new string to the array of strings. Terminate the array with two '\0' chars
  546. wcscpy( &pwszStringDest[dwStrIndex], SERVICE_NAMEW );
  547. dwStrIndex += wcslen( SERVICE_NAMEW ) + 1;
  548. dwDestSize = (dwStrIndex +1)* sizeof(WCHAR); // Add space for terminating extra '\0'
  549. lResult = RegSetValueExW(hKey,
  550. SVCHOST_SUBKEYW, // subkey name
  551. NULL,
  552. dwType,
  553. (BYTE*)pwszStringDest, // string buffer
  554. dwDestSize ); // size of returned string
  555. Error:
  556. if( pwszStringOrg ) delete [] pwszStringOrg;
  557. if( pwszStringDest ) delete [] pwszStringDest;
  558. if( hKey ) RegCloseKey(hKey);
  559. return hr;
  560. }
  561. // Stop and Uninstall the old .exe service
  562. BOOL UnregisterOldServer( SC_HANDLE hSCM )
  563. {
  564. char szKey[256];
  565. BOOL bRet = TRUE;
  566. SC_HANDLE hServiceOld;
  567. SERVICE_STATUS ss;
  568. if( !hSCM ) return FALSE;
  569. hServiceOld = OpenService( hSCM,
  570. SERVICE_OLD_NAME,
  571. SERVICE_ALL_ACCESS);
  572. // Could not find the old service
  573. if( !hServiceOld )
  574. {
  575. bRet = FALSE;
  576. goto Error;
  577. }
  578. // stop the service
  579. bRet = ControlService(hServiceOld,
  580. SERVICE_CONTROL_STOP,
  581. &ss);
  582. // Delete the service
  583. if ( !::DeleteService(hServiceOld))
  584. {
  585. bRet = FALSE;
  586. }
  587. // Delete old EventLog entry in registry
  588. strcpy(szKey, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
  589. strcat(szKey, SERVICE_OLD_NAME);
  590. RegDeleteKey( HKEY_LOCAL_MACHINE, szKey );
  591. Error:
  592. if(hServiceOld) CloseServiceHandle(hServiceOld);
  593. return bRet;
  594. }