Move group-list related code to a separate file and keep the group name in the group...
[reactos.git] / reactos / subsys / system / services / database.c
index 404ba3d..34bf1eb 100644 (file)
@@ -1,9 +1,9 @@
-/* $Id: database.c,v 1.18 2004/09/20 19:47:25 hbirr Exp $
+/*
  *
  * service control manager
- * 
+ *
  * ReactOS Operating System
- * 
+ *
  * --------------------------------------------------------------------
  *
  * This software is free software; you can redistribute it and/or
 
 /* INCLUDES *****************************************************************/
 
-#define NTOS_MODE_USER
-#include <ntos.h>
-#include <rosrtl/string.h>
-
-#include <windows.h>
-#include <tchar.h>
-
 #include "services.h"
 
 #define NDEBUG
 #include <debug.h>
 
 
-/* TYPES *********************************************************************/
+/* GLOBALS *******************************************************************/
 
-typedef struct _SERVICE_GROUP
-{
-  LIST_ENTRY GroupListEntry;
-  UNICODE_STRING GroupName;
+LIST_ENTRY ServiceListHead;
 
-  BOOLEAN ServicesRunning;
-  ULONG TagCount;
-  PULONG TagArray;
+static RTL_RESOURCE DatabaseLock;
+static DWORD dwResumeCount = 1;
 
-} SERVICE_GROUP, *PSERVICE_GROUP;
 
+/* FUNCTIONS *****************************************************************/
 
-typedef struct _SERVICE
+PSERVICE
+ScmGetServiceEntryByName(LPWSTR lpServiceName)
 {
-  LIST_ENTRY ServiceListEntry;
-  UNICODE_STRING ServiceName;
-  UNICODE_STRING RegistryPath;
-  UNICODE_STRING ServiceGroup;
-
-  ULONG Start;
-  ULONG Type;
-  ULONG ErrorControl;
-  ULONG Tag;
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
 
-  BOOLEAN ServiceRunning;
-  BOOLEAN ServiceVisited;
-
-  HANDLE ControlPipeHandle;
-  ULONG ProcessId;
-  ULONG ThreadId;
-} SERVICE, *PSERVICE;
+    DPRINT("ScmGetServiceEntryByName() called\n");
 
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
+    {
+        CurrentService = CONTAINING_RECORD(ServiceEntry,
+                                           SERVICE,
+                                           ServiceListEntry);
+        if (_wcsicmp(CurrentService->lpServiceName, lpServiceName) == 0)
+        {
+            DPRINT("Found service: '%S'\n", CurrentService->lpServiceName);
+            return CurrentService;
+        }
 
-/* GLOBALS *******************************************************************/
+        ServiceEntry = ServiceEntry->Flink;
+    }
 
-LIST_ENTRY GroupListHead;
-LIST_ENTRY ServiceListHead;
+    DPRINT("Couldn't find a matching service\n");
 
+    return NULL;
+}
 
-/* FUNCTIONS *****************************************************************/
 
-static NTSTATUS STDCALL 
-CreateGroupOrderListRoutine(PWSTR ValueName,
-                           ULONG ValueType,
-                           PVOID ValueData,
-                           ULONG ValueLength,
-                           PVOID Context,
-                           PVOID EntryContext)
+PSERVICE
+ScmGetServiceEntryByDisplayName(LPWSTR lpDisplayName)
 {
-  PSERVICE_GROUP Group;
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
 
-  DPRINT("IopGetGroupOrderList(%S, %x, %x, %x, %x, %x)\n",
-         ValueName, ValueType, ValueData, ValueLength, Context, EntryContext);
+    DPRINT("ScmGetServiceEntryByDisplayName() called\n");
 
-  if (ValueType == REG_BINARY &&
-      ValueData != NULL &&
-      ValueLength >= sizeof(DWORD) &&
-      ValueLength >= (*(PULONG)ValueData + 1) * sizeof(DWORD))
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
     {
-      Group = (PSERVICE_GROUP)Context;
-      Group->TagCount = ((PULONG)ValueData)[0];
-      if (Group->TagCount > 0)
+        CurrentService = CONTAINING_RECORD(ServiceEntry,
+                                           SERVICE,
+                                           ServiceListEntry);
+        if (_wcsicmp(CurrentService->lpDisplayName, lpDisplayName) == 0)
         {
-         if (ValueLength >= (Group->TagCount + 1) * sizeof(DWORD))
-            {
-              Group->TagArray = (PULONG)HeapAlloc(GetProcessHeap(),
-                                                 HEAP_ZERO_MEMORY,
-                                                 Group->TagCount * sizeof(DWORD));
-             if (Group->TagArray == NULL)
-               {
-                 Group->TagCount = 0;
-                 return STATUS_INSUFFICIENT_RESOURCES;
-               }
-             RtlCopyMemory(Group->TagArray, 
-                           (PULONG)ValueData + 1, 
-                           Group->TagCount * sizeof(DWORD));
-           }
-         else
-           {
-             Group->TagCount = 0;
-             return STATUS_UNSUCCESSFUL;
-           }
-       }
+            DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
+            return CurrentService;
+        }
+
+        ServiceEntry = ServiceEntry->Flink;
     }
-  return STATUS_SUCCESS;
+
+    DPRINT("Couldn't find a matching service\n");
+
+    return NULL;
 }
 
-static NTSTATUS STDCALL
-CreateGroupListRoutine(PWSTR ValueName,
-                      ULONG ValueType,
-                      PVOID ValueData,
-                      ULONG ValueLength,
-                      PVOID Context,
-                      PVOID EntryContext)
+
+PSERVICE
+ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
 {
-  PSERVICE_GROUP Group;
-  RTL_QUERY_REGISTRY_TABLE QueryTable[2];
-  NTSTATUS Status;
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
 
-  if (ValueType == REG_SZ)
+    DPRINT("ScmGetServiceEntryByResumeCount() called\n");
+
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
     {
-      DPRINT("Data: '%S'\n", (PWCHAR)ValueData);
-
-      Group = (PSERVICE_GROUP)HeapAlloc(GetProcessHeap(),
-                                       HEAP_ZERO_MEMORY,
-                                       sizeof(SERVICE_GROUP));
-      if (Group == NULL)
-       {
-         return(STATUS_INSUFFICIENT_RESOURCES);
-       }
-
-      if (!RtlCreateUnicodeString(&Group->GroupName,
-                                 (PWSTR)ValueData))
-       {
-         return(STATUS_INSUFFICIENT_RESOURCES);
-       }
-      
-      RtlZeroMemory(&QueryTable, sizeof(QueryTable));
-      QueryTable[0].Name = (PWSTR)ValueData;
-      QueryTable[0].QueryRoutine = CreateGroupOrderListRoutine;
-
-      Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
-                                     L"GroupOrderList",
-                                     QueryTable,
-                                     (PVOID)Group,
-                                     NULL);
-      DPRINT("%x %d %S\n", Status, Group->TagCount, (PWSTR)ValueData);
-
-
-      InsertTailList(&GroupListHead,
-                    &Group->GroupListEntry);
+        CurrentService = CONTAINING_RECORD(ServiceEntry,
+                                           SERVICE,
+                                           ServiceListEntry);
+        if (CurrentService->dwResumeCount > dwResumeCount)
+        {
+            DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
+            return CurrentService;
+        }
+
+        ServiceEntry = ServiceEntry->Flink;
     }
 
