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.

587 lines
17 KiB

  1. // svcprop1.cpp : implementation file
  2. //
  3. // Implementation of page "General" of service property.
  4. //
  5. // HISTORY
  6. // 30-Sep-96 t-danmo Creation
  7. //
  8. #include "stdafx.h"
  9. #include "progress.h"
  10. #include "cookie.h"
  11. #include "dataobj.h"
  12. #include "DynamLnk.h" // DynamicDLL
  13. #ifdef _DEBUG
  14. #define new DEBUG_NEW
  15. #undef THIS_FILE
  16. static char THIS_FILE[] = __FILE__;
  17. #endif
  18. /*
  19. /////////////////////////////////////////////////////////////////////
  20. // WM_COMPARE_IDATAOBJECT
  21. //
  22. // wParam = (WPARAM)(IDataObject *)pDataObject;
  23. // lParam = 0;
  24. //
  25. // Return TRUE if content of pDataObject matches current data object,
  26. // otherwise return FALSE.
  27. //
  28. // USAGE
  29. // This message is sent to a property page asking to
  30. // compare content pDataObject with its current data object.
  31. // The comparison is done by comparing data strings from
  32. // the various clipboard formats supported by pDataObject.
  33. //
  34. #define WM_COMPARE_IDATAOBJECT (WM_USER+1234)
  35. */
  36. /////////////////////////////////////////////////////////////////////
  37. // WM_UPDATE_SERVICE_STATUS
  38. //
  39. // wParam = (WPARAM)(BOOL *)rgfEnableButton;
  40. // lParam = (LPARAM)dwCurrentState;
  41. //
  42. // Notification message of the current service status.
  43. //
  44. #define WM_UPDATE_SERVICE_STATUS (WM_USER+1235)
  45. /////////////////////////////////////////////////////////////////////
  46. static const TStringParamEntry rgzspeStartupType[] =
  47. {
  48. { IDS_SVC_STARTUP_AUTOMATIC, SERVICE_AUTO_START },
  49. { IDS_SVC_STARTUP_MANUAL, SERVICE_DEMAND_START },
  50. { IDS_SVC_STARTUP_DISABLED, SERVICE_DISABLED },
  51. { 0, 0 }
  52. };
  53. #ifdef EDIT_DISPLAY_NAME_373025
  54. const UINT rgzidDisableServiceDescription[] =
  55. {
  56. IDC_STATIC_DESCRIPTION,
  57. IDC_EDIT_DESCRIPTION,
  58. 0,
  59. };
  60. #endif // EDIT_DISPLAY_NAME_373025
  61. const UINT rgzidDisableStartupParameters[] =
  62. {
  63. IDC_STATIC_STARTUP_PARAMETERS,
  64. IDC_EDIT_STARTUP_PARAMETERS,
  65. 0,
  66. };
  67. /////////////////////////////////////////////////////////////////////
  68. // CServicePageGeneral property page
  69. IMPLEMENT_DYNCREATE(CServicePageGeneral, CPropertyPage)
  70. CServicePageGeneral::CServicePageGeneral() : CPropertyPage(CServicePageGeneral::IDD)
  71. {
  72. //{{AFX_DATA_INIT(CServicePageGeneral)
  73. //}}AFX_DATA_INIT
  74. m_pData = NULL;
  75. m_hThread = NULL;
  76. m_pThreadProcInit = NULL;
  77. }
  78. CServicePageGeneral::~CServicePageGeneral()
  79. {
  80. }
  81. void CServicePageGeneral::DoDataExchange(CDataExchange* pDX)
  82. {
  83. Assert(m_pData != NULL);
  84. HWND hwndCombo = HGetDlgItem(m_hWnd, IDC_COMBO_STARTUP_TYPE);
  85. if (!pDX->m_bSaveAndValidate)
  86. {
  87. //
  88. // Initialize data from m_pData into UI
  89. //
  90. ComboBox_FlushContent(hwndCombo);
  91. (void)ComboBox_FFill(hwndCombo, IN rgzspeStartupType,
  92. m_pData->m_paQSC->dwStartType);
  93. //
  94. // JonN 4/10/00
  95. // 89823: RPC Service:Cannot restart the service when you disable it
  96. //
  97. // Do not allow the RpcSs service to change from Automatic
  98. //
  99. if ( !lstrcmpi(m_pData->m_strServiceName,L"RpcSs")
  100. && SERVICE_AUTO_START == m_pData->m_paQSC->dwStartType )
  101. {
  102. EnableDlgItem(IDC_COMBO_STARTUP_TYPE, FALSE);
  103. }
  104. #ifndef EDIT_DISPLAY_NAME_373025
  105. DDX_Text(pDX, IDC_EDIT_DISPLAY_NAME, m_pData->m_strServiceDisplayName);
  106. DDX_Text(pDX, IDC_EDIT_DESCRIPTION, m_pData->m_strDescription);
  107. #endif // EDIT_DISPLAY_NAME_373025
  108. } // if
  109. CPropertyPage::DoDataExchange(pDX);
  110. //{{AFX_DATA_MAP(CServicePageGeneral)
  111. //}}AFX_DATA_MAP
  112. #ifdef EDIT_DISPLAY_NAME_373025
  113. DDX_Text(pDX, IDC_EDIT_DISPLAY_NAME, m_pData->m_strServiceDisplayName);
  114. DDV_MaxChars(pDX, m_pData->m_strServiceDisplayName, 255);
  115. DDX_Text(pDX, IDC_EDIT_DESCRIPTION, m_pData->m_strDescription);
  116. DDV_MaxChars(pDX, m_pData->m_strDescription, 2047);
  117. #endif // EDIT_DISPLAY_NAME_373025
  118. if (pDX->m_bSaveAndValidate)
  119. {
  120. //
  121. // Write data from UI into m_pData
  122. //
  123. #ifdef EDIT_DISPLAY_NAME_373025
  124. if (m_pData->m_strServiceDisplayName.IsEmpty())
  125. {
  126. DoServicesErrMsgBox(m_hWnd, MB_OK | MB_ICONEXCLAMATION, 0, IDS_MSG_PLEASE_ENTER_DISPLAY_NAME);
  127. pDX->PrepareEditCtrl(IDC_EDIT_DISPLAY_NAME);
  128. pDX->Fail();
  129. }
  130. #endif // EDIT_DISPLAY_NAME_373025
  131. m_pData->m_paQSC->dwStartType = (DWORD)ComboBox_GetSelectedItemData(hwndCombo);
  132. } // if
  133. } // CServicePageGeneral::DoDataExchange()
  134. BEGIN_MESSAGE_MAP(CServicePageGeneral, CPropertyPage)
  135. //{{AFX_MSG_MAP(CServicePageGeneral)
  136. #ifdef EDIT_DISPLAY_NAME_373025
  137. ON_EN_CHANGE(IDC_EDIT_DISPLAY_NAME, OnChangeEditDisplayName)
  138. ON_EN_CHANGE(IDC_EDIT_DESCRIPTION, OnChangeEditDescription)
  139. #endif // EDIT_DISPLAY_NAME_373025
  140. ON_CBN_SELCHANGE(IDC_COMBO_STARTUP_TYPE, OnSelchangeComboStartupType)
  141. ON_BN_CLICKED(IDC_BUTTON_PAUSE, OnButtonPauseService)
  142. ON_BN_CLICKED(IDC_BUTTON_START, OnButtonStartService)
  143. ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStopService)
  144. ON_BN_CLICKED(IDC_BUTTON_RESUME, OnButtonResumeService)
  145. ON_WM_DESTROY()
  146. ON_MESSAGE(WM_HELP, OnHelp)
  147. ON_MESSAGE(WM_CONTEXTMENU, OnContextHelp)
  148. // ON_MESSAGE(WM_COMPARE_IDATAOBJECT, OnCompareIDataObject)
  149. ON_MESSAGE(WM_UPDATE_SERVICE_STATUS, OnUpdateServiceStatus)
  150. //}}AFX_MSG_MAP
  151. END_MESSAGE_MAP()
  152. /////////////////////////////////////////////////////////////////////////////
  153. // CServicePageGeneral message handlers
  154. BOOL CServicePageGeneral::OnInitDialog()
  155. {
  156. CPropertyPage::OnInitDialog();
  157. Assert(m_pData != NULL);
  158. Assert(m_pData->m_paQSC != NULL);
  159. m_pData->m_hwndPropertySheet = ::GetParent(m_hWnd);
  160. m_pData->UpdateCaption();
  161. SetDlgItemText(IDC_STATIC_SERVICE_NAME, m_pData->m_pszServiceName);
  162. SetDlgItemText(IDC_STATIC_PATH_TO_EXECUTABLE, m_pData->m_paQSC->lpBinaryPathName);
  163. #ifdef EDIT_DISPLAY_NAME_373025
  164. EnableDlgItemGroup(m_hWnd, rgzidDisableServiceDescription, m_pData->m_fQueryServiceConfig2);
  165. #endif // EDIT_DISPLAY_NAME_373025
  166. RefreshServiceStatusButtons();
  167. // Create a thread for periodic update
  168. m_pThreadProcInit = new CThreadProcInit(this); // Note the object will be freed by the thread
  169. m_pThreadProcInit->m_strServiceName = m_pData->m_strServiceName;
  170. Assert(m_hThread == NULL);
  171. m_hThread = ::CreateThread(
  172. NULL,
  173. 0,
  174. (LPTHREAD_START_ROUTINE)ThreadProcPeriodicServiceStatusUpdate,
  175. m_pThreadProcInit,
  176. 0,
  177. NULL);
  178. Report(m_hThread != NULL && "Unable to create thread");
  179. return TRUE;
  180. }
  181. /////////////////////////////////////////////////////////////////////
  182. void CServicePageGeneral::OnDestroy()
  183. {
  184. {
  185. CSingleLock lock(&m_pThreadProcInit->m_CriticalSection, TRUE);
  186. m_pThreadProcInit->m_hwnd = NULL;
  187. m_pThreadProcInit->m_fAutoDestroy = TRUE;
  188. }
  189. if (NULL != m_hThread) {
  190. VERIFY(::CloseHandle(m_hThread));
  191. m_hThread = NULL;
  192. }
  193. CPropertyPage::OnDestroy();
  194. delete m_pData;
  195. }
  196. /////////////////////////////////////////////////////////////////////
  197. BOOL CServicePageGeneral::OnSetActive()
  198. {
  199. Assert(m_pData != NULL);
  200. if (m_pData->m_hScManager == NULL)
  201. {
  202. AFX_MANAGE_STATE(AfxGetStaticModuleState( )); // required for CWaitCursor
  203. CWaitCursor wait;
  204. (void)m_pData->FOpenScManager(); // Re-open the service control manager database (if previously closed)
  205. }
  206. {
  207. CSingleLock lock(&m_pThreadProcInit->m_CriticalSection, TRUE);
  208. m_pThreadProcInit->m_hScManager = m_pData->m_hScManager;
  209. m_pThreadProcInit->m_hwnd = m_hWnd;
  210. }
  211. return CPropertyPage::OnSetActive();
  212. }
  213. /////////////////////////////////////////////////////////////////////
  214. BOOL CServicePageGeneral::OnKillActive()
  215. {
  216. if (!CPropertyPage::OnKillActive())
  217. return FALSE;
  218. {
  219. CSingleLock lock(&m_pThreadProcInit->m_CriticalSection, TRUE);
  220. m_pThreadProcInit->m_hwnd = NULL;
  221. }
  222. return TRUE;
  223. }
  224. #ifdef EDIT_DISPLAY_NAME_373025
  225. void CServicePageGeneral::OnChangeEditDisplayName()
  226. {
  227. m_pData->SetDirty(CServicePropertyData::mskfDirtyDisplayName);
  228. SetModified();
  229. }
  230. void CServicePageGeneral::OnChangeEditDescription()
  231. {
  232. m_pData->SetDirty(CServicePropertyData::mskfDirtyDescription);
  233. SetModified();
  234. }
  235. #endif // EDIT_DISPLAY_NAME_373025
  236. void CServicePageGeneral::OnSelchangeComboStartupType()
  237. {
  238. m_pData->SetDirty(CServicePropertyData::mskfDirtyStartupType);
  239. SetModified();
  240. }
  241. void CServicePageGeneral::SetDlgItemFocus(INT nIdDlgItem)
  242. {
  243. ::SetDlgItemFocus(m_hWnd, nIdDlgItem);
  244. }
  245. void CServicePageGeneral::EnableDlgItem(INT nIdDlgItem, BOOL fEnable)
  246. {
  247. ::EnableDlgItem(m_hWnd, nIdDlgItem, fEnable);
  248. }
  249. /////////////////////////////////////////////////////////////////////
  250. // RefreshServiceStatusButtons()
  251. //
  252. // Query the service manager to get the status of the service, and
  253. // enable/disable buttons Start, Stop, Pause and Continue accordingly.
  254. //
  255. void CServicePageGeneral::RefreshServiceStatusButtons()
  256. {
  257. BOOL rgfEnableButton[iServiceActionMax];
  258. DWORD dwCurrentState;
  259. CWaitCursor wait;
  260. if (!Service_FGetServiceButtonStatus(
  261. m_pData->m_hScManager,
  262. m_pData->m_pszServiceName,
  263. OUT rgfEnableButton,
  264. OUT &dwCurrentState))
  265. {
  266. // let's not do this m_pData->m_hScManager = NULL;
  267. }
  268. m_dwCurrentStatePrev = !dwCurrentState; // Force a refresh
  269. OnUpdateServiceStatus((WPARAM)rgfEnableButton, dwCurrentState);
  270. } // CServicePageGeneral::RefreshServiceStatusButtons()
  271. typedef enum _SVCPROP_Shell32ApiIndex
  272. {
  273. CMDLINE_ENUM = 0
  274. };
  275. // not subject to localization
  276. static LPCSTR g_apchShell32FunctionNames[] = {
  277. "CommandLineToArgvW",
  278. NULL
  279. };
  280. typedef LPWSTR * (*COMMANDLINETOARGVWPROC)(LPCWSTR, int*);
  281. // not subject to localization
  282. DynamicDLL g_SvcpropShell32DLL( _T("SHELL32.DLL"), g_apchShell32FunctionNames );
  283. /////////////////////////////////////////////////////////////////////
  284. void CServicePageGeneral::OnButtonStartService()
  285. {
  286. CString strStartupParameters;
  287. LPCWSTR * lpServiceArgVectors = NULL; // Array of pointers to strings
  288. int cArgs = 0; // Count of arguments
  289. // Get the startup parameters
  290. GetDlgItemText(IDC_EDIT_STARTUP_PARAMETERS, OUT strStartupParameters);
  291. if ( !strStartupParameters.IsEmpty() )
  292. {
  293. #ifndef UNICODE
  294. #error CODEWORK t-danmo: CommandLineToArgvW will only work for unicode strings
  295. #endif
  296. if ( !g_SvcpropShell32DLL.LoadFunctionPointers() )
  297. {
  298. ASSERT(FALSE);
  299. return;
  300. }
  301. lpServiceArgVectors = (LPCWSTR *)((COMMANDLINETOARGVWPROC)g_SvcpropShell32DLL[CMDLINE_ENUM])
  302. (strStartupParameters, OUT &cArgs);
  303. if (lpServiceArgVectors == NULL)
  304. {
  305. DoServicesErrMsgBox(m_hWnd, MB_OK | MB_ICONEXCLAMATION, 0, IDS_MSG_INVALID_STARTUP_PARAMETERS);
  306. SetDlgItemFocus(IDC_EDIT_STARTUP_PARAMETERS);
  307. return;
  308. }
  309. }
  310. // Disable the edit control for better UI
  311. EnableDlgItemGroup(m_hWnd, rgzidDisableStartupParameters, FALSE);
  312. DWORD dwErr = CServiceControlProgress::S_EStartService(
  313. m_hWnd,
  314. m_pData->m_hScManager,
  315. m_pData->m_strUiMachineName,
  316. m_pData->m_pszServiceName,
  317. m_pData->m_strServiceDisplayName,
  318. cArgs,
  319. lpServiceArgVectors);
  320. if (NULL != lpServiceArgVectors)
  321. LocalFree(lpServiceArgVectors);
  322. if (dwErr == CServiceControlProgress::errUserAbort)
  323. return;
  324. RefreshServiceStatusButtons();
  325. SetDlgItemFocus(IDC_BUTTON_STOP);
  326. m_pData->NotifySnapInParent();
  327. }
  328. /////////////////////////////////////////////////////////////////////
  329. void CServicePageGeneral::OnButtonStopService()
  330. {
  331. DWORD dwErr = CServiceControlProgress::S_EControlService(
  332. m_hWnd,
  333. m_pData->m_hScManager,
  334. m_pData->m_strUiMachineName,
  335. m_pData->m_pszServiceName,
  336. m_pData->m_strServiceDisplayName,
  337. SERVICE_CONTROL_STOP);
  338. if (dwErr == CServiceControlProgress::errUserAbort)
  339. return;
  340. RefreshServiceStatusButtons();
  341. SetDlgItemFocus(IDC_BUTTON_START);
  342. m_pData->NotifySnapInParent();
  343. }
  344. /////////////////////////////////////////////////////////////////////
  345. void CServicePageGeneral::OnButtonPauseService()
  346. {
  347. DWORD dwErr = CServiceControlProgress::S_EControlService(
  348. m_hWnd,
  349. m_pData->m_hScManager,
  350. m_pData->m_strUiMachineName,
  351. m_pData->m_pszServiceName,
  352. m_pData->m_strServiceDisplayName,
  353. SERVICE_CONTROL_PAUSE);
  354. if (dwErr == CServiceControlProgress::errUserAbort)
  355. return;
  356. RefreshServiceStatusButtons();
  357. SetDlgItemFocus(IDC_BUTTON_RESUME);
  358. m_pData->NotifySnapInParent();
  359. }
  360. /////////////////////////////////////////////////////////////////////
  361. void CServicePageGeneral::OnButtonResumeService()
  362. {
  363. DWORD dwErr = CServiceControlProgress::S_EControlService(
  364. m_hWnd,
  365. m_pData->m_hScManager,
  366. m_pData->m_strUiMachineName,
  367. m_pData->m_pszServiceName,
  368. m_pData->m_strServiceDisplayName,
  369. SERVICE_CONTROL_CONTINUE);
  370. if (dwErr == CServiceControlProgress::errUserAbort)
  371. return;
  372. RefreshServiceStatusButtons();
  373. SetDlgItemFocus(IDC_BUTTON_PAUSE);
  374. m_pData->NotifySnapInParent();
  375. }
  376. /////////////////////////////////////////////////////////////////////
  377. BOOL CServicePageGeneral::OnApply()
  378. {
  379. // Write the data into the service control database
  380. if (!m_pData->FOnApply())
  381. {
  382. // Unable to write the information
  383. return FALSE;
  384. }
  385. UpdateData(FALSE);
  386. RefreshServiceStatusButtons();
  387. m_pData->UpdateCaption();
  388. return CPropertyPage::OnApply();
  389. }
  390. /*
  391. /////////////////////////////////////////////////////////////////////
  392. // OnCompareIDataObject()
  393. //
  394. // Return TRUE if 'service name' and 'machine name' of pDataObject
  395. // matches 'service name' and 'machine name' of current property sheet.
  396. //
  397. LRESULT CServicePageGeneral::OnCompareIDataObject(WPARAM wParam, LPARAM lParam)
  398. {
  399. IDataObject * pDataObject;
  400. CString strServiceName;
  401. CString strMachineName;
  402. HRESULT hr;
  403. pDataObject = reinterpret_cast<IDataObject *>(wParam);
  404. Assert(pDataObject != NULL);
  405. // Get the service name from IDataObject
  406. hr = ::ExtractString(
  407. pDataObject,
  408. CFileMgmtDataObject::m_CFServiceName,
  409. OUT &strServiceName,
  410. 255);
  411. if (FAILED(hr))
  412. return FALSE;
  413. if (0 != lstrcmpi(strServiceName, m_pData->m_strServiceName))
  414. {
  415. // Service name do not match
  416. return FALSE;
  417. }
  418. // Get the machine name (computer name) from IDataObject
  419. hr = ::ExtractString(
  420. pDataObject,
  421. CFileMgmtDataObject::m_CFMachineName,
  422. OUT &strMachineName,
  423. 255);
  424. if (FAILED(hr))
  425. return FALSE;
  426. return FCompareMachineNames(m_pData->m_strMachineName, strMachineName);
  427. } // CServicePageGeneral::OnCompareIDataObject()
  428. */
  429. /////////////////////////////////////////////////////////////////////
  430. LRESULT CServicePageGeneral::OnUpdateServiceStatus(WPARAM wParam, LPARAM lParam)
  431. {
  432. const BOOL * rgfEnableButton = (BOOL *)wParam;
  433. const DWORD dwCurrentState = (DWORD)lParam;
  434. Assert(rgfEnableButton != NULL);
  435. if (dwCurrentState != m_dwCurrentStatePrev)
  436. {
  437. m_dwCurrentStatePrev = dwCurrentState;
  438. SetDlgItemText(IDC_STATIC_CURRENT_STATUS,
  439. Service_PszMapStateToName(dwCurrentState, TRUE));
  440. EnableDlgItem(IDC_BUTTON_START, rgfEnableButton[iServiceActionStart]);
  441. EnableDlgItem(IDC_BUTTON_STOP, rgfEnableButton[iServiceActionStop]);
  442. EnableDlgItem(IDC_BUTTON_PAUSE, rgfEnableButton[iServiceActionPause]);
  443. EnableDlgItem(IDC_BUTTON_RESUME, rgfEnableButton[iServiceActionResume]);
  444. // Enable/disable the edit box of the startup parameter according
  445. // to the state of the 'start' button
  446. EnableDlgItemGroup(m_hWnd, rgzidDisableStartupParameters, rgfEnableButton[iServiceActionStart]);
  447. if (dwCurrentState == 0)
  448. {
  449. // Service state is unknown
  450. m_pData->m_hScManager = NULL;
  451. DoServicesErrMsgBox(m_hWnd, MB_OK | MB_ICONEXCLAMATION, 0, IDS_MSG_ss_UNABLE_TO_QUERY_SERVICE_STATUS,
  452. (LPCTSTR)m_pData->m_strServiceDisplayName, (LPCTSTR)m_pData->m_strUiMachineName);
  453. }
  454. }
  455. return 0;
  456. } // CServicePageGeneral::OnUpdateServiceStatus()
  457. /////////////////////////////////////////////////////////////////////
  458. // Periodically update the service status.
  459. //
  460. // Send a message to CServicePageGeneral object to notify the update.
  461. //
  462. // INTERFACE NOTES
  463. // The thread is responsible of deleting the paThreadProcInit object
  464. // before terminating itself.
  465. //
  466. DWORD CServicePageGeneral::ThreadProcPeriodicServiceStatusUpdate(CThreadProcInit * paThreadProcInit)
  467. {
  468. Assert(paThreadProcInit != NULL);
  469. Assert(paThreadProcInit->m_pThis != NULL);
  470. Assert(paThreadProcInit->m_fAutoDestroy == FALSE);
  471. BOOL rgfEnableButton[iServiceActionMax];
  472. DWORD dwCurrentState;
  473. // Infinite loop querying the service status
  474. while (!paThreadProcInit->m_fAutoDestroy)
  475. {
  476. if (paThreadProcInit->m_hwnd != NULL)
  477. {
  478. SC_HANDLE hScManager;
  479. {
  480. CSingleLock lock(&paThreadProcInit->m_CriticalSection, TRUE);
  481. hScManager = paThreadProcInit->m_hScManager;
  482. }
  483. BOOL fSuccess = Service_FGetServiceButtonStatus(
  484. hScManager,
  485. paThreadProcInit->m_strServiceName,
  486. OUT rgfEnableButton,
  487. OUT &dwCurrentState,
  488. TRUE /* fSilentError */);
  489. HWND hwnd = NULL;
  490. {
  491. CSingleLock lock(&paThreadProcInit->m_CriticalSection, TRUE);
  492. hwnd = paThreadProcInit->m_hwnd;
  493. }
  494. if (hwnd != NULL)
  495. {
  496. Assert(IsWindow(hwnd));
  497. ::SendMessage(hwnd, WM_UPDATE_SERVICE_STATUS,
  498. (WPARAM)rgfEnableButton, (LPARAM)dwCurrentState);
  499. }
  500. if (!fSuccess)
  501. {
  502. CSingleLock lock(&paThreadProcInit->m_CriticalSection, TRUE);
  503. paThreadProcInit->m_hwnd = NULL;
  504. }
  505. }
  506. Sleep(1000);
  507. } // while
  508. delete paThreadProcInit;
  509. return 0;
  510. } // CServicePageGeneral::ThreadProcPeriodicServiceStatusUpdate()
  511. /////////////////////////////////////////////////////////////////////
  512. // Help
  513. BOOL CServicePageGeneral::OnHelp(WPARAM /*wParam*/, LPARAM lParam)
  514. {
  515. return DoHelp(lParam, HELP_DIALOG_TOPIC(IDD_PROPPAGE_SERVICE_GENERAL));
  516. }
  517. BOOL CServicePageGeneral::OnContextHelp(WPARAM wParam, LPARAM /*lParam*/)
  518. {
  519. return DoContextHelp(wParam, HELP_DIALOG_TOPIC(IDD_PROPPAGE_SERVICE_GENERAL));
  520. }