Source code of Windows XP (NT5)
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.

726 lines
17 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. grovctrl.cpp
  5. Abstract:
  6. SIS Groveler controller main function
  7. Authors:
  8. John Douceur, 1998
  9. Environment:
  10. User Mode
  11. Revision History:
  12. --*/
  13. #include "all.hxx"
  14. static _TCHAR *service_name = _T("Groveler");
  15. static _TCHAR *service_path = _T("%SystemRoot%\\System32\\grovel.exe");
  16. static const int num_actions = 11;
  17. static Action actions[num_actions] =
  18. {
  19. {_T("background"), 1, command_service, CMD_background, _T(" [drive_letter ...]")},
  20. {_T("continue"), 1, control_service, CTRL_continue, _T("")},
  21. {_T("foreground"), 1, command_service, CMD_foreground, _T(" [drive_letter ...]")},
  22. {_T("install"), 3, install_service, 0, _T("")},
  23. {_T("interact"), 3, set_service_interaction, TRUE, _T("")},
  24. {_T("nointeract"), 1, set_service_interaction, FALSE, _T("")},
  25. {_T("pause"), 1, control_service, CTRL_pause, _T("")},
  26. {_T("remove"), 1, remove_service, 0, _T("")},
  27. {_T("start"), 3, start_service, 0, _T("")},
  28. {_T("stop"), 3, control_service, CTRL_stop, _T("")},
  29. {_T("volscan"), 1, command_service, CMD_volscan, _T(" [drive_letter ...]")}
  30. };
  31. static const int perf_value_count = 4;
  32. static _TCHAR *perf_tags[perf_value_count] =
  33. {
  34. _T("Library"),
  35. _T("Open"),
  36. _T("Collect"),
  37. _T("Close")
  38. };
  39. static _TCHAR *perf_values[perf_value_count] =
  40. {
  41. _T("grovperf.dll"),
  42. _T("OpenGrovelerPerformanceData"),
  43. _T("CollectGrovelerPerformanceData"),
  44. _T("CloseGrovelerPerformanceData")
  45. };
  46. void
  47. usage(
  48. _TCHAR *progname,
  49. _TCHAR *prefix = 0)
  50. {
  51. int prefixlen;
  52. if (prefix == 0)
  53. {
  54. prefixlen = 0;
  55. _ftprintf(stderr, _T("usage:\n"));
  56. }
  57. else
  58. {
  59. prefixlen = _tcslen(prefix);
  60. _ftprintf(stderr, _T("unrecognized or ambiguous command: %s\n"),
  61. prefix);
  62. }
  63. for (int index = 0; index < num_actions; index++)
  64. {
  65. _TCHAR *arg = actions[index].arg;
  66. int min_chars = actions[index].min_character_count;
  67. if (_tcsncicmp(prefix, arg, prefixlen) == 0)
  68. {
  69. _ftprintf(stderr, _T("\t%s %.*s[%s]%s\n"), progname, min_chars,
  70. arg, &arg[min_chars], actions[index].help);
  71. }
  72. }
  73. }
  74. void
  75. display_error(
  76. DWORD err = 0)
  77. {
  78. void *buffer = 0;
  79. if (err == 0)
  80. {
  81. err = GetLastError();
  82. }
  83. DWORD lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
  84. DWORD result = FormatMessage(
  85. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
  86. | FORMAT_MESSAGE_IGNORE_INSERTS, 0, err, lang, (LPTSTR) &buffer, 0, 0);
  87. if (result != 0)
  88. {
  89. ASSERT(buffer != 0);
  90. _ftprintf(stderr, (_TCHAR *)buffer);
  91. }
  92. else
  93. {
  94. _ftprintf(stderr, _T("error number = %d\n"), err);
  95. }
  96. if (buffer != 0)
  97. {
  98. LocalFree(buffer);
  99. }
  100. }
  101. extern "C" __cdecl _tmain(int argc, _TCHAR **argv)
  102. {
  103. if (argc < 2)
  104. {
  105. usage(argv[0]);
  106. return 1;
  107. }
  108. int arglen = _tcslen(argv[1]);
  109. for (int index = 0; index < num_actions; index++)
  110. {
  111. if (arglen >= actions[index].min_character_count &&
  112. _tcsncicmp(argv[1], actions[index].arg, arglen) == 0)
  113. {
  114. break;
  115. }
  116. }
  117. if (index < num_actions)
  118. {
  119. Function function = actions[index].function;
  120. int flag = actions[index].flag;
  121. ASSERT(function != 0);
  122. int exit_code = (*function)(flag, argc - 2, &argv[2]);
  123. return exit_code;
  124. }
  125. else
  126. {
  127. usage(argv[0], argv[1]);
  128. return 1;
  129. }
  130. }
  131. int
  132. install_service(
  133. int dummy,
  134. int argc,
  135. _TCHAR **argv)
  136. {
  137. SC_HANDLE sc_manager = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
  138. if (sc_manager == 0)
  139. {
  140. display_error();
  141. return 1;
  142. }
  143. SC_HANDLE service = CreateService(sc_manager, service_name,
  144. service_name, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
  145. SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, service_path,
  146. 0, 0, 0, 0, 0);
  147. if (service == 0)
  148. {
  149. display_error();
  150. CloseServiceHandle(sc_manager);
  151. return 1;
  152. }
  153. _ftprintf(stderr, _T("Service installed\n"));
  154. CloseServiceHandle(service);
  155. CloseServiceHandle(sc_manager);
  156. #if DBG
  157. load_counters();
  158. #endif // DBG
  159. return 0;
  160. }
  161. int remove_service(
  162. int dummy,
  163. int argc,
  164. _TCHAR **argv)
  165. {
  166. #if DBG
  167. unload_counters();
  168. #endif // DBG
  169. SC_HANDLE sc_manager = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
  170. if (sc_manager == 0)
  171. {
  172. display_error();
  173. return 1;
  174. }
  175. SC_HANDLE service =
  176. OpenService(sc_manager, service_name, SERVICE_ALL_ACCESS);
  177. if (service == 0)
  178. {
  179. display_error();
  180. CloseServiceHandle(sc_manager);
  181. return 1;
  182. }
  183. SERVICE_STATUS status;
  184. int ok = QueryServiceStatus(service, &status);
  185. if (ok && status.dwCurrentState != SERVICE_STOPPED)
  186. {
  187. ok = ControlService(service, SERVICE_CONTROL_STOP, &status);
  188. while (ok && status.dwCurrentState == SERVICE_STOP_PENDING)
  189. {
  190. Sleep(100);
  191. ok = QueryServiceStatus(service, &status);
  192. }
  193. if (!ok)
  194. {
  195. display_error();
  196. CloseServiceHandle(service);
  197. CloseServiceHandle(sc_manager);
  198. return 1;
  199. }
  200. else if (status.dwCurrentState != SERVICE_STOPPED)
  201. {
  202. _ftprintf(stderr,
  203. _T("Unable to stop service\nService not removed\n"));
  204. CloseServiceHandle(service);
  205. CloseServiceHandle(sc_manager);
  206. return 1;
  207. }
  208. }
  209. ok = DeleteService(service);
  210. if (!ok)
  211. {
  212. display_error();
  213. CloseServiceHandle(service);
  214. CloseServiceHandle(sc_manager);
  215. return 1;
  216. }
  217. _ftprintf(stderr, _T("Service removed\n"));
  218. CloseServiceHandle(service);
  219. CloseServiceHandle(sc_manager);
  220. return 0;
  221. }
  222. int
  223. set_service_interaction(
  224. int interactive,
  225. int argc,
  226. _TCHAR **argv)
  227. {
  228. SC_HANDLE sc_manager = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
  229. if (sc_manager == 0)
  230. {
  231. display_error();
  232. return 1;
  233. }
  234. SC_LOCK sc_lock = LockServiceDatabase(sc_manager);
  235. if (sc_lock == 0)
  236. {
  237. display_error();
  238. CloseServiceHandle(sc_manager);
  239. return 1;
  240. }
  241. SC_HANDLE service =
  242. OpenService(sc_manager, service_name, SERVICE_ALL_ACCESS);
  243. if (service == 0)
  244. {
  245. display_error();
  246. UnlockServiceDatabase(sc_lock);
  247. CloseServiceHandle(sc_manager);
  248. return 1;
  249. }
  250. DWORD service_type = SERVICE_WIN32_OWN_PROCESS;
  251. if (interactive)
  252. {
  253. service_type |= SERVICE_INTERACTIVE_PROCESS;
  254. }
  255. int ok = ChangeServiceConfig(service, service_type,
  256. SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0);
  257. if (!ok)
  258. {
  259. display_error();
  260. CloseServiceHandle(service);
  261. CloseServiceHandle(sc_manager);
  262. return 1;
  263. }
  264. if (interactive)
  265. {
  266. _ftprintf(stderr, _T("Service configured for interaction\n"));
  267. }
  268. else
  269. {
  270. _ftprintf(stderr, _T("Service configured for no interaction\n"));
  271. }
  272. CloseServiceHandle(service);
  273. UnlockServiceDatabase(sc_lock);
  274. CloseServiceHandle(sc_manager);
  275. return 0;
  276. }
  277. int start_service(
  278. int dummy,
  279. int argc,
  280. _TCHAR **argv)
  281. {
  282. SC_HANDLE sc_manager = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
  283. if (sc_manager == 0)
  284. {
  285. display_error();
  286. return 1;
  287. }
  288. SC_HANDLE service =
  289. OpenService(sc_manager, service_name, SERVICE_ALL_ACCESS);
  290. if (service == 0)
  291. {
  292. display_error();
  293. CloseServiceHandle(sc_manager);
  294. return 1;
  295. }
  296. SERVICE_STATUS status;
  297. int ok = StartService(service, 0, 0);
  298. if (!ok)
  299. {
  300. display_error();
  301. CloseServiceHandle(service);
  302. CloseServiceHandle(sc_manager);
  303. return 1;
  304. }
  305. ok = QueryServiceStatus(service, &status);
  306. while (ok && status.dwCurrentState == SERVICE_START_PENDING)
  307. {
  308. Sleep(100);
  309. ok = QueryServiceStatus(service, &status);
  310. }
  311. if (!ok)
  312. {
  313. display_error();
  314. CloseServiceHandle(service);
  315. CloseServiceHandle(sc_manager);
  316. return 1;
  317. }
  318. else if (status.dwCurrentState != SERVICE_RUNNING)
  319. {
  320. _ftprintf(stderr, _T("Service not started"));
  321. CloseServiceHandle(service);
  322. CloseServiceHandle(sc_manager);
  323. return 1;
  324. }
  325. _ftprintf(stderr, _T("Service started\n"));
  326. CloseServiceHandle(service);
  327. CloseServiceHandle(sc_manager);
  328. return 0;
  329. }
  330. int control_service(
  331. int control,
  332. int argc,
  333. _TCHAR **argv)
  334. {
  335. DWORD control_code;
  336. DWORD pending_state;
  337. DWORD target_state;
  338. _TCHAR *good_message;
  339. _TCHAR *bad_message;
  340. switch (control)
  341. {
  342. case CTRL_stop:
  343. control_code = SERVICE_CONTROL_STOP;
  344. pending_state = SERVICE_STOP_PENDING;
  345. target_state = SERVICE_STOPPED;
  346. good_message = _T("Service stopped\n");
  347. bad_message = _T("Service not stopped\n");
  348. break;
  349. case CTRL_pause:
  350. control_code = SERVICE_CONTROL_PAUSE;
  351. pending_state = SERVICE_PAUSE_PENDING;
  352. target_state = SERVICE_PAUSED;
  353. good_message = _T("Service paused\n");
  354. bad_message = _T("Service not paused\n");
  355. break;
  356. case CTRL_continue:
  357. control_code = SERVICE_CONTROL_CONTINUE;
  358. pending_state = SERVICE_CONTINUE_PENDING;
  359. target_state = SERVICE_RUNNING;
  360. good_message = _T("Service continued\n");
  361. bad_message = _T("Service not continued\n");
  362. break;
  363. default:
  364. return 1;
  365. }
  366. SC_HANDLE sc_manager = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
  367. if (sc_manager == 0)
  368. {
  369. display_error();
  370. return 1;
  371. }
  372. SC_HANDLE service =
  373. OpenService(sc_manager, service_name, SERVICE_ALL_ACCESS);
  374. if (service == 0)
  375. {
  376. display_error();
  377. CloseServiceHandle(sc_manager);
  378. return 1;
  379. }
  380. SERVICE_STATUS status;
  381. int ok = ControlService(service, control_code, &status);
  382. while (ok && status.dwCurrentState == pending_state)
  383. {
  384. Sleep(100);
  385. ok = QueryServiceStatus(service, &status);
  386. }
  387. if (!ok)
  388. {
  389. display_error();
  390. CloseServiceHandle(service);
  391. CloseServiceHandle(sc_manager);
  392. return 1;
  393. }
  394. else if (status.dwCurrentState != target_state)
  395. {
  396. _ftprintf(stderr, bad_message);
  397. CloseServiceHandle(service);
  398. CloseServiceHandle(sc_manager);
  399. return 1;
  400. }
  401. _ftprintf(stderr, good_message);
  402. CloseServiceHandle(service);
  403. CloseServiceHandle(sc_manager);
  404. return 0;
  405. }
  406. int command_service(
  407. int command,
  408. int argc,
  409. _TCHAR **argv)
  410. {
  411. DWORD control_code;
  412. _TCHAR *message;
  413. switch (command)
  414. {
  415. case CMD_foreground:
  416. control_code = SERVICE_CONTROL_FOREGROUND;
  417. message = _T("Service mode set to foreground");
  418. break;
  419. case CMD_background:
  420. control_code = SERVICE_CONTROL_BACKGROUND;
  421. message = _T("Service mode set to background");
  422. break;
  423. case CMD_volscan:
  424. control_code = SERVICE_CONTROL_VOLSCAN;
  425. message = _T("Volume scan initiated");
  426. break;
  427. default:
  428. return 1;
  429. }
  430. SC_HANDLE sc_manager = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
  431. if (sc_manager == 0)
  432. {
  433. display_error();
  434. return 1;
  435. }
  436. SC_HANDLE service =
  437. OpenService(sc_manager, service_name, SERVICE_ALL_ACCESS);
  438. if (service == 0)
  439. {
  440. display_error();
  441. CloseServiceHandle(sc_manager);
  442. return 1;
  443. }
  444. int exit_code = 0;
  445. SERVICE_STATUS status;
  446. if (argc > 0)
  447. {
  448. for (int index = 0; index < argc; index++)
  449. {
  450. _TCHAR drive_letter = argv[index][0];
  451. ASSERT(drive_letter != 0);
  452. if (drive_letter >= _T('a') && drive_letter <= _T('z')
  453. || drive_letter >= _T('A') && drive_letter <= _T('Z'))
  454. {
  455. DWORD drive_spec = SERVICE_CONTROL_PARTITION_MASK &
  456. (DWORD)(_totlower(drive_letter) - _T('a'));
  457. int ok = ControlService(service,
  458. control_code | drive_spec, &status);
  459. if (ok)
  460. {
  461. _ftprintf(stderr, _T("%s on drive %c\n"),
  462. message, drive_letter);
  463. }
  464. else
  465. {
  466. display_error();
  467. exit_code++;
  468. }
  469. }
  470. else
  471. {
  472. _ftprintf(stderr, _T("Invalid drive letter: %c\n"),
  473. drive_letter);
  474. exit_code++;
  475. }
  476. }
  477. }
  478. else
  479. {
  480. int ok = ControlService(service,
  481. control_code | SERVICE_CONTROL_ALL_PARTITIONS, &status);
  482. if (ok)
  483. {
  484. _ftprintf(stderr, _T("%s on all drives\n"), message);
  485. }
  486. else
  487. {
  488. display_error();
  489. exit_code++;
  490. }
  491. }
  492. CloseServiceHandle(service);
  493. CloseServiceHandle(sc_manager);
  494. return 0;
  495. }
  496. int load_counters()
  497. {
  498. HKEY grovperf_key = 0;
  499. HKEY perflib_key = 0;
  500. _TCHAR grovperf_path[1024];
  501. _stprintf(grovperf_path,
  502. _T("SYSTEM\\CurrentControlSet\\Services\\%s\\Performance"),
  503. service_name);
  504. bool ok = Registry::write_string_set(HKEY_LOCAL_MACHINE, grovperf_path,
  505. perf_value_count, perf_values, perf_tags);
  506. if (!ok)
  507. {
  508. display_error();
  509. _ftprintf(stderr, _T("Unable to configure performance counters\n"));
  510. return 1;
  511. }
  512. _ftprintf(stderr, _T("Adding counter names and explain text for %s\n"),
  513. service_name);
  514. try
  515. {
  516. Registry::open_key_ex(HKEY_LOCAL_MACHINE, grovperf_path, 0,
  517. KEY_ALL_ACCESS, &grovperf_key);
  518. Registry::open_key_ex(HKEY_LOCAL_MACHINE,
  519. _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib"),
  520. 0, KEY_ALL_ACCESS, &perflib_key);
  521. DWORD last_counter = 0;
  522. DWORD ctr_size = sizeof(DWORD);
  523. Registry::query_value_ex(perflib_key, _T("Last Counter"), 0, 0,
  524. (BYTE *)&last_counter, &ctr_size);
  525. DWORD last_help = 0;
  526. ctr_size = sizeof(DWORD);
  527. Registry::query_value_ex(perflib_key, _T("Last Help"), 0, 0,
  528. (BYTE *)&last_help, &ctr_size);
  529. DWORD current_counter;
  530. DWORD current_help;
  531. for (int language = 0; language < num_languages; language++)
  532. {
  533. HKEY lang_key = 0;
  534. BYTE *counter_text = 0;
  535. BYTE *help_text = 0;
  536. try
  537. {
  538. Registry::open_key_ex(perflib_key, language_codes[language], 0,
  539. KEY_ALL_ACCESS, &lang_key);
  540. DWORD ctr_size;
  541. Registry::query_value_ex(lang_key, _T("Counter"), 0, 0, 0,
  542. &ctr_size);
  543. counter_text =
  544. new BYTE[ctr_size + num_perf_counters * 64];
  545. Registry::query_value_ex(lang_key, _T("Counter"), 0, 0,
  546. counter_text, &ctr_size);
  547. DWORD help_size;
  548. Registry::query_value_ex(lang_key, _T("Help"), 0, 0, 0,
  549. &help_size);
  550. help_text = new BYTE[help_size + num_perf_counters * 256];
  551. Registry::query_value_ex(lang_key, _T("Help"), 0, 0,
  552. help_text, &help_size);
  553. current_counter = last_counter;
  554. current_help = last_help;
  555. _TCHAR *counter_point =
  556. (_TCHAR *)(counter_text + ctr_size - sizeof(_TCHAR));
  557. _TCHAR *help_point =
  558. (_TCHAR *)(help_text + help_size - sizeof(_TCHAR));
  559. current_counter += 2;
  560. int chars_written =
  561. _stprintf(counter_point, _T("%d%c%s%c"), current_counter,
  562. 0, object_info.text[language].counter_name, 0);
  563. counter_point += chars_written;
  564. ctr_size += chars_written * sizeof(_TCHAR);
  565. current_help += 2;
  566. chars_written =
  567. _stprintf(help_point, _T("%d%c%s%c"), current_help, 0,
  568. object_info.text[language].counter_help, 0);
  569. help_point += chars_written;
  570. help_size += chars_written * sizeof(_TCHAR);
  571. for (int index = 0; index < num_perf_counters; index++)
  572. {
  573. current_counter += 2;
  574. chars_written = _stprintf(counter_point, _T("%d%c%s%c"),
  575. current_counter, 0,
  576. counter_info[index].text[language].counter_name, 0);
  577. counter_point += chars_written;
  578. ctr_size += chars_written * sizeof(_TCHAR);
  579. current_help += 2;
  580. chars_written = _stprintf(help_point, _T("%d%c%s%c"),
  581. current_help, 0,
  582. counter_info[index].text[language].counter_help, 0);
  583. help_point += chars_written;
  584. help_size += chars_written * sizeof(_TCHAR);
  585. }
  586. Registry::set_value_ex(lang_key, _T("Counter"), 0,
  587. REG_MULTI_SZ, counter_text, ctr_size);
  588. Registry::set_value_ex(lang_key, _T("Help"), 0,
  589. REG_MULTI_SZ, help_text, help_size);
  590. delete[] counter_text;
  591. delete[] help_text;
  592. RegCloseKey(lang_key);
  593. lang_key = 0;
  594. _ftprintf(stderr, _T("Updating text for language %s\n"),
  595. language_codes[language]);
  596. }
  597. catch (DWORD)
  598. {
  599. if (counter_text != 0)
  600. {
  601. delete[] counter_text;
  602. counter_text = 0;
  603. }
  604. if (help_text != 0)
  605. {
  606. delete[] help_text;
  607. help_text = 0;
  608. }
  609. if (lang_key != 0)
  610. {
  611. RegCloseKey(lang_key);
  612. lang_key = 0;
  613. }
  614. }
  615. }
  616. Registry::set_value_ex(perflib_key, _T("Last Counter"), 0, REG_DWORD,
  617. (BYTE *)&current_counter, sizeof(DWORD));
  618. Registry::set_value_ex(perflib_key, _T("Last Help"), 0, REG_DWORD,
  619. (BYTE *)&current_help, sizeof(DWORD));
  620. DWORD first_counter = last_counter + 2;
  621. DWORD first_help = last_help + 2;
  622. Registry::set_value_ex(grovperf_key, _T("First Counter"), 0, REG_DWORD,
  623. (BYTE *)&first_counter, sizeof(DWORD));
  624. Registry::set_value_ex(grovperf_key, _T("First Help"), 0, REG_DWORD,
  625. (BYTE *)&first_help, sizeof(DWORD));
  626. Registry::set_value_ex(grovperf_key, _T("Last Counter"), 0, REG_DWORD,
  627. (BYTE *)&current_counter, sizeof(DWORD));
  628. Registry::set_value_ex(grovperf_key, _T("Last Help"), 0, REG_DWORD,
  629. (BYTE *)&current_help, sizeof(DWORD));
  630. Registry::close_key(grovperf_key);
  631. grovperf_key = 0;
  632. Registry::close_key(perflib_key);
  633. perflib_key = 0;
  634. }
  635. catch (DWORD)
  636. {
  637. if (grovperf_key != 0)
  638. {
  639. RegCloseKey(grovperf_key);
  640. grovperf_key = 0;
  641. }
  642. if (perflib_key != 0)
  643. {
  644. RegCloseKey(perflib_key);
  645. perflib_key = 0;
  646. }
  647. _ftprintf(stderr, _T("Unable to configure performance counters\n"));
  648. return 1;
  649. }
  650. return 0;
  651. }
  652. int unload_counters()
  653. {
  654. STARTUPINFO startupinfo;
  655. PROCESS_INFORMATION process_information;
  656. startupinfo.cb = sizeof(STARTUPINFO);
  657. startupinfo.lpReserved = 0;
  658. startupinfo.lpDesktop = 0;
  659. startupinfo.lpTitle = 0;
  660. startupinfo.dwFlags = 0;
  661. startupinfo.cbReserved2 = 0;
  662. startupinfo.lpReserved2 = 0;
  663. int pathlen = GetSystemDirectory(0, 0);
  664. if (pathlen == 0)
  665. {
  666. display_error();
  667. return 1;
  668. }
  669. _TCHAR *command_line = new _TCHAR[pathlen + 64];
  670. pathlen = GetSystemDirectory(command_line, pathlen);
  671. if (pathlen == 0)
  672. {
  673. delete[] command_line;
  674. display_error();
  675. return 1;
  676. }
  677. _stprintf(&command_line[pathlen], _T("\\unlodctr.exe \"%s\""),
  678. service_name);
  679. BOOL ok = CreateProcess(0, command_line,
  680. 0, 0, FALSE, 0, 0, 0, &startupinfo, &process_information);
  681. if (!ok)
  682. {
  683. delete[] command_line;
  684. display_error();
  685. return 1;
  686. }
  687. delete[] command_line;
  688. DWORD result = WaitForSingleObject(process_information.hProcess, 5000);
  689. _tprintf(_T("\n"));
  690. if (result != WAIT_OBJECT_0)
  691. {
  692. return 1;
  693. }
  694. return 0;
  695. }