-  return(STATUS_SUCCESS);
+    DPRINT("Couldn't find a matching service\n");
+
+    return NULL;
 }
 
 
-static NTSTATUS STDCALL
-CreateServiceListEntry(PUNICODE_STRING ServiceName)
+DWORD
+ScmCreateNewServiceRecord(LPWSTR lpServiceName,
+                          PSERVICE *lpServiceRecord)
 {
-  RTL_QUERY_REGISTRY_TABLE QueryTable[6];
-  PSERVICE Service = NULL;
-  NTSTATUS Status;
+    PSERVICE lpService = NULL;
 
-  DPRINT("Service: '%wZ'\n", ServiceName);
+    DPRINT("Service: '%S'\n", lpServiceName);
 
-  /* Allocate service entry */
-  Service = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
-                     sizeof(SERVICE));
-  if (Service == NULL)
-    {
-      return(STATUS_INSUFFICIENT_RESOURCES);
-    }
+    /* Allocate service entry */
+    lpService = HeapAlloc(GetProcessHeap(),
+                          HEAP_ZERO_MEMORY,
+                          sizeof(SERVICE) + ((wcslen(lpServiceName) + 1) * sizeof(WCHAR)));
+    if (lpService == NULL)
+        return ERROR_NOT_ENOUGH_MEMORY;
 
