1 /*
2  * PROJECT:     ReactOS Applications
3  * LICENSE:     LGPL - See COPYING in the top level directory
4  * FILE:        base/applications/msconfig_new/srvpage.cpp
5  * PURPOSE:     Services page message handler
6  * COPYRIGHT:   Copyright 2005-2006 Christoph von Wittich <Christoph@ApiViewer.de>
7  *              Copyright 2011-2012 Hermes BELUSCA - MAITO <hermes.belusca@sfr.fr>
8  *
9  */
10 
11 #include "precomp.h"
12 #include "utils.h"
13 #include "regutils.h"
14 #include "stringutils.h"
15 // #include "CmdLineParser.h"
16 #include "listview.h"
17 #include "uxthemesupp.h"
18 
19 #include <winsvc.h>
20 
21 // #include <atlbase.h>
22 #include <atlcoll.h>
23 #include <atlstr.h>
24 
25 static HWND hServicesPage        = NULL;
26 static HWND hServicesListCtrl    = NULL;
27 static int  iSortedColumn        = 0;
28 static BOOL bMaskProprietarySvcs = FALSE;
29 
30 DWORD GetServicesActivation(VOID)
31 {
32     DWORD dwServices = 0;
33     RegGetDWORDValue(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\state", L"services", &dwServices);
34     return dwServices;
35 }
36 
37 BOOL SetServicesActivation(DWORD dwState)
38 {
39     return (RegSetDWORDValue(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\state", L"services", TRUE, dwState) == ERROR_SUCCESS);
40 }
41 
42 static BOOL
43 RegisterNoMsgAnymore(VOID)
44 {
45     return (RegSetDWORDValue(HKEY_CURRENT_USER /* HKEY_LOCAL_MACHINE ?? */,
46                              L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig",
47                              L"HideEssentialServiceWarning",
48                              TRUE, 1) == ERROR_SUCCESS);
49 }
50 
51 BOOL
52 HideEssentialServiceWarning(VOID)
53 {
54     BOOL  bRetVal = FALSE;
55     DWORD dwValue = 0;
56 
57     bRetVal = ( (RegGetDWORDValue(HKEY_CURRENT_USER /* HKEY_LOCAL_MACHINE ?? */,
58                                   L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig",
59                                   L"HideEssentialServiceWarning",
60                                   &dwValue) == ERROR_SUCCESS) &&
61                 (dwValue == 1) );
62 
63     return bRetVal;
64 }
65 
66 struct ServiceItem
67 {
68     ServiceItem(const LPCWSTR lpszSvcName,
69                 BOOL          bIsEnabled,
70                 BOOL          bIsRequired) :
71         m_lpszSvcName(lpszSvcName),
72         m_bIsEnabled(bIsEnabled),
73         m_bIsRequired(bIsRequired)
74     { }
75 
76     ~ServiceItem(void)
77     { }
78 
79     CAtlStringW m_lpszSvcName;
80     BOOL        m_bIsEnabled;
81     BOOL        m_bIsRequired;
82 };
83 
84 struct RegistryDisabledServiceItemParams
85 {
86     BOOL bIsPresent;
87     BOOL bIsKeyed; // bIsKeyed == TRUE for a keyed-registered service ; == FALSE for a valued-registered service.
88     DWORD dwStartType;
89     SYSTEMTIME time;
90 };
91 
92 static CAtlList<CAtlStringW> userModificationsList;
93 
94 QUERY_REGISTRY_VALUES_ROUTINE(GetRegistryValuedDisabledServicesQueryRoutine)
95 {
96     UNREFERENCED_PARAMETER(KeyName);
97     UNREFERENCED_PARAMETER(ValueData);
98     UNREFERENCED_PARAMETER(ValueLength);
99 
100     if (!EntryContext)
101         return ERROR_SUCCESS;
102 
103     RegistryDisabledServiceItemParams* pContextParams = (RegistryDisabledServiceItemParams*)EntryContext;
104     if (pContextParams->bIsPresent)
105         return ERROR_SUCCESS;
106 
107     if ( (hRootKey == HKEY_LOCAL_MACHINE) && (ValueType == REG_DWORD) && (ValueLength == sizeof(DWORD)) &&
108          (wcsicmp((LPCWSTR)Context, ValueName) == 0) )
109     {
110         pContextParams->bIsPresent  = TRUE;
111         pContextParams->bIsKeyed    = FALSE;
112         pContextParams->dwStartType = *(DWORD*)ValueData;
113         // pContextParams->time        = {};
114     }
115     else
116     {
117         pContextParams->bIsPresent  = FALSE;
118         pContextParams->bIsKeyed    = FALSE;
119         pContextParams->dwStartType = 0;
120         // pContextParams->time        = {};
121     }
122 
123     return ERROR_SUCCESS;
124 }
125 
126 QUERY_REGISTRY_KEYS_ROUTINE(GetRegistryKeyedDisabledServicesQueryRoutine)
127 {
128     UNREFERENCED_PARAMETER(hRootKey);
129     UNREFERENCED_PARAMETER(KeyName);
130 
131     if (!EntryContext)
132         return ERROR_SUCCESS;
133 
134     RegistryDisabledServiceItemParams* pContextParams = (RegistryDisabledServiceItemParams*)EntryContext;
135     if (pContextParams->bIsPresent)
136         return ERROR_SUCCESS;
137 
138     DWORD dwType = 0, dwBufSize = 0;
139 
140     // Be careful, the order of the operations in the comparison is very important.
141     if ( (wcsicmp((LPCWSTR)Context, SubKeyName) == 0) &&
142          (RegQueryValueEx(hOpenedSubKey, /* ValueName == */ SubKeyName, NULL, &dwType, NULL, &dwBufSize) == ERROR_SUCCESS) &&
143          (dwType == REG_DWORD) && (dwBufSize == sizeof(DWORD)) )
144     {
145 #if 1 // DisableDate
146         SYSTEMTIME disableDate = {};
147         DWORD dwRegData = 0;
148 
149         dwRegData = 0;
150         RegGetDWORDValue(hOpenedSubKey, NULL, L"DAY", &dwRegData);
151         disableDate.wDay = LOWORD(dwRegData);
152 
153         dwRegData = 0;
154         RegGetDWORDValue(hOpenedSubKey, NULL, L"HOUR", &dwRegData);
155         disableDate.wHour = LOWORD(dwRegData);
156 
157         dwRegData = 0;
158         RegGetDWORDValue(hOpenedSubKey, NULL, L"MINUTE", &dwRegData);
159         disableDate.wMinute = LOWORD(dwRegData);
160 
161         dwRegData = 0;
162         RegGetDWORDValue(hOpenedSubKey, NULL, L"MONTH", &dwRegData);
163         disableDate.wMonth = LOWORD(dwRegData);
164 
165         dwRegData = 0;
166         RegGetDWORDValue(hOpenedSubKey, NULL, L"SECOND", &dwRegData);
167         disableDate.wSecond = LOWORD(dwRegData);
168 
169         dwRegData = 0;
170         RegGetDWORDValue(hOpenedSubKey, NULL, L"YEAR", &dwRegData);
171         disableDate.wYear = LOWORD(dwRegData);
172 #endif
173 
174         DWORD dwStartType = 0;
175         RegGetDWORDValue(hOpenedSubKey, NULL, SubKeyName /* Service name */, &dwStartType);
176 
177         pContextParams->bIsPresent  = TRUE;
178         pContextParams->bIsKeyed    = TRUE;
179         pContextParams->dwStartType = dwStartType;
180         pContextParams->time        = disableDate;
181     }
182     else
183     {
184         pContextParams->bIsPresent  = FALSE;
185         pContextParams->bIsKeyed    = TRUE;
186         pContextParams->dwStartType = 0;
187         // pContextParams->time        = {};
188     }
189 
190     return ERROR_SUCCESS;
191 }
192 
193 
194 
195 static void AddService(SC_HANDLE hSCManager, LPENUM_SERVICE_STATUS_PROCESS Service, BOOL bHideOSVendorServices)
196 {
197     //
198     // Retrieve a handle to the service.
199     //
200     SC_HANDLE hService = OpenServiceW(hSCManager, Service->lpServiceName, SERVICE_QUERY_CONFIG);
201     if (hService == NULL)
202         return;
203 
204     DWORD dwBytesNeeded = 0;
205     QueryServiceConfigW(hService, NULL, 0, &dwBytesNeeded);
206     // if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
207 
208     LPQUERY_SERVICE_CONFIG lpServiceConfig = (LPQUERY_SERVICE_CONFIG)MemAlloc(0, dwBytesNeeded);
209     if (!lpServiceConfig)
210     {
211         CloseServiceHandle(hService);
212         return;
213     }
214     QueryServiceConfigW(hService, lpServiceConfig, dwBytesNeeded, &dwBytesNeeded);
215 
216     //
217     // Get the service's vendor...
218     //
219     LPWSTR lpszVendor = NULL;
220     {
221     // Isolate only the executable path, without any arguments.
222     // TODO: Correct at the level of CmdLineToArgv the potential bug when lpszFilename == NULL.
223 #if 0 // Disabled until CmdLineToArgv is included
224     unsigned int argc = 0;
225     LPWSTR*      argv = NULL;
226     CmdLineToArgv(lpServiceConfig->lpBinaryPathName, &argc, &argv, L" \t");
227     if (argc >= 1 && argv[0])
228         lpszVendor = GetExecutableVendor(argv[0]);
229 #else
230     // Hackish solution taken from the original srvpage.c.
231     // Will be removed after CmdLineToArgv is introduced.
232     WCHAR FileName[MAX_PATH];
233     memset(&FileName, 0, sizeof(FileName));
234     if (wcscspn(lpServiceConfig->lpBinaryPathName, L"\""))
235     {
236         wcsncpy(FileName, lpServiceConfig->lpBinaryPathName, wcscspn(lpServiceConfig->lpBinaryPathName, L" ") );
237     }
238     else
239     {
240         wcscpy(FileName, lpServiceConfig->lpBinaryPathName);
241     }
242     lpszVendor = GetExecutableVendor(FileName);
243 #endif
244     if (!lpszVendor)
245         lpszVendor = LoadResourceString(hInst, IDS_UNKNOWN);
246 #if 0
247     MemFree(argv);
248 #endif
249     }
250 
251     // ...and display or not the Microsoft / ReactOS services.
252     BOOL bContinue = TRUE;
253     if (bHideOSVendorServices)
254     {
255         if (FindSubStrI(lpszVendor, bIsWindows ? IDS_MICROSOFT : IDS_REACTOS))
256             bContinue = FALSE;
257     }
258 
259     if (bContinue)
260     {
261         BOOL bIsServiceEnabled  = (lpServiceConfig->dwStartType != SERVICE_DISABLED);
262         BOOL bAddServiceToList  = FALSE;
263         BOOL bIsModifiedService = FALSE;
264         RegistryDisabledServiceItemParams params = {};
265 
266         //
267         // Try to look into the user modifications list...
268         //
269         POSITION it = userModificationsList.Find(Service->lpServiceName);
270         if (it)
271         {
272             bAddServiceToList  = TRUE;
273             bIsModifiedService = TRUE;
274         }
275 
276         //
277         // ...if not found, try to find if the disabled service is in the registry.
278         //
279         if (!bAddServiceToList)
280         {
281             if (!bIsServiceEnabled)
282             {
283                 QUERY_REGISTRY_KEYS_TABLE KeysQueryTable[2] = {};
284                 KeysQueryTable[0].QueryRoutine = GetRegistryKeyedDisabledServicesQueryRoutine;
285                 KeysQueryTable[0].EntryContext = &params;
286                 RegQueryRegistryKeys(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", KeysQueryTable, Service->lpServiceName);
287 
288                 bAddServiceToList = params.bIsPresent;
289 
290                 if (bIsWindows && bIsPreVistaOSVersion && !bAddServiceToList)
291                 {
292                     QUERY_REGISTRY_VALUES_TABLE ValuesQueryTable[2] = {};
293                     ValuesQueryTable[0].QueryRoutine = GetRegistryValuedDisabledServicesQueryRoutine;
294                     ValuesQueryTable[0].EntryContext = &params;
295                     RegQueryRegistryValues(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", ValuesQueryTable, Service->lpServiceName);
296 
297                     bAddServiceToList = params.bIsPresent;
298                 }
299             }
300             else
301             {
302                 bAddServiceToList = TRUE;
303             }
304         }
305 
306         if (bAddServiceToList)
307         {
308             //
309             // Check if service is required by the system.
310             //
311             BOOL bIsRequired = FALSE;
312 
313             dwBytesNeeded = 0;
314             QueryServiceConfig2(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &dwBytesNeeded);
315             // if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
316 
317             LPSERVICE_FAILURE_ACTIONS lpServiceFailureActions = (LPSERVICE_FAILURE_ACTIONS)MemAlloc(0, dwBytesNeeded);
318             if (!lpServiceFailureActions)
319             {
320                 MemFree(lpszVendor);
321                 MemFree(lpServiceConfig);
322                 CloseServiceHandle(hService);
323                 return;
324             }
325 
326             QueryServiceConfig2(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)lpServiceFailureActions, dwBytesNeeded, &dwBytesNeeded);
327 
328             // In Microsoft's MSConfig, things are done just like that!! (extracted string values from msconfig.exe)
329             if ( ( wcsicmp(Service->lpServiceName, L"rpcss"     ) == 0   ||
330                    wcsicmp(Service->lpServiceName, L"rpclocator") == 0   ||
331                    wcsicmp(Service->lpServiceName, L"dcomlaunch") == 0 ) ||
332                    ( lpServiceFailureActions &&
333                      (lpServiceFailureActions->cActions >= 1) &&
334                      (lpServiceFailureActions->lpsaActions[0].Type == SC_ACTION_REBOOT) ) ) // We add also this test, which corresponds to real life.
335             {
336                 bIsRequired = TRUE;
337             }
338             MemFree(lpServiceFailureActions);
339 
340             //
341             // Add the service into the list.
342             //
343             LVITEM item = {};
344             item.mask = LVIF_TEXT | LVIF_PARAM;
345             item.pszText = Service->lpDisplayName;
346             item.lParam = reinterpret_cast<LPARAM>(new ServiceItem(Service->lpServiceName, bIsServiceEnabled, bIsRequired));
347             item.iItem = ListView_InsertItem(hServicesListCtrl, &item);
348 
349             if (bIsRequired)
350             {
351                 LPWSTR lpszYes = LoadResourceString(hInst, IDS_YES);
352                 ListView_SetItemText(hServicesListCtrl, item.iItem, 1, lpszYes);
353                 MemFree(lpszYes);
354             }
355 
356             ListView_SetItemText(hServicesListCtrl, item.iItem, 2, lpszVendor);
357 
358             LPWSTR lpszStatus = LoadResourceString(hInst, ((Service->ServiceStatusProcess.dwCurrentState == SERVICE_STOPPED) ? IDS_SERVICES_STATUS_STOPPED : IDS_SERVICES_STATUS_RUNNING));
359             ListView_SetItemText(hServicesListCtrl, item.iItem, 3, lpszStatus);
360             MemFree(lpszStatus);
361 
362             if (!bIsServiceEnabled)
363             {
364                 LPWSTR lpszUnknown = LoadResourceString(hInst, IDS_UNKNOWN);
365 
366                 LPWSTR lpszDisableDate = FormatDateTime(&params.time);
367                 ListView_SetItemText(hServicesListCtrl, item.iItem, 4, (lpszDisableDate ? lpszDisableDate : lpszUnknown));
368                 FreeDateTime(lpszDisableDate);
369 
370                 MemFree(lpszUnknown);
371             }
372 
373             ListView_SetCheckState(hServicesListCtrl, item.iItem, (!bIsModifiedService ? bIsServiceEnabled : !bIsServiceEnabled));
374         }
375     }
376 
377     MemFree(lpszVendor);
378     MemFree(lpServiceConfig);
379     CloseServiceHandle(hService);
380 
381     return;
382 }
383 
384 static void ClearServicesList(void)
385 {
386     LVITEM lvitem = {};
387     lvitem.mask  = LVIF_PARAM;
388     lvitem.iItem = -1; // From the beginning.
389 
390     while ((lvitem.iItem = ListView_GetNextItem(hServicesListCtrl, lvitem.iItem, LVNI_ALL)) != -1)
391     {
392         ListView_GetItem(hServicesListCtrl, &lvitem);
393 
394         delete reinterpret_cast<ServiceItem*>(lvitem.lParam);
395         lvitem.lParam = NULL;
396     }
397     ListView_DeleteAllItems(hServicesListCtrl);
398 
399     return;
400 }
401 
402 static void GetServices(BOOL bHideOSVendorServices = FALSE)
403 {
404     //
405     // First of all, clear the list.
406     //
407     ClearServicesList();
408 
409     //
410     // Now, we can list the services.
411     //
412 
413     // Open the Service Control Manager.
414     SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
415     if (hSCManager == NULL)
416         return;
417 
418     // Enumerate all the Win32 services.
419     DWORD dwBytesNeeded = 0;
420     DWORD dwNumServices = 0;
421     // DWORD dwResumeHandle = 0;
422     EnumServicesStatusExW(hSCManager, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &dwBytesNeeded, &dwNumServices, NULL /* &dwResumeHandle */, NULL);
423     // if (GetLastError() == ERROR_MORE_DATA)
424 
425     LPENUM_SERVICE_STATUS_PROCESS lpServices = (LPENUM_SERVICE_STATUS_PROCESS)MemAlloc(0, dwBytesNeeded);
426     if (!lpServices)
427     {
428         CloseServiceHandle(hSCManager);
429         return;
430     }
431     EnumServicesStatusExW(hSCManager, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, (LPBYTE)lpServices, dwBytesNeeded, &dwBytesNeeded, &dwNumServices, NULL /* &dwResumeHandle */, NULL);
432 
433     // Add them into the list.
434     for (DWORD i = 0 ; i < dwNumServices ; ++i)
435     {
436         AddService(hSCManager, lpServices + i, bHideOSVendorServices);
437     }
438 
439     // Cleaning.
440     MemFree(lpServices);
441     CloseServiceHandle(hSCManager);
442 
443     return;
444 }
445 
446 INT_PTR CALLBACK
447 RequiredServicesDisablingDialogWndProc(HWND hDlg,
448                                        UINT message,
449                                        WPARAM wParam,
450                                        LPARAM lParam)
451 {
452     UNREFERENCED_PARAMETER(lParam);
453 
454     switch (message)
455     {
456         case WM_INITDIALOG:
457         {
458             /* Correctly display message strings */
459             LPCWSTR szOSVendor;
460             size_t itemLength = 0;
461             LPWSTR szItem = NULL, szNewItem = NULL;
462 
463             szOSVendor = (bIsWindows ? IDS_WINDOWS : IDS_REACTOS);
464 
465             itemLength = GetWindowTextLength(GetDlgItem(hDlg, IDC_STATIC_REQSVCSDIS_INFO)) + 1;
466             szItem     = (LPWSTR)MemAlloc(0, itemLength * sizeof(WCHAR));
467             GetDlgItemText(hDlg, IDC_STATIC_REQSVCSDIS_INFO, szItem, (int)itemLength);
468             szNewItem  = FormatString(szItem, szOSVendor);
469             SetDlgItemText(hDlg, IDC_STATIC_REQSVCSDIS_INFO, szNewItem);
470             MemFree(szNewItem);
471             MemFree(szItem);
472 
473             return TRUE;
474         }
475 
476         case WM_COMMAND:
477         {
478             switch (LOWORD(wParam))
479             {
480                 case IDOK:
481                 {
482                     if (Button_GetCheck(GetDlgItem(hDlg, IDC_CBX_REQSVCSDIS_NO_MSG_ANYMORE)) == BST_CHECKED)
483                         RegisterNoMsgAnymore();
484 
485                     EndDialog(hDlg, LOWORD(wParam));
486                     return TRUE;
487                 }
488 
489                 case IDCANCEL:
490                     EndDialog(hDlg, LOWORD(wParam));
491                     return TRUE;
492 
493                 default:
494                     //break;
495                     return FALSE;
496             }
497         }
498     }
499 
500     return FALSE;
501 }
502 
503 static BOOL ValidateItem(int index, BOOL bNewState, BOOL bDisplayErrors)
504 {
505     ServiceItem* pSvcItem = NULL;
506 
507     LVITEM truc = {};
508     truc.mask = LVIF_PARAM;
509     truc.iItem = index;
510     ListView_GetItem(hServicesListCtrl, &truc);
511 
512     // The lParam member must be valid.
513     pSvcItem = reinterpret_cast<ServiceItem*>(truc.lParam);
514     if (!pSvcItem)
515         return FALSE;
516 
517     //
518     // Allow modifications only if the service is not a required service for the system,
519     // or allow only the activation of a disabled required service.
520     //
521     BOOL bOldState = !!(ListView_GetCheckState(hServicesListCtrl, truc.iItem /* == index */) % 2);
522 
523     if ( !pSvcItem->m_bIsRequired ||
524          (pSvcItem->m_bIsRequired && !pSvcItem->m_bIsEnabled && bOldState == FALSE && bNewState == TRUE) )
525     {
526         if (bOldState == bNewState)
527             return FALSE;
528 
529         ListView_SetCheckState(hServicesListCtrl, index, bNewState);
530 
531         if (pSvcItem->m_bIsEnabled) // Enabled service.
532         {
533             if (bNewState == FALSE) // To be deactivated.
534             {
535                 userModificationsList.AddTail(pSvcItem->m_lpszSvcName);
536             }
537             else if (bNewState == TRUE) // To be reactivated
538             {
539                 POSITION it = userModificationsList.Find(pSvcItem->m_lpszSvcName);
540                 if (it)
541                 {
542                     userModificationsList.RemoveAt(it);
543                 }
544                 else
545                 {
546                     OutputDebugString(_T("(1) \"WTF: What The Fukhurmajalmahamadahaldeliya ?!\" (The Dictator, Sacha Baron Cohen)\n"));
547                 }
548             }
549         }
550         else // Disabled service.
551         {
552             if (bNewState == TRUE) // To be activated.
553             {
554                 userModificationsList.AddTail(pSvcItem->m_lpszSvcName);
555             }
556             else if (bNewState == FALSE) // To be redeactivated
557             {
558                 POSITION it = userModificationsList.Find(pSvcItem->m_lpszSvcName);
559                 if (it)
560                 {
561                     userModificationsList.RemoveAt(it);
562                 }
563                 else
564                 {
565                     OutputDebugString(_T("(2) \"WTF: What The Fukhurmajalmahamadahaldeliya ?!\" (The Dictator, Sacha Baron Cohen)\n"));
566                 }
567             }
568         }
569 
570         return TRUE;
571     }
572     else
573     {
574         if (bDisplayErrors)
575         {
576             DialogBoxW(hInst, MAKEINTRESOURCEW(IDD_REQUIRED_SERVICES_DISABLING_DIALOG), hServicesPage /* hMainWnd */, RequiredServicesDisablingDialogWndProc);
577         }
578 
579         return FALSE;
580     }
581 }
582 
583 
584 static void
585 Update_Btn_States(HWND hDlg)
586 {
587     // HWND hTree = GetDlgItem(hDlg, IDC_SYSTEM_TREE);
588 
589     //
590     // "Enable all" / "Disable all" buttons.
591     //
592     // UINT uRootCheckState = TreeView_GetRealSubtreeState(hTree, TVI_ROOT);
593     UINT uRootCheckState = ListView_GetCheckState(hServicesListCtrl, 0);
594 #define OP(a, b) ((a) == (b) ? (a) : 2)
595     int index = 0; // -1 // From the beginning + 1.
596     while ((index = ListView_GetNextItem(hServicesListCtrl, index, LVNI_ALL)) != -1)
597     {
598         UINT temp = ListView_GetCheckState(hServicesListCtrl, index);
599         uRootCheckState = OP(uRootCheckState, temp);
600     }
601 
602     if (uRootCheckState == 0)
603     {
604         EnableWindow(GetDlgItem(hDlg, IDC_BTN_SERVICES_ACTIVATE)  , TRUE );
605         EnableWindow(GetDlgItem(hDlg, IDC_BTN_SERVICES_DEACTIVATE), FALSE);
606     }
607     else if (uRootCheckState == 1)
608     {
609         EnableWindow(GetDlgItem(hDlg, IDC_BTN_SERVICES_ACTIVATE)  , FALSE);
610         EnableWindow(GetDlgItem(hDlg, IDC_BTN_SERVICES_DEACTIVATE), TRUE );
611     }
612     else if (uRootCheckState == 2)
613     {
614         EnableWindow(GetDlgItem(hDlg, IDC_BTN_SERVICES_ACTIVATE)  , TRUE);
615         EnableWindow(GetDlgItem(hDlg, IDC_BTN_SERVICES_DEACTIVATE), TRUE);
616     }
617     else
618     {
619         EnableWindow(GetDlgItem(hDlg, IDC_BTN_SERVICES_ACTIVATE)  , FALSE);
620         EnableWindow(GetDlgItem(hDlg, IDC_BTN_SERVICES_DEACTIVATE), FALSE);
621     }
622 
623     return;
624 }
625 
626 extern "C" {
627 
628 INT_PTR CALLBACK
629 ServicesPageWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
630 {
631     UNREFERENCED_PARAMETER(lParam);
632     UNREFERENCED_PARAMETER(wParam);
633 
634     switch (message)
635     {
636         case WM_INITDIALOG:
637         {
638             hServicesPage     = hDlg;
639             hServicesListCtrl = GetDlgItem(hServicesPage, IDC_SERVICES_LIST);
640 
641             //
642             // Correctly display message strings.
643             //
644             LPCWSTR szOSVendor = (bIsWindows ? IDS_MICROSOFT : IDS_REACTOS);
645 
646             size_t itemLength = 0;
647             LPWSTR szItem = NULL, szNewItem = NULL;
648 
649             itemLength = GetWindowTextLength(GetDlgItem(hServicesPage, IDC_STATIC_SERVICES_WARNING)) + 1;
650             szItem     = (LPWSTR)MemAlloc(0, itemLength * sizeof(WCHAR));
651             GetDlgItemText(hServicesPage, IDC_STATIC_SERVICES_WARNING, szItem, (int)itemLength);
652             szNewItem  = FormatString(szItem, szOSVendor);
653             SetDlgItemText(hServicesPage, IDC_STATIC_SERVICES_WARNING, szNewItem);
654             MemFree(szNewItem);
655             MemFree(szItem);
656 
657             itemLength = GetWindowTextLength(GetDlgItem(hServicesPage, IDC_CBX_SERVICES_MASK_PROPRIETARY_SVCS)) + 1;
658             szItem     = (LPWSTR)MemAlloc(0, itemLength * sizeof(WCHAR));
659             GetDlgItemText(hServicesPage, IDC_CBX_SERVICES_MASK_PROPRIETARY_SVCS, szItem, (int)itemLength);
660             szNewItem  = FormatString(szItem, szOSVendor);
661             SetDlgItemText(hServicesPage, IDC_CBX_SERVICES_MASK_PROPRIETARY_SVCS, szNewItem);
662             MemFree(szNewItem);
663             MemFree(szItem);
664 
665             //
666             // Initialize the styles.
667             //
668             DWORD dwStyle = ListView_GetExtendedListViewStyle(hServicesListCtrl);
669             ListView_SetExtendedListViewStyle(hServicesListCtrl, dwStyle | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
670             SetWindowTheme(hServicesListCtrl, L"Explorer", NULL);
671 
672             //
673             // Initialize the application page's controls.
674             //
675             LVCOLUMN column = {};
676 
677             // First column : Service's name.
678             column.mask = LVCF_TEXT | LVCF_WIDTH;
679             column.pszText = LoadResourceString(hInst, IDS_SERVICES_COLUMN_SERVICE);
680             column.cx = 150;
681             ListView_InsertColumn(hServicesListCtrl, 0, &column);
682             MemFree(column.pszText);
683 
684             // Second column : Whether the service is required or not.
685             column.mask = LVCF_TEXT | LVCF_WIDTH;
686             column.pszText = LoadResourceString(hInst, IDS_SERVICES_COLUMN_REQ);
687             column.cx = 60;
688             ListView_InsertColumn(hServicesListCtrl, 1, &column);
689             MemFree(column.pszText);
690 
691             // Third column : Service's vendor.
692             column.mask = LVCF_TEXT | LVCF_WIDTH;
693             column.pszText = LoadResourceString(hInst, IDS_SERVICES_COLUMN_VENDOR);
694             column.cx = 150;
695             ListView_InsertColumn(hServicesListCtrl, 2, &column);
696             MemFree(column.pszText);
697 
698             // Fourth column : Service's status.
699             column.mask = LVCF_TEXT | LVCF_WIDTH;
700             column.pszText = LoadResourceString(hInst, IDS_SERVICES_COLUMN_STATUS);
701             column.cx = 60;
702             ListView_InsertColumn(hServicesListCtrl, 3, &column);
703             MemFree(column.pszText);
704 
705             // Fifth column : Service's disabled date.
706             column.mask = LVCF_TEXT | LVCF_WIDTH;
707             column.pszText = LoadResourceString(hInst, IDS_SERVICES_COLUMN_DATEDISABLED);
708             column.cx = 120;
709             ListView_InsertColumn(hServicesListCtrl, 4, &column);
710             MemFree(column.pszText);
711 
712             //
713             // Populate and sort the list.
714             //
715             GetServices();
716             ListView_Sort(hServicesListCtrl, 0);
717             Update_Btn_States(hDlg);
718 
719             // Select the first item.
720             ListView_SetItemState(hServicesListCtrl, 0, LVIS_SELECTED, LVIS_SELECTED);
721 
722             return TRUE;
723         }
724 
725         case WM_DESTROY:
726         {
727             ClearServicesList();
728             userModificationsList.RemoveAll();
729             return 0;
730         }
731 
732         case WM_COMMAND:
733         {
734             switch (LOWORD(wParam))
735             {
736                 case IDC_BTN_SERVICES_ACTIVATE:
737                 {
738                     BOOL bAreThereMods = FALSE;
739 
740                     int index = -1; // From the beginning.
741                     while ((index = ListView_GetNextItem(hServicesListCtrl, index, LVNI_ALL)) != -1)
742                     {
743                         bAreThereMods = ValidateItem(index, TRUE, FALSE) || bAreThereMods; // The order is verrrrrry important !!!!
744                     }
745 
746                     if (bAreThereMods)
747                     {
748                         Update_Btn_States(hDlg);
749                         PropSheet_Changed(GetParent(hServicesPage), hServicesPage);
750                     }
751 
752                     return TRUE;
753                 }
754 
755                 case IDC_BTN_SERVICES_DEACTIVATE:
756                 {
757                     BOOL bAreThereMods = FALSE;
758 
759                     int index = -1; // From the beginning.
760                     while ((index = ListView_GetNextItem(hServicesListCtrl, index, LVNI_ALL)) != -1)
761                     {
762                         bAreThereMods = ValidateItem(index, FALSE, FALSE) || bAreThereMods; // The order is verrrrrry important !!!!
763                     }
764 
765                     if (bAreThereMods)
766                     {
767                         Update_Btn_States(hDlg);
768                         PropSheet_Changed(GetParent(hServicesPage), hServicesPage);
769                     }
770 
771                     return TRUE;
772                 }
773 
774                 case IDC_CBX_SERVICES_MASK_PROPRIETARY_SVCS:
775                 {
776                     bMaskProprietarySvcs = !bMaskProprietarySvcs;
777                     GetServices(bMaskProprietarySvcs);
778                     Update_Btn_States(hDlg);
779 
780                     return TRUE;
781                 }
782 
783                 default:
784                     return FALSE;
785             }
786             return FALSE;
787         }
788 
789         case UM_CHECKSTATECHANGE:
790         {
791             BOOL bNewCheckState = !!((ListView_GetCheckState(hServicesListCtrl, int(lParam)) + 1) % 2);
792 
793             if (ValidateItem(/*reinterpret_cast<int>*/ int(lParam), bNewCheckState, !HideEssentialServiceWarning()))
794             {
795                 Update_Btn_States(hDlg);
796                 PropSheet_Changed(GetParent(hServicesPage), hServicesPage);
797             }
798 
799             return TRUE;
800         }
801 
802         case WM_NOTIFY:
803         {
804             if (reinterpret_cast<LPNMHDR>(lParam)->hwndFrom == hServicesListCtrl)
805             {
806                 switch (reinterpret_cast<LPNMHDR>(lParam)->code)
807                 {
808                     case NM_CLICK:
809                     case NM_RCLICK:
810                     {
811                         DWORD         dwpos = GetMessagePos();
812                         LVHITTESTINFO ht    = {};
813                         ht.pt.x = GET_X_LPARAM(dwpos);
814                         ht.pt.y = GET_Y_LPARAM(dwpos);
815                         MapWindowPoints(HWND_DESKTOP /*NULL*/, hServicesListCtrl, &ht.pt, 1);
816 
817                         /*
818                          * We use ListView_SubItemHitTest(...) and not ListView_HitTest(...)
819                          * because ListView_HitTest(...) returns bad flags when one clicks
820                          * on a sub-item different from 0. The flags then contain LVHT_ONITEMSTATEICON
821                          * which must not be obviously present in this case.
822                          */
823                         ListView_SubItemHitTest(hServicesListCtrl, &ht);
824 
825                         if (LVHT_ONITEMSTATEICON & ht.flags)
826                         {
827                             PostMessage(hDlg, UM_CHECKSTATECHANGE, 0, (LPARAM)ht.iItem);
828 
829                             // Disable default behaviour. Needed for the UM_CHECKSTATECHANGE
830                             // custom notification to work as expected.
831                             SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
832                         }
833 
834                         return TRUE;
835                     }
836 
837                     case NM_DBLCLK:
838                     case NM_RDBLCLK:
839                     {
840                         // We deactivate double-clicks.
841                         SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
842                         return TRUE;
843                     }
844 
845                     case LVN_KEYDOWN:
846                     {
847                         if (reinterpret_cast<LPNMLVKEYDOWN>(lParam)->wVKey == VK_SPACE)
848                         {
849                             int iItem = ListView_GetSelectionMark(hServicesListCtrl);
850                             PostMessage(hDlg, UM_CHECKSTATECHANGE, 0, (LPARAM)iItem);
851 
852                             // Disable default behaviour. Needed for the UM_CHECKSTATECHANGE
853                             // custom notification to work as expected.
854                             SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
855                         }
856 
857                         return TRUE;
858                     }
859 
860                     case LVN_COLUMNCLICK:
861                     {
862                         int iSortingColumn = reinterpret_cast<LPNMLISTVIEW>(lParam)->iSubItem;
863 
864                         ListView_SortEx(hServicesListCtrl, iSortingColumn, iSortedColumn);
865                         iSortedColumn = iSortingColumn;
866 
867                         return TRUE;
868                     }
869                 }
870             }
871             else
872             {
873                 switch (reinterpret_cast<LPNMHDR>(lParam)->code)
874                 {
875                     case PSN_APPLY:
876                     {
877                         // Try to apply the modifications to the system.
878                         MessageBox(NULL, _T("In Services page: PSN_APPLY"), _T("Info"), MB_ICONINFORMATION);
879 
880                         /*
881                         //
882                         // Move this away...
883                         //
884                         int iRetVal = MessageBox(NULL, _T("Would you really want to modify the configuration of your system ?"), _T("Warning"), MB_ICONWARNING | MB_YESNOCANCEL);
885 
886                         if (iRetVal == IDYES /\* modifications are OK *\/)
887                             SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, PSNRET_NOERROR);
888                         else if (iRetVal == IDNO /\* modifications are not OK *\/)
889                             SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, PSNRET_NOERROR);
890                         else // if (iRetVal == IDCANCEL) // There was an error...
891                             SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, PSNRET_INVALID);
892                         */
893 
894                         //
895                         // We modify the services which are stored in the user modification list.
896                         //
897 
898                         // 1- Open the Service Control Manager for modifications.
899                         SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
900                         if (hSCManager != NULL)
901                         {
902                             LPCWSTR svcName;
903 
904                             for (POSITION it = userModificationsList.GetHeadPosition(); it; userModificationsList.GetNext(it))
905                             {
906                                 svcName = userModificationsList.GetAt(it);
907 
908                                 // 2- Retrieve a handle to the service.
909                                 SC_HANDLE hService = OpenServiceW(hSCManager, svcName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
910                                 if (hService == NULL)
911                                 {
912                                     // TODO : Show a message box.
913                                     continue;
914                                 }
915 
916                                 DWORD dwBytesNeeded = 0;
917                                 QueryServiceConfigW(hService, NULL, 0, &dwBytesNeeded);
918                                 // if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
919 
920                                 LPQUERY_SERVICE_CONFIG lpServiceConfig = (LPQUERY_SERVICE_CONFIG)MemAlloc(0, dwBytesNeeded);
921                                 if (!lpServiceConfig)
922                                 {
923                                     CloseServiceHandle(hService);
924                                     continue; // TODO ? Show a message box...
925                                 }
926                                 QueryServiceConfigW(hService, lpServiceConfig, dwBytesNeeded, &dwBytesNeeded);
927 
928                                 if (lpServiceConfig->dwStartType == SERVICE_DISABLED) // We have a disabled service which is becoming to be enabled.
929                                 {
930                                     // 3a- Retrieve the properties of the disabled service from the registry.
931                                     RegistryDisabledServiceItemParams params = {};
932 
933                                     QUERY_REGISTRY_KEYS_TABLE KeysQueryTable[2] = {};
934                                     KeysQueryTable[0].QueryRoutine = GetRegistryKeyedDisabledServicesQueryRoutine;
935                                     KeysQueryTable[0].EntryContext = &params;
936                                     RegQueryRegistryKeys(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", KeysQueryTable, (PVOID)svcName);
937 
938                                     if (bIsWindows && bIsPreVistaOSVersion && !params.bIsPresent)
939                                     {
940                                         QUERY_REGISTRY_VALUES_TABLE ValuesQueryTable[2] = {};
941                                         ValuesQueryTable[0].QueryRoutine = GetRegistryValuedDisabledServicesQueryRoutine;
942                                         ValuesQueryTable[0].EntryContext = &params;
943                                         RegQueryRegistryValues(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", ValuesQueryTable, (PVOID)svcName);
944                                     }
945 
946                                     if (params.bIsPresent)
947                                     {
948                                         // 4a- Modify the service.
949                                         ChangeServiceConfigW(hService, SERVICE_NO_CHANGE, params.dwStartType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
950 
951                                         // 5a- Remove the registry entry of the service.
952                                         if (params.bIsKeyed)
953                                         {
954                                             CAtlStringW serviceRegKey(L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services\\");
955                                             serviceRegKey += svcName;
956                                             RegDeleteKeyW(HKEY_LOCAL_MACHINE, serviceRegKey);
957 
958                                             /***** HACK for Windows < Vista (e.g. 2000, Xp, 2003...) *****/
959                                             //
960                                             // Delete also the valued-entry of the service.
961                                             //
962                                             if (bIsWindows && bIsPreVistaOSVersion)
963                                             {
964                                                 HKEY hSubKey = NULL;
965                                                 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", 0, KEY_SET_VALUE /*KEY_READ*/, &hSubKey) == ERROR_SUCCESS)
966                                                 {
967                                                     RegDeleteValue(hSubKey, svcName);
968                                                     RegCloseKey(hSubKey);
969                                                 }
970                                             }
971                                             /*************************************************************/
972                                         }
973                                         else
974                                         {
975                                             HKEY hSubKey = NULL;
976                                             if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", 0, KEY_SET_VALUE /*KEY_READ*/, &hSubKey) == ERROR_SUCCESS)
977                                             {
978                                                 RegDeleteValue(hSubKey, svcName);
979                                                 RegCloseKey(hSubKey);
980                                             }
981                                         }
982 
983                                         ////////// HACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACK ///////////
984                                         // userModificationsList.RemoveAt(it);
985                                     }
986                                     else
987                                     {
988                                         // Ohoh !! We have a very big problem.
989                                         MessageBox(NULL, _T("WTF ??"), _T("FATAL ERROR !!!!"), MB_ICONERROR);
990                                     }
991                                 }
992                                 else // We have an enabled service which is becoming to be disabled.
993                                 {
994                                     // 3b- Retrieve the local time of disabling.
995                                     SYSTEMTIME disableDate = {};
996                                     GetLocalTime(&disableDate);
997 
998                                     // 4b- Modify the service.
999                                     ChangeServiceConfigW(hService, SERVICE_NO_CHANGE, SERVICE_DISABLED, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1000 
1001                                     // 5b- Register the service into the registry.
1002                                     CAtlStringW serviceRegKey(L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services\\");
1003                                     serviceRegKey += svcName;
1004                                     HKEY hSubKey = NULL;
1005                                     if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, serviceRegKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
1006                                     {
1007                                         RegSetDWORDValue(hSubKey, NULL, svcName, FALSE, lpServiceConfig->dwStartType);
1008 
1009                                     #if 1 // DisableDate
1010                                         RegSetDWORDValue(hSubKey, NULL, L"DAY"   , FALSE, disableDate.wDay   );
1011                                         RegSetDWORDValue(hSubKey, NULL, L"HOUR"  , FALSE, disableDate.wHour  );
1012                                         RegSetDWORDValue(hSubKey, NULL, L"MINUTE", FALSE, disableDate.wMinute);
1013                                         RegSetDWORDValue(hSubKey, NULL, L"MONTH" , FALSE, disableDate.wMonth );
1014                                         RegSetDWORDValue(hSubKey, NULL, L"SECOND", FALSE, disableDate.wSecond);
1015                                         RegSetDWORDValue(hSubKey, NULL, L"YEAR"  , FALSE, disableDate.wYear  );
1016                                     #endif
1017 
1018                                         RegCloseKey(hSubKey);
1019                                     }
1020 
1021                                     /***** HACK for Windows < Vista (e.g. 2000, Xp, 2003...) *****/
1022                                     //
1023                                     // Save also a valued-entry for the service.
1024                                     //
1025                                     if (bIsWindows && bIsPreVistaOSVersion)
1026                                     {
1027                                         RegSetDWORDValue(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", svcName, TRUE, lpServiceConfig->dwStartType);
1028                                     }
1029                                     /*************************************************************/
1030 
1031                                     ////////// HACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACK ///////////
1032                                     // userModificationsList.RemoveAt(it);
1033                                 }
1034 
1035                                 MemFree(lpServiceConfig);
1036                                 CloseServiceHandle(hService);
1037                             }
1038 
1039                             //////////// HACK HACK !!!! ////////////
1040                             userModificationsList.RemoveAll();
1041                             ////////////////////////////////////////
1042 
1043                             CloseServiceHandle(hSCManager);
1044 
1045 
1046                             //// PropSheet_UnChanged(GetParent(hServicesPage), hServicesPage); ////
1047                             PropSheet_CancelToClose(GetParent(hDlg));
1048 
1049                             /* Modifications are OK */
1050                             SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, PSNRET_NOERROR);
1051                         }
1052                         else
1053                         {
1054                             MessageBox(hDlg, _T("Impossible to open the SC manager..."), _T("Error"), MB_ICONERROR);
1055 
1056                             // There was an error...
1057                             SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, PSNRET_INVALID);
1058                         }
1059 
1060                         GetServices(bMaskProprietarySvcs);
1061                         Update_Btn_States(hDlg);
1062 
1063                         return TRUE;
1064                     }
1065 
1066                     case PSN_HELP:
1067                     {
1068                         MessageBox(hServicesPage, _T("Help not implemented yet!"), _T("Help"), MB_ICONINFORMATION | MB_OK);
1069                         return TRUE;
1070                     }
1071 
1072                     case PSN_KILLACTIVE: // Is going to lose activation.
1073                     {
1074                         // Changes are always valid of course.
1075                         SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, FALSE);
1076                         return TRUE;
1077                     }
1078 
1079                     case PSN_QUERYCANCEL:
1080                     {
1081                         // RefreshStartupList();
1082 
1083                         // Allows cancellation.
1084                         SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, FALSE);
1085 
1086                         return TRUE;
1087                     }
1088 
1089                     case PSN_QUERYINITIALFOCUS:
1090                     {
1091                         // Give the focus on and select the first item.
1092                         ListView_SetItemState(hServicesListCtrl, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
1093 
1094                         SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, (LONG_PTR)hServicesListCtrl);
1095                         return TRUE;
1096                     }
1097 
1098                     //
1099                     // DO NOT TOUCH THESE NEXT MESSAGES, THEY ARE OK LIKE THIS...
1100                     //
1101                     case PSN_RESET: // Perform final cleaning, called before WM_DESTROY.
1102                         return TRUE;
1103 
1104                     case PSN_SETACTIVE: // Is going to gain activation.
1105                     {
1106                         SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, 0);
1107                         return TRUE;
1108                     }
1109 
1110                     default:
1111                         break;
1112                 }
1113             }
1114         }
1115 
1116         default:
1117             return FALSE;
1118     }
1119 
1120     return FALSE;
1121 }
1122 
1123 }
1124