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.

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