-  /* Copy service name */
-  Service->ServiceName.Length = ServiceName->Length;
-  Service->ServiceName.MaximumLength = ServiceName->Length + sizeof(WCHAR);
-  Service->ServiceName.Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
-                                         Service->ServiceName.MaximumLength);
-  if (Service->ServiceName.Buffer == NULL)
-    {
-      HeapFree(GetProcessHeap(), 0, Service);
-      return(STATUS_INSUFFICIENT_RESOURCES);
-    }
-  RtlCopyMemory(Service->ServiceName.Buffer,
-               ServiceName->Buffer,
-               ServiceName->Length);
-  Service->ServiceName.Buffer[ServiceName->Length / sizeof(WCHAR)] = 0;
-
-  /* Build registry path */
-  Service->RegistryPath.MaximumLength = MAX_PATH * sizeof(WCHAR);
-  Service->RegistryPath.Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
-                                          MAX_PATH * sizeof(WCHAR));
-  if (Service->ServiceName.Buffer == NULL)
+    *lpServiceRecord = lpService;
+
+    /* Copy service name */
+    wcscpy(lpService->szServiceName, lpServiceName);
+    lpService->lpServiceName = lpService->szServiceName;
+    lpService->lpDisplayName = lpService->lpServiceName;
+
+    /* Set the resume count */
+    lpService->dwResumeCount = dwResumeCount++;
+
+    /* Append service entry */
+    InsertTailList(&ServiceListHead,
+                   &lpService->ServiceListEntry);
+
+    lpService->Status.dwCurrentState = SERVICE_STOPPED;
+    lpService->Status.dwControlsAccepted = 0;
+    lpService->Status.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED;
+    lpService->Status.dwServiceSpecificExitCode = 0;
+    lpService->Status.dwCheckPoint = 0;
+    lpService->Status.dwWaitHint = 2000; /* 2 seconds */
+
+    return ERROR_SUCCESS;
+}
+
+
+static DWORD
+CreateServiceListEntry(LPWSTR lpServiceName,
+                       HKEY hServiceKey)
+{
+    PSERVICE lpService = NULL;
+    LPWSTR lpDisplayName = NULL;
+    LPWSTR lpGroup = NULL;
+    DWORD dwSize;
+    DWORD dwError;
+    DWORD dwServiceType;
+    DWORD dwStartType;
+    DWORD dwErrorControl;
+    DWORD dwTagId;
+
+    DPRINT("Service: '%S'\n", lpServiceName);
+    if (*lpServiceName == L'{')
+        return ERROR_SUCCESS;
+
+    dwSize = sizeof(DWORD);
+    dwError = RegQueryValueExW(hServiceKey,
+                               L"Type",
+                               NULL,
+                               NULL,
+                               (LPBYTE)&dwServiceType,
+                               &dwSize);
+    if (dwError != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    if (((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
+        ((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS) &&
+        (dwServiceType != SERVICE_KERNEL_DRIVER) &&
+        (dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
+        return ERROR_SUCCESS;
+
+    DPRINT("Service type: %lx\n", dwServiceType);
+
+    dwSize = sizeof(DWORD);
+    dwError = RegQueryValueExW(hServiceKey,
+                               L"Start",
+                               NULL,
+                               NULL,
+                               (LPBYTE)&dwStartType,
+                               &dwSize);
+    if (dwError != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    DPRINT("Start type: %lx\n", dwStartType);
+
+    dwSize = sizeof(DWORD);
+    dwError = RegQueryValueExW(hServiceKey,
+                               L"ErrorControl",
+                               NULL,
+                               NULL,
+                               (LPBYTE)&dwErrorControl,
+                               &dwSize);
+    if (dwError != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    DPRINT("Error control: %lx\n", dwErrorControl);
+
+    dwError = RegQueryValueExW(hServiceKey,
+                               L"Tag",
+                               NULL,
+                               NULL,
+                               (LPBYTE)&dwTagId,
+                               &dwSize);
+    if (dwError != ERROR_SUCCESS)
+        dwTagId = 0;
+
+    DPRINT("Tag: %lx\n", dwTagId);
+
+    dwError = ScmReadString(hServiceKey,
+                            L"Group",
+                            &lpGroup);
+    if (dwError != ERROR_SUCCESS)
+        lpGroup = NULL;
+
+    DPRINT("Group: %S\n", lpGroup);
+
+    dwError = ScmReadString(hServiceKey,
+                            L"DisplayName",
+                            &lpDisplayName);
+    if (dwError != ERROR_SUCCESS)
+        lpDisplayName = NULL;
+
+    DPRINT("Display name: %S\n", lpDisplayName);
+
+    dwError = ScmCreateNewServiceRecord(lpServiceName,
+                                        &lpService);
+    if (dwError != ERROR_SUCCESS)
+        goto done;
+
+    lpService->Status.dwServiceType = dwServiceType;
+    lpService->dwStartType = dwStartType;
+    lpService->dwErrorControl = dwErrorControl;
+    lpService->dwTag = dwTagId;
+
+    if (lpGroup != NULL)
     {
-      HeapFree(GetProcessHeap(), 0, Service->ServiceName.Buffer);
-      HeapFree(GetProcessHeap(), 0, Service);
-      return(STATUS_INSUFFICIENT_RESOURCES);
+        lpService->lpServiceGroup = lpGroup;
+        lpGroup = NULL;
     }
-  wcscpy(Service->RegistryPath.Buffer,
-        L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
-  wcscat(Service->RegistryPath.Buffer,
-        Service->ServiceName.Buffer);
-  Service->RegistryPath.Length = wcslen(Service->RegistryPath.Buffer) * sizeof(WCHAR);
-
-  /* Get service data */
-  RtlZeroMemory(&QueryTable,
-               sizeof(QueryTable));
-
-  QueryTable[0].Name = L"Start";
-  QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-  QueryTable[0].EntryContext = &Service->Start;
-
-  QueryTable[1].Name = L"Type";
-  QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-  QueryTable[1].EntryContext = &Service->Type;
-
-  QueryTable[2].Name = L"ErrorControl";
-  QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-  QueryTable[2].EntryContext = &Service->ErrorControl;
-
-  QueryTable[3].Name = L"Group";
-  QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
-  QueryTable[3].EntryContext = &Service->ServiceGroup;
-
-  QueryTable[4].Name = L"Tag";
-  QueryTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
-  QueryTable[4].EntryContext = &Service->Tag;
-
-  Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
-                                 ServiceName->Buffer,
-                                 QueryTable,
-                                 NULL,
-                                 NULL);
-  if (!NT_SUCCESS(Status))
+
+    if (lpDisplayName != NULL)
     {
-      PrintString("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
-      RtlFreeUnicodeString(&Service->RegistryPath);
-      RtlFreeUnicodeString(&Service->ServiceName);
-      HeapFree(GetProcessHeap(), 0, Service);
-      return(Status);
+        lpService->lpDisplayName = lpDisplayName;
+        lpDisplayName = NULL;
     }
 
-  DPRINT("ServiceName: '%wZ'\n", &Service->ServiceName);
-  DPRINT("RegistryPath: '%wZ'\n", &Service->RegistryPath);
-  DPRINT("ServiceGroup: '%wZ'\n", &Service->ServiceGroup);
-  DPRINT("Start %lx  Type %lx  Tag %lx  ErrorControl %lx\n",
-        Service->Start, Service->Type, Service->Tag, Service->ErrorControl);
+    DPRINT("ServiceName: '%S'\n", lpService->lpServiceName);
+    DPRINT("Group: '%S'\n", lpService->lpServiceGroup);
+    DPRINT("Start %lx  Type %lx  Tag %lx  ErrorControl %lx\n",
+           lpService->dwStartType,
+           lpService->Status.dwServiceType,
+           lpService->dwTag,
+           lpService->dwErrorControl);
+
+    if (ScmIsDeleteFlagSet(hServiceKey))
+        lpService->bDeleted = TRUE;
 
-  /* Append service entry */
-  InsertTailList(&ServiceListHead,
-                &Service->ServiceListEntry);
+done:;
+    if (lpGroup != NULL)
+        HeapFree(GetProcessHeap(), 0, lpGroup);
 
-  return(STATUS_SUCCESS);
+    if (lpDisplayName != NULL)
+        HeapFree(GetProcessHeap(), 0, lpDisplayName);
+
+    return dwError;
 }
 
 
-NTSTATUS
-ScmCreateServiceDataBase(VOID)
+VOID
+ScmDeleteMarkedServices(VOID)
 {
-  RTL_QUERY_REGISTRY_TABLE QueryTable[2];
-  OBJECT_ATTRIBUTES ObjectAttributes;
-  UNICODE_STRING ServicesKeyName;
-  UNICODE_STRING SubKeyName;
-  HKEY ServicesKey;
-  ULONG Index;
-  NTSTATUS Status;
-
-  PKEY_BASIC_INFORMATION KeyInfo = NULL;
-  ULONG KeyInfoLength = 0;
-  ULONG ReturnedLength;
-
-  DPRINT("ScmCreateServiceDataBase() called\n");
-
-  /* Initialize basic variables */
-  InitializeListHead(&GroupListHead);
-  InitializeListHead(&ServiceListHead);
-
-  /* Build group order list */
-  RtlZeroMemory(&QueryTable,
-               sizeof(QueryTable));
-
-  QueryTable[0].Name = L"List";
-  QueryTable[0].QueryRoutine = CreateGroupListRoutine;
-
-  Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
-                                 L"ServiceGroupOrder",
-                                 QueryTable,
-                                 NULL,
-                                 NULL);
-  if (!NT_SUCCESS(Status))
-    return(Status);
-
-  RtlRosInitUnicodeStringFromLiteral(&ServicesKeyName,
-                      L"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
-
-  InitializeObjectAttributes(&ObjectAttributes,
-                            &ServicesKeyName,
-                            OBJ_CASE_INSENSITIVE,
-                            NULL,
-                            NULL);
-
-  Status = RtlpNtOpenKey(&ServicesKey,
-                        0x10001,
-                        &ObjectAttributes,
-                        0);
-  if (!NT_SUCCESS(Status))
-    return(Status);
-
-  /* Allocate key info buffer */
-  KeyInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(WCHAR);
-  KeyInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, KeyInfoLength);
-  if (KeyInfo == NULL)
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
+
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
     {
-      NtClose(ServicesKey);
-      return(STATUS_INSUFFICIENT_RESOURCES);
+        CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+
+        ServiceEntry = ServiceEntry->Flink;
+
+        if (CurrentService->bDeleted == TRUE)
+        {
+            DPRINT1("Delete service: %S\n", CurrentService->lpServiceName);
+
+            /* FIXME: Delete the registry keys */
+
+            /* FIXME: Delete the service record from the list */
+
+        }
     }
+}
+
 
-  Index = 0;
-  while (TRUE)
+DWORD
+ScmCreateServiceDatabase(VOID)
+{
+    WCHAR szSubKey[MAX_PATH];
+    HKEY hServicesKey;
+    HKEY hServiceKey;
+    DWORD dwSubKey;
+    DWORD dwSubKeyLength;
+    FILETIME ftLastChanged;
+    DWORD dwError;
+
+    DPRINT("ScmCreateServiceDatabase() called\n");
+
+    dwError = ScmCreateGroupList();
+    if (dwError != ERROR_SUCCESS)
+        return dwError;
+
+    /* Initialize basic variables */
+    InitializeListHead(&ServiceListHead);
+
+    /* Initialize the database lock */
+    RtlInitializeResource(&DatabaseLock);
+
+    dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                            L"System\\CurrentControlSet\\Services",
+                            0,
+                            KEY_READ,
+                            &hServicesKey);
+    if (dwError != ERROR_SUCCESS)
+        return dwError;
+
+    dwSubKey = 0;
+    for (;;)
     {
-      Status = NtEnumerateKey(ServicesKey,
-                             Index,
-                             KeyBasicInformation,
-                             KeyInfo,
-                             KeyInfoLength,
-                             &ReturnedLength);
-      if (NT_SUCCESS(Status))
-       {
-         if (KeyInfo->NameLength < MAX_PATH * sizeof(WCHAR))
-           {
-
-             SubKeyName.Length = KeyInfo->NameLength;
-             SubKeyName.MaximumLength = KeyInfo->NameLength + sizeof(WCHAR);
-             SubKeyName.Buffer = KeyInfo->Name;
-             SubKeyName.Buffer[SubKeyName.Length / sizeof(WCHAR)] = 0;
-
-             DPRINT("KeyName: '%wZ'\n", &SubKeyName);
-             Status = CreateServiceListEntry(&SubKeyName);
-           }
-       }
-
-      if (!NT_SUCCESS(Status))
-       break;
-
-      Index++;
+        dwSubKeyLength = MAX_PATH;
+        dwError = RegEnumKeyExW(hServicesKey,
+                                dwSubKey,
+                                szSubKey,
+                                &dwSubKeyLength,
+                                NULL,
+                                NULL,
+                                NULL,
+                                &ftLastChanged);
+        if (dwError == ERROR_SUCCESS &&
+            szSubKey[0] != L'{')
+        {
+            DPRINT("SubKeyName: '%S'\n", szSubKey);
+
+            dwError = RegOpenKeyExW(hServicesKey,
+                                    szSubKey,
+                                    0,
+                                    KEY_READ,
+                                    &hServiceKey);
+            if (dwError == ERROR_SUCCESS)
+            {
+                dwError = CreateServiceListEntry(szSubKey,
+                                                 hServiceKey);
+
+                RegCloseKey(hServiceKey);
+            }
+        }
+
+        if (dwError != ERROR_SUCCESS)
+            break;
+
+        dwSubKey++;
     }
 
-  HeapFree(GetProcessHeap(), 0, KeyInfo);
-  NtClose(ServicesKey);
+    RegCloseKey(hServicesKey);
+
+    /* Delete services that are marked for delete */
+    ScmDeleteMarkedServices();
 
-  DPRINT("ScmCreateServiceDataBase() done\n");
+    DPRINT("ScmCreateServiceDatabase() done\n");
 
-  return(STATUS_SUCCESS);
+    return ERROR_SUCCESS;
 }
 
 
 static NTSTATUS
 ScmCheckDriver(PSERVICE Service)
 {
-  OBJECT_ATTRIBUTES ObjectAttributes;
-  UNICODE_STRING DirName;
-  HANDLE DirHandle;
-  NTSTATUS Status;
-  PDIRECTORY_BASIC_INFORMATION DirInfo;
-  ULONG BufferLength;
-  ULONG DataLength;
-  ULONG Index;
-  PLIST_ENTRY GroupEntry;
-  PSERVICE_GROUP CurrentGroup;
-
-  DPRINT("ScmCheckDriver(%wZ) called\n", &Service->ServiceName);
-
-  if (Service->Type == SERVICE_KERNEL_DRIVER)
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING DirName;
+    HANDLE DirHandle;
+    NTSTATUS Status;
+    POBJECT_DIRECTORY_INFORMATION DirInfo;
+    ULONG BufferLength;
+    ULONG DataLength;
+    ULONG Index;
+    PLIST_ENTRY GroupEntry;
+    PSERVICE_GROUP CurrentGroup;
+
+    DPRINT("ScmCheckDriver(%S) called\n", Service->lpServiceName);
+
+    if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
     {
-      RtlRosInitUnicodeStringFromLiteral(&DirName,
-                          L"\\Driver");
+        RtlInitUnicodeString(&DirName,
+                             L"\\Driver");
     }
-  else
+    else
     {
-      RtlRosInitUnicodeStringFromLiteral(&DirName,
-                          L"\\FileSystem");
+        RtlInitUnicodeString(&DirName,
+                             L"\\FileSystem");
     }
 
-  InitializeObjectAttributes(&ObjectAttributes,
-                            &DirName,
-                            0,
-                            NULL,
-                            NULL);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &DirName,
+                               0,
+                               NULL,
+                               NULL);
 
-  Status = NtOpenDirectoryObject(&DirHandle,
-                                DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
-                                &ObjectAttributes);
-  if (!NT_SUCCESS(Status))
+    Status = NtOpenDirectoryObject(&DirHandle,
+                                   DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
+                                   &ObjectAttributes);
+    if (!NT_SUCCESS(Status))
     {
-      return(Status);
+        return Status;
     }
 
-  BufferLength = sizeof(DIRECTORY_BASIC_INFORMATION) +
-                2 * MAX_PATH * sizeof(WCHAR);
-  DirInfo = HeapAlloc(GetProcessHeap(),
-                     HEAP_ZERO_MEMORY,
-                     BufferLength);
+    BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
+                   2 * MAX_PATH * sizeof(WCHAR);
+    DirInfo = HeapAlloc(GetProcessHeap(),
+                        HEAP_ZERO_MEMORY,
+                        BufferLength);
 
-  Index = 0;
-  while (TRUE)
+    Index = 0;
+    while (TRUE)
     {
-      Status = NtQueryDirectoryObject(DirHandle,
-                                     DirInfo,
-                                     BufferLength,
-                                     TRUE,
-                                     FALSE,
-                                     &Index,
-                                     &DataLength);
-      if (Status == STATUS_NO_MORE_ENTRIES)
-       {
-         /* FIXME: Add current service to 'failed service' list */
-         DPRINT("Service '%wZ' failed\n", &Service->ServiceName);
-         break;
-       }
-
-      if (!NT_SUCCESS(Status))
-       break;
-
-      DPRINT("Comparing: '%wZ'  '%wZ'\n", &Service->ServiceName, &DirInfo->ObjectName);
-
-      if (RtlEqualUnicodeString(&Service->ServiceName, &DirInfo->ObjectName, TRUE))
-       {
-         DPRINT("Found: '%wZ'  '%wZ'\n", &Service->ServiceName, &DirInfo->ObjectName);
-
-         /* Mark service as 'running' */
-         Service->ServiceRunning = TRUE;
-
-         /* Find the driver's group and mark it as 'running' */
-         if (Service->ServiceGroup.Buffer != NULL)
-           {
-             GroupEntry = GroupListHead.Flink;
-             while (GroupEntry != &GroupListHead)
-               {
-                 CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
-
-                 DPRINT("Checking group '%wZ'\n", &CurrentGroup->GroupName);
-                 if (RtlEqualUnicodeString(&Service->ServiceGroup, &CurrentGroup->GroupName, TRUE))
-                   {
-                     CurrentGroup->ServicesRunning = TRUE;
-                   }
-
-                 GroupEntry = GroupEntry->Flink;
-               }
-           }
-         break;
-       }
+        Status = NtQueryDirectoryObject(DirHandle,
+                                        DirInfo,
+                                        BufferLength,
+                                        TRUE,
+                                        FALSE,
+                                        &Index,
+                                        &DataLength);
+        if (Status == STATUS_NO_MORE_ENTRIES)
+        {
+            /* FIXME: Add current service to 'failed service' list */
+            DPRINT("Service '%S' failed\n", Service->lpServiceName);
+            break;
+        }
+
+        if (!NT_SUCCESS(Status))
+            break;
+
+        DPRINT("Comparing: '%S'  '%wZ'\n", Service->lpServiceName, &DirInfo->ObjectName);
+
+        if (_wcsicmp(Service->lpServiceName, DirInfo->ObjectName.Buffer) == 0)
+        {
+            DPRINT("Found: '%S'  '%wZ'\n",
+                   Service->lpServiceName, &DirInfo->ObjectName);
+
+            /* Mark service as 'running' */
+            Service->Status.dwCurrentState = SERVICE_RUNNING;
+
+            /* Find the driver's group and mark it as 'running' */
+            if (Service->lpServiceGroup != NULL)
+            {
+                GroupEntry = GroupListHead.Flink;
+                while (GroupEntry != &GroupListHead)
+                {
+                    CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
+
+                    DPRINT("Checking group '%S'\n", &CurrentGroup->lpGroupName);
+                    if (Service->lpServiceGroup != NULL &&
+                        _wcsicmp(Service->lpServiceGroup, CurrentGroup->lpGroupName) == 0)
+                    {
+                        CurrentGroup->ServicesRunning = TRUE;
+                    }
+
+                    GroupEntry = GroupEntry->Flink;
+                }
+            }
+            break;
+        }
     }
 
-  HeapFree(GetProcessHeap(),
-          0,
-          DirInfo);
-  NtClose(DirHandle);
+    HeapFree(GetProcessHeap(),
+             0,
+             DirInfo);
+    NtClose(DirHandle);
 
-  return(STATUS_SUCCESS);
+    return STATUS_SUCCESS;
 }
 
 
 VOID
 ScmGetBootAndSystemDriverState(VOID)
 {
-  PLIST_ENTRY ServiceEntry;
-  PSERVICE CurrentService;
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
 
-  DPRINT("ScmGetBootAndSystemDriverState() called\n");
+    DPRINT("ScmGetBootAndSystemDriverState() called\n");
 
-  ServiceEntry = ServiceListHead.Flink;
-  while (ServiceEntry != &ServiceListHead)
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
     {
-      CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+        CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+
+        if (CurrentService->dwStartType == SERVICE_BOOT_START ||
+            CurrentService->dwStartType == SERVICE_SYSTEM_START)
+        {
+            /* Check driver */
+            DPRINT("  Checking service: %S\n", CurrentService->lpServiceName);
 
-      if (CurrentService->Start == SERVICE_BOOT_START ||
-         CurrentService->Start == SERVICE_SYSTEM_START)
-       {
-         /* Check driver */
-         DPRINT("  Checking service: %wZ\n", &CurrentService->ServiceName);
+            ScmCheckDriver(CurrentService);
+        }
 
-         ScmCheckDriver(CurrentService);
-       }
-      ServiceEntry = ServiceEntry->Flink;
+        ServiceEntry = ServiceEntry->Flink;
     }
 
-  DPRINT("ScmGetBootAndSystemDriverState() done\n");
+    DPRINT("ScmGetBootAndSystemDriverState() done\n");
+}
+
+
+static NTSTATUS
+ScmSendStartCommand(PSERVICE Service, LPWSTR Arguments)
+{
+    PSCM_START_PACKET StartPacket;
+    DWORD TotalLength;
+#if 0
+    DWORD Length;
+#endif
+    PWSTR Ptr;
+    DWORD Count;
+
+    DPRINT("ScmSendStartCommand() called\n");
+
+    /* Calculate the total length of the start command line */
+    TotalLength = wcslen(Service->lpServiceName) + 1;
+#if 0
+    if (Arguments != NULL)
+    {
+        Ptr = Arguments;
+        while (*Ptr)
+        {
+            Length = wcslen(Ptr) + 1;
+            TotalLength += Length;
+            Ptr += Length;
+        }
+    }
+#endif
+    TotalLength++;
+
+    /* Allocate start command packet */
+    StartPacket = HeapAlloc(GetProcessHeap(),
+                            HEAP_ZERO_MEMORY,
+                            sizeof(SCM_START_PACKET) + (TotalLength - 1) * sizeof(WCHAR));
+    if (StartPacket == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    StartPacket->Command = SCM_START_COMMAND;
+    StartPacket->Size = TotalLength;
+    Ptr = &StartPacket->Arguments[0];
+    wcscpy(Ptr, Service->lpServiceName);
+    Ptr += (wcslen(Service->lpServiceName) + 1);
+
+    /* FIXME: Copy argument list */
+
+    *Ptr = 0;
+
+    /* Send the start command */
+    WriteFile(Service->ControlPipeHandle,
+              StartPacket,
+              sizeof(SCM_START_PACKET) + (TotalLength - 1) * sizeof(WCHAR),
+              &Count,
+              NULL);
+
+    /* FIXME: Read the reply */
+
+    HeapFree(GetProcessHeap(),
+             0,
+             StartPacket);
+
+    DPRINT("ScmSendStartCommand() done\n");
+
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS
+ScmStartUserModeService(PSERVICE Service)
+{
+    RTL_QUERY_REGISTRY_TABLE QueryTable[3];
+    PROCESS_INFORMATION ProcessInformation;
+    STARTUPINFOW StartupInfo;
+    UNICODE_STRING ImagePath;
+    ULONG Type;
+    BOOL Result;
+    NTSTATUS Status;
+
+    RtlInitUnicodeString(&ImagePath, NULL);
+
+    /* Get service data */
+    RtlZeroMemory(&QueryTable,
+                  sizeof(QueryTable));
+
+    QueryTable[0].Name = L"Type";
+    QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
+    QueryTable[0].EntryContext = &Type;
+
+    QueryTable[1].Name = L"ImagePath";
+    QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
+    QueryTable[1].EntryContext = &ImagePath;
+
+    Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
+                                    Service->lpServiceName,
+                                    QueryTable,
+                                    NULL,
+                                    NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
+        return Status;
+    }
+    DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
+    DPRINT("Type: %lx\n", Type);
+
+    /* Create '\\.\pipe\net\NtControlPipe' instance */
+    Service->ControlPipeHandle = CreateNamedPipeW(L"\\\\.\\pipe\\net\\NtControlPipe",
+                                                  PIPE_ACCESS_DUPLEX,
+                                                  PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
+                                                  100,
+                                                  8000,
+                                                  4,
+                                                  30000,
+                                                  NULL);
+    DPRINT("CreateNamedPipeW() done\n");
+    if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE)
+    {
+        DPRINT1("Failed to create control pipe!\n");
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    StartupInfo.cb = sizeof(StartupInfo);
+    StartupInfo.lpReserved = NULL;
+    StartupInfo.lpDesktop = NULL;
+    StartupInfo.lpTitle = NULL;
+    StartupInfo.dwFlags = 0;
+    StartupInfo.cbReserved2 = 0;
+    StartupInfo.lpReserved2 = 0;
+
+    Result = CreateProcessW(ImagePath.Buffer,
+                            NULL,
+                            NULL,
+                            NULL,
+                            FALSE,
+                            DETACHED_PROCESS | CREATE_SUSPENDED,
+                            NULL,
+                            NULL,
+                            &StartupInfo,
+                            &ProcessInformation);
+    RtlFreeUnicodeString(&ImagePath);
+
+    if (!Result)
+    {
+        /* Close control pipe */
+        CloseHandle(Service->ControlPipeHandle);
+        Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
+
+        DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    DPRINT("Process Id: %lu  Handle %lx\n",
+           ProcessInformation.dwProcessId,
+           ProcessInformation.hProcess);
+    DPRINT("Thread Id: %lu  Handle %lx\n",
+           ProcessInformation.dwThreadId,
+           ProcessInformation.hThread);
+
+    /* Get process and thread ids */
+    Service->ProcessId = ProcessInformation.dwProcessId;
+    Service->ThreadId = ProcessInformation.dwThreadId;
+
+    /* Resume Thread */
+    ResumeThread(ProcessInformation.hThread);
+
+    /* Connect control pipe */
+    if (ConnectNamedPipe(Service->ControlPipeHandle, NULL))
+    {
+        DWORD dwProcessId = 0;
+        DWORD dwRead = 0;
+
+        DPRINT("Control pipe connected!\n");
+
+        /* Read thread id from pipe */
+        if (!ReadFile(Service->ControlPipeHandle,
+                      (LPVOID)&dwProcessId,
+                      sizeof(DWORD),
+                      &dwRead,
+                      NULL))
+        {
+            DPRINT1("Reading the service control pipe failed (Error %lu)\n",
+                    GetLastError());
+            Status = STATUS_UNSUCCESSFUL;
+        }
+        else
+        {
+            DPRINT("Received process id %lu\n", dwProcessId);
+
+            /* FIXME: Send start command */
+
+            Status = STATUS_SUCCESS;
+        }
+    }
+    else
+    {
+        DPRINT("Connecting control pipe failed!\n");
+
+        /* Close control pipe */
+        CloseHandle(Service->ControlPipeHandle);
+        Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
+        Service->ProcessId = 0;
+        Service->ThreadId = 0;
+        Status = STATUS_UNSUCCESSFUL;
+    }
+
+    ScmSendStartCommand(Service, NULL);
+
+    /* Close process and thread handle */
+    CloseHandle(ProcessInformation.hThread);
+    CloseHandle(ProcessInformation.hProcess);
+
+    return Status;
 }
 
 
 static NTSTATUS
 ScmStartService(PSERVICE Service,
-               PSERVICE_GROUP Group)
+                PSERVICE_GROUP Group)
 {
-  RTL_QUERY_REGISTRY_TABLE QueryTable[3];
-  PROCESS_INFORMATION ProcessInformation;
-  STARTUPINFOW StartupInfo;
-  UNICODE_STRING ImagePath;
-  NTSTATUS Status;
-  ULONG Type;
-  BOOL Result;
-
-  DPRINT("ScmStartService() called\n");
-
-  Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
-  DPRINT("Service->Type: %u\n", Service->Type);
-
-  if (Service->Type == SERVICE_KERNEL_DRIVER ||
-      Service->Type == SERVICE_FILE_SYSTEM_DRIVER ||
-      Service->Type == SERVICE_RECOGNIZER_DRIVER)
+    NTSTATUS Status;
+
+    DPRINT("ScmStartService() called\n");
+
+    Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
+    DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
+
+    if (Service->Status.dwServiceType & SERVICE_DRIVER)
     {
-      /* Load driver */
-      DPRINT("  Path: %wZ\n", &Service->RegistryPath);
-      Status = NtLoadDriver(&Service->RegistryPath);
+        /* Load driver */
+        Status = ScmLoadDriver(Service);
+        if (Status == STATUS_SUCCESS)
+            Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
     }
-  else
+    else
     {
-      RtlInitUnicodeString(&ImagePath, NULL);
-
-      /* Get service data */
-      RtlZeroMemory(&QueryTable,
-                   sizeof(QueryTable));
-
-      QueryTable[0].Name = L"Type";
-      QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-      QueryTable[0].EntryContext = &Type;
-
-      QueryTable[1].Name = L"ImagePath";
-      QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-      QueryTable[1].EntryContext = &ImagePath;
-
-      Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
-                                     Service->ServiceName.Buffer,
-                                     QueryTable,
-                                     NULL,
-                                     NULL);
-      if (NT_SUCCESS(Status))
-       {
-         DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
-         DPRINT("Type: %lx\n", Type);
-
-         /* FIXME: create '\\.\pipe\net\NtControlPipe' instance */
-         Service->ControlPipeHandle = CreateNamedPipeW(L"\\\\.\\pipe\\net\\NtControlPipe",
-                                                       PIPE_ACCESS_DUPLEX,
-                                                       PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
-                                                       100,
-                                                       8000,
-                                                       4,
-                                                       30000,
-                                                       NULL);
-         DPRINT("CreateNamedPipeW() done\n");
-         if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE)
-           {
-             DPRINT1("Failed to create control pipe!\n");
-             Status = STATUS_UNSUCCESSFUL;
-             goto Done;
-           }
-
-         StartupInfo.cb = sizeof(StartupInfo);
-         StartupInfo.lpReserved = NULL;
-         StartupInfo.lpDesktop = NULL;
-         StartupInfo.lpTitle = NULL;
-         StartupInfo.dwFlags = 0;
-         StartupInfo.cbReserved2 = 0;
-         StartupInfo.lpReserved2 = 0;
-
-         Result = CreateProcessW(ImagePath.Buffer,
-                                 NULL,
-                                 NULL,
-                                 NULL,
-                                 FALSE,
-                                 DETACHED_PROCESS | CREATE_SUSPENDED,
-                                 NULL,
-                                 NULL,
-                                 &StartupInfo,
-                                 &ProcessInformation);
-         RtlFreeUnicodeString(&ImagePath);
-
-         if (!Result)
-           {
-             /* Close control pipe */
-             CloseHandle(Service->ControlPipeHandle);
-             Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
-
-             DPRINT("Starting '%S' failed!\n", Service->ServiceName.Buffer);
-             Status = STATUS_UNSUCCESSFUL;
-           }
-         else
-           {
-             DPRINT("Process Id: %lu  Handle %lx\n",
-                    ProcessInformation.dwProcessId,
-                    ProcessInformation.hProcess);
-             DPRINT("Thread Id: %lu  Handle %lx\n",
-                    ProcessInformation.dwThreadId,
-                    ProcessInformation.hThread);
-
-             /* Get process and thread ids */
-             Service->ProcessId = ProcessInformation.dwProcessId;
-             Service->ThreadId = ProcessInformation.dwThreadId;
-
-             /* Resume Thread */
-             ResumeThread(ProcessInformation.hThread);
-
-             /* FIXME: connect control pipe */
-             if (ConnectNamedPipe(Service->ControlPipeHandle, NULL))
-               {
-                 DPRINT("Control pipe connected!\n");
-                 Status = STATUS_SUCCESS;
-               }
-             else
-               {
-                 DPRINT1("Connecting control pipe failed!\n");
-
-                 /* Close control pipe */
-                 CloseHandle(Service->ControlPipeHandle);
-                 Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
-                 Service->ProcessId = 0;
-                 Service->ThreadId = 0;
-                 Status = STATUS_UNSUCCESSFUL;
-               }
-
-             /* Close process and thread handle */
-             CloseHandle(ProcessInformation.hThread);
-             CloseHandle(ProcessInformation.hProcess);
-           }
-       }
+        /* Start user-mode service */
+        Status = ScmStartUserModeService(Service);
     }
 
-Done:
-  if (NT_SUCCESS(Status))
+    DPRINT("ScmStartService() done (Status %lx)\n", Status);
+
+    if (NT_SUCCESS(Status))
     {
-      if (Group != NULL)
-       {
-         Group->ServicesRunning = TRUE;
-       }
-      Service->ServiceRunning = TRUE;
+        if (Group != NULL)
+        {
+            Group->ServicesRunning = TRUE;
+        }
+        Service->Status.dwCurrentState = SERVICE_RUNNING;
     }
 #if 0
-  else
+    else
     {
-      if (CurrentService->ErrorControl == 1)
-       {
-         /* Log error */
-
-       }
-      else if (CurrentService->ErrorControl == 2)
-       {
-         if (IsLastKnownGood == FALSE)
-           {
-             /* Boot last known good configuration */
-
-           }
-       }
-      else if (CurrentService->ErrorControl == 3)
-       {
-         if (IsLastKnownGood == FALSE)
-           {
-             /* Boot last known good configuration */
-
-           }
-         else
-           {
-             /* BSOD! */
-
-           }
-       }
+        switch (Service->ErrorControl)
+        {
+            case SERVICE_ERROR_NORMAL:
+                /* FIXME: Log error */
+                break;
+
+            case SERVICE_ERROR_SEVERE:
+                if (IsLastKnownGood == FALSE)
+                {
+                    /* FIXME: Boot last known good configuration */
+                }
+                break;
+
+            case SERVICE_ERROR_CRITICAL:
+                if (IsLastKnownGood == FALSE)
+                {
+                    /* FIXME: Boot last known good configuration */
+                }
+                else
+                {
+                    /* FIXME: BSOD! */
+                }
+                break;
+        }
     }
 #endif
 
-  return(STATUS_SUCCESS);
+    return Status;
 }
 
 
 VOID
 ScmAutoStartServices(VOID)
 {
-  PLIST_ENTRY GroupEntry;
-  PLIST_ENTRY ServiceEntry;
-  PSERVICE_GROUP CurrentGroup;
-  PSERVICE CurrentService;
-  ULONG i;
-
-  /* Clear 'ServiceVisited' flag */
-  ServiceEntry = ServiceListHead.Flink;
-  while (ServiceEntry != &ServiceListHead)
+    PLIST_ENTRY GroupEntry;
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE_GROUP CurrentGroup;
+    PSERVICE CurrentService;
+    ULONG i;
+
+    /* Clear 'ServiceVisited' flag */
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
     {
       CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
       CurrentService->ServiceVisited = FALSE;
       ServiceEntry = ServiceEntry->Flink;
     }
 
-  /* Start all services which are members of an existing group */
-  GroupEntry = GroupListHead.Flink;
-  while (GroupEntry != &GroupListHead)
+    /* Start all services which are members of an existing group */
+    GroupEntry = GroupListHead.Flink;
+    while (GroupEntry != &GroupListHead)
     {
-      CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
+        CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
+
+        DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
 
-      DPRINT("Group '%wZ'\n", &CurrentGroup->GroupName);
+        /* Start all services witch have a valid tag */
+        for (i = 0; i < CurrentGroup->TagCount; i++)
+        {
+            ServiceEntry = ServiceListHead.Flink;
+            while (ServiceEntry != &ServiceListHead)
+            {
+                CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+
+                if ((CurrentService->lpServiceGroup != NULL) &&
+                    (_wcsicmp(CurrentGroup->lpGroupName, CurrentService->lpServiceGroup) == 0) &&
+                    (CurrentService->dwStartType == SERVICE_AUTO_START) &&
+                    (CurrentService->ServiceVisited == FALSE) &&
+                    (CurrentService->dwTag == CurrentGroup->TagArray[i]))
+                {
+                    CurrentService->ServiceVisited = TRUE;
+                    ScmStartService(CurrentService,
+                                    CurrentGroup);
+                }
+
+                ServiceEntry = ServiceEntry->Flink;
+             }
+        }
 
-      /* Start all services witch have a valid tag */
-      for (i = 0; i < CurrentGroup->TagCount; i++)
+        /* Start all services which have an invalid tag or which do not have a tag */
+        ServiceEntry = ServiceListHead.Flink;
+        while (ServiceEntry != &ServiceListHead)
         {
-          ServiceEntry = ServiceListHead.Flink;
-          while (ServiceEntry != &ServiceListHead)
-           {
-             CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
-
-             if ((RtlEqualUnicodeString(&CurrentGroup->GroupName, &CurrentService->ServiceGroup, TRUE)) &&
-                 (CurrentService->Start == SERVICE_AUTO_START) &&
-                 (CurrentService->ServiceVisited == FALSE) &&
-                 (CurrentService->Tag == CurrentGroup->TagArray[i]))
-               {
-                 CurrentService->ServiceVisited = TRUE;
-                 ScmStartService(CurrentService,
-                                 CurrentGroup);
-               }
-
-             ServiceEntry = ServiceEntry->Flink;
-           }
+            CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+
+            if ((CurrentService->lpServiceGroup != NULL) &&
+                (_wcsicmp(CurrentGroup->lpGroupName, CurrentService->lpServiceGroup) == 0) &&
+                (CurrentService->dwStartType == SERVICE_AUTO_START) &&
+                (CurrentService->ServiceVisited == FALSE))
+            {
+                CurrentService->ServiceVisited = TRUE;
+                ScmStartService(CurrentService,
+                                CurrentGroup);
+            }
+
+            ServiceEntry = ServiceEntry->Flink;
         }
 
-      /* Start all services which have an invalid tag or which do not have a tag */ 
-      ServiceEntry = ServiceListHead.Flink;
-      while (ServiceEntry != &ServiceListHead)
-       {
-         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
-
-         if ((RtlEqualUnicodeString(&CurrentGroup->GroupName, &CurrentService->ServiceGroup, TRUE)) &&
-             (CurrentService->Start == SERVICE_AUTO_START) &&
-             (CurrentService->ServiceVisited == FALSE))
-           {
-             CurrentService->ServiceVisited = TRUE;
-             ScmStartService(CurrentService,
-                             CurrentGroup);
-           }
-
-         ServiceEntry = ServiceEntry->Flink;
-       }
-
-      GroupEntry = GroupEntry->Flink;
+        GroupEntry = GroupEntry->Flink;
     }
 
-  /* Start all services which are members of any non-existing group */
-  ServiceEntry = ServiceListHead.Flink;
-  while (ServiceEntry != &ServiceListHead)
+    /* Start all services which are members of any non-existing group */
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
     {
-      CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+        CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
 
-      if ((CurrentService->ServiceGroup.Length != 0) &&
-         (CurrentService->Start == SERVICE_AUTO_START) &&
-         (CurrentService->ServiceVisited == FALSE))
-       {
-         CurrentService->ServiceVisited = TRUE;
-         ScmStartService(CurrentService,
-                         NULL);
-       }
+        if ((CurrentService->lpServiceGroup != NULL) &&
+            (CurrentService->dwStartType == SERVICE_AUTO_START) &&
+            (CurrentService->ServiceVisited == FALSE))
+        {
+            CurrentService->ServiceVisited = TRUE;
+            ScmStartService(CurrentService,
+                            NULL);
+        }
 
-      ServiceEntry = ServiceEntry->Flink;
+        ServiceEntry = ServiceEntry->Flink;
     }
 
-  /* Start all services which are not a member of any group */
-  ServiceEntry = ServiceListHead.Flink;
-  while (ServiceEntry != &ServiceListHead)
+    /* Start all services which are not a member of any group */
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
     {
-      CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+        CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
 
-      if ((CurrentService->ServiceGroup.Length == 0) &&
-         (CurrentService->Start == SERVICE_AUTO_START) &&
-         (CurrentService->ServiceVisited == FALSE))
-       {
-         CurrentService->ServiceVisited = TRUE;
-         ScmStartService(CurrentService,
-                         NULL);
-       }
+        if ((CurrentService->lpServiceGroup == NULL) &&
+            (CurrentService->dwStartType == SERVICE_AUTO_START) &&
+            (CurrentService->ServiceVisited == FALSE))
+        {
+            CurrentService->ServiceVisited = TRUE;
+            ScmStartService(CurrentService,
+                            NULL);
+        }
 
-      ServiceEntry = ServiceEntry->Flink;
+        ServiceEntry = ServiceEntry->Flink;
     }
 
-  /* Clear 'ServiceVisited' flag again */
-  ServiceEntry = ServiceListHead.Flink;
-  while (ServiceEntry != &ServiceListHead)
+    /* Clear 'ServiceVisited' flag again */
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
     {
-      CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
-      CurrentService->ServiceVisited = FALSE;
-      ServiceEntry = ServiceEntry->Flink;
+        CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+        CurrentService->ServiceVisited = FALSE;
+        ServiceEntry = ServiceEntry->Flink;
     }
 }