[CRT] crtdefs.h: Wrap localeinfo_struct in ifdef
[reactos.git] / dll / win32 / advapi32 / service / sctrl.c
index 3a2d65d..d13c1ce 100644 (file)
@@ -6,13 +6,14 @@
  * COPYRIGHT:   Copyright 1999 Emanuele Aliberti
  *              Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
  *                             Gregor Brunmar <gregor.brunmar@home.se>
- *
  */
 
 
 /* INCLUDES ******************************************************************/
 
 #include <advapi32.h>
+#include <pseh/pseh2.h>
+
 WINE_DEFAULT_DEBUG_CHANNEL(advapi);
 
 
@@ -23,6 +24,7 @@ typedef struct _SERVICE_THREAD_PARAMSA
     LPSERVICE_MAIN_FUNCTIONA lpServiceMain;
     DWORD dwArgCount;
     LPSTR *lpArgVector;
+    DWORD dwServiceTag;
 } SERVICE_THREAD_PARAMSA, *PSERVICE_THREAD_PARAMSA;
 
 
@@ -31,6 +33,7 @@ typedef struct _SERVICE_THREAD_PARAMSW
     LPSERVICE_MAIN_FUNCTIONW lpServiceMain;
     DWORD dwArgCount;
     LPWSTR *lpArgVector;
+    DWORD dwServiceTag;
 } SERVICE_THREAD_PARAMSW, *PSERVICE_THREAD_PARAMSW;
 
 
@@ -40,14 +43,15 @@ typedef struct _ACTIVE_SERVICE
     UNICODE_STRING ServiceName;
     union
     {
-        SERVICE_THREAD_PARAMSA A;
-        SERVICE_THREAD_PARAMSW W;
-    } ThreadParams;
+        LPSERVICE_MAIN_FUNCTIONA A;
+        LPSERVICE_MAIN_FUNCTIONW W;
+    } ServiceMain;
     LPHANDLER_FUNCTION HandlerFunction;
     LPHANDLER_FUNCTION_EX HandlerFunctionEx;
     LPVOID HandlerContext;
     BOOL bUnicode;
     BOOL bOwnProcess;
+    DWORD dwServiceTag;
 } ACTIVE_SERVICE, *PACTIVE_SERVICE;
 
 
@@ -56,6 +60,7 @@ typedef struct _ACTIVE_SERVICE
 static DWORD dwActiveServiceCount = 0;
 static PACTIVE_SERVICE lpActiveServices = NULL;
 static handle_t hStatusBinding = NULL;
+static BOOL bSecurityServiceProcess = FALSE;
 
 
 /* FUNCTIONS *****************************************************************/
@@ -80,7 +85,7 @@ ScCreateStatusBinding(VOID)
     LPWSTR pszStringBinding;
     RPC_STATUS status;
 
-    TRACE("ScCreateStatusBinding() called\n");
+    TRACE("ScCreateStatusBinding()\n");
 
     status = RpcStringBindingComposeW(NULL,
                                       L"ncacn_np",
@@ -117,7 +122,7 @@ ScDestroyStatusBinding(VOID)
 {
     RPC_STATUS status;
 
-    TRACE("ScDestroyStatusBinding() called\n");
+    TRACE("ScDestroyStatusBinding()\n");
 
     if (hStatusBinding == NULL)
         return RPC_S_OK;
@@ -141,7 +146,8 @@ ScLookupServiceByServiceName(LPCWSTR lpServiceName)
 {
     DWORD i;
 
-    TRACE("ScLookupServiceByServiceName(%S) called\n", lpServiceName);
+    TRACE("ScLookupServiceByServiceName(%S)\n",
+          lpServiceName);
 
     if (lpActiveServices[0].bOwnProcess)
         return &lpActiveServices[0];
@@ -157,51 +163,63 @@ ScLookupServiceByServiceName(LPCWSTR lpServiceName)
     }
 
     TRACE("No service found!\n");
-
-    SetLastError(ERROR_SERVICE_DOES_NOT_EXIST);
-
     return NULL;
 }
 
 
 static DWORD WINAPI
-ScServiceMainStub(LPVOID Context)
+ScServiceMainStubA(LPVOID Context)
 {
-    PACTIVE_SERVICE lpService = (PACTIVE_SERVICE)Context;
+    PTEB Teb;
+    PSERVICE_THREAD_PARAMSA ThreadParams = Context;
+
+    TRACE("ScServiceMainStubA(%p)\n", Context);
 
-    TRACE("ScServiceMainStub() called\n");
+    /* Set service tag */
+    Teb = NtCurrentTeb();
+    Teb->SubProcessTag = UlongToPtr(ThreadParams->dwServiceTag);
 
     /* Call the main service routine and free the arguments vector */
-    if (lpService->bUnicode)
-    {
-        (lpService->ThreadParams.W.lpServiceMain)(lpService->ThreadParams.W.dwArgCount,
-                                                  lpService->ThreadParams.W.lpArgVector);
+    (ThreadParams->lpServiceMain)(ThreadParams->dwArgCount,
+                                  ThreadParams->lpArgVector);
 
-        if (lpService->ThreadParams.W.lpArgVector != NULL)
-        {
-            HeapFree(GetProcessHeap(),
-                     0,
-                     lpService->ThreadParams.W.lpArgVector);
+    /* Reset service tag */
+    Teb->SubProcessTag = 0;
 
-            lpService->ThreadParams.W.lpArgVector = NULL;
-            lpService->ThreadParams.W.dwArgCount = 0;
-        }
-    }
-    else
+    if (ThreadParams->lpArgVector != NULL)
     {
-        (lpService->ThreadParams.A.lpServiceMain)(lpService->ThreadParams.A.dwArgCount,
-                                                  lpService->ThreadParams.A.lpArgVector);
+        HeapFree(GetProcessHeap(), 0, ThreadParams->lpArgVector);
+    }
+    HeapFree(GetProcessHeap(), 0, ThreadParams);
 
-        if (lpService->ThreadParams.A.lpArgVector != NULL)
-        {
-            HeapFree(GetProcessHeap(),
-                     0,
-                     lpService->ThreadParams.A.lpArgVector);
+    return ERROR_SUCCESS;
+}
 
-            lpService->ThreadParams.A.lpArgVector = NULL;
-            lpService->ThreadParams.A.dwArgCount = 0;
-        }
+
+static DWORD WINAPI
+ScServiceMainStubW(LPVOID Context)
+{
+    PTEB Teb;
+    PSERVICE_THREAD_PARAMSW ThreadParams = Context;
+
+    TRACE("ScServiceMainStubW(%p)\n", Context);
+
+    /* Set service tag */
+    Teb = NtCurrentTeb();
+    Teb->SubProcessTag = UlongToPtr(ThreadParams->dwServiceTag);
+
+    /* Call the main service routine and free the arguments vector */
+    (ThreadParams->lpServiceMain)(ThreadParams->dwArgCount,
+                                  ThreadParams->lpArgVector);
+
+    /* Reset service tag */
+    Teb->SubProcessTag = 0;
+
+    if (ThreadParams->lpArgVector != NULL)
+    {
+        HeapFree(GetProcessHeap(), 0, ThreadParams->lpArgVector);
     }
+    HeapFree(GetProcessHeap(), 0, ThreadParams);
 
     return ERROR_SUCCESS;
 }
@@ -212,35 +230,44 @@ ScConnectControlPipe(HANDLE *hPipe)
 {
     DWORD dwBytesWritten;
     DWORD dwState;
-    DWORD dwServiceCurrent = 0;
+    DWORD dwServiceCurrent = 1;
     NTSTATUS Status;
     WCHAR NtControlPipeName[MAX_PATH + 1];
     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
     DWORD dwProcessId;
 
-    /* Get the service number and create the named pipe */
-    RtlZeroMemory(&QueryTable,
-                  sizeof(QueryTable));
-
-    QueryTable[0].Name = L"";
-    QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-    QueryTable[0].EntryContext = &dwServiceCurrent;
-
-    Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
-                                    L"ServiceCurrent",
-                                    QueryTable,
-                                    NULL,
-                                    NULL);
+    TRACE("ScConnectControlPipe(%p)\n",
+          hPipe);
 
-    if (!NT_SUCCESS(Status))
+    /* Get the service number and create the named pipe */
+    if (bSecurityServiceProcess == FALSE)
+    {
+        RtlZeroMemory(&QueryTable, sizeof(QueryTable));
+
+        QueryTable[0].Name = L"";
+        QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
+        QueryTable[0].EntryContext = &dwServiceCurrent;
+
+        Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
+                                        L"ServiceCurrent",
+                                        QueryTable,
+                                        NULL,
+                                        NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
+            return RtlNtStatusToDosError(Status);
+        }
+    }
+    else
     {
-        ERR("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
-        return RtlNtStatusToDosError(Status);
+        dwServiceCurrent = 0;
     }
 
     swprintf(NtControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", dwServiceCurrent);
+    TRACE("PipeName: %S\n", NtControlPipeName);
 
-    if (!WaitNamedPipeW(NtControlPipeName, 15000))
+    if (!WaitNamedPipeW(NtControlPipeName, 30000))
     {
         ERR("WaitNamedPipe(%S) failed (Error %lu)\n", NtControlPipeName, GetLastError());
         return ERROR_FAILED_SERVICE_CONTROLLER_CONNECT;
@@ -248,7 +275,7 @@ ScConnectControlPipe(HANDLE *hPipe)
 
     *hPipe = CreateFileW(NtControlPipeName,
                          GENERIC_READ | GENERIC_WRITE,
-                         0,
+                         FILE_SHARE_READ | FILE_SHARE_WRITE,
                          NULL,
                          OPEN_EXISTING,
                          FILE_ATTRIBUTE_NORMAL,
@@ -271,7 +298,7 @@ ScConnectControlPipe(HANDLE *hPipe)
     dwProcessId = GetCurrentProcessId();
     WriteFile(*hPipe,
               &dwProcessId,
-              sizeof(DWORD),
+              sizeof(dwProcessId),
               &dwBytesWritten,
               NULL);
 
@@ -281,44 +308,90 @@ ScConnectControlPipe(HANDLE *hPipe)
 }
 
 
+/*
+ * Ansi/Unicode argument layout of the vector passed to a service at startup,
+ * depending on the different versions of Windows considered:
+ *
+ * - XP/2003:
+ *   [argv array of pointers][parameter 1][parameter 2]...[service name]
+ *
+ * - Vista:
+ *   [argv array of pointers][align to 8 bytes]
+ *   [parameter 1][parameter 2]...[service name]
+ *
+ * - Win7/8:
+ *   [argv array of pointers][service name]
+ *   [parameter 1][align to 4 bytes][parameter 2][align to 4 bytes]...
+ *
+ * Space for parameters and service name is always enough to store
+ * both the Ansi and the Unicode versions including NULL terminator.
+ */
+
 static DWORD
 ScBuildUnicodeArgsVector(PSCM_CONTROL_PACKET ControlPacket,
                          LPDWORD lpArgCount,
                          LPWSTR **lpArgVector)
 {
-    LPWSTR *lpVector;
-    LPWSTR *lpArg;
+    PWSTR *lpVector;
+    PWSTR pszServiceName;
+    DWORD cbServiceName;
+    DWORD cbArguments;
+    DWORD cbTotal;
     DWORD i;
 
     if (ControlPacket == NULL || lpArgCount == NULL || lpArgVector == NULL)
         return ERROR_INVALID_PARAMETER;
 
-    *lpArgCount = 0;
+    *lpArgCount  = 0;
     *lpArgVector = NULL;
 
+    /* Retrieve and count the start command line (NULL-terminated) */
+    pszServiceName = (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset);
+    cbServiceName  = lstrlenW(pszServiceName) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
+
+    /*
+     * The total size of the argument vector is equal to the entry for
+     * the service name, plus the size of the original argument vector.
+     */
+    cbTotal = sizeof(PWSTR) + cbServiceName;
     if (ControlPacket->dwArgumentsCount > 0)
-    {
-        lpVector = HeapAlloc(GetProcessHeap(),
-                             HEAP_ZERO_MEMORY,
-                             ControlPacket->dwSize - ControlPacket->dwArgumentsOffset);
-        if (lpVector == NULL)
-            return ERROR_OUTOFMEMORY;
+        cbArguments = ControlPacket->dwSize - ControlPacket->dwArgumentsOffset;
+    else
+        cbArguments = 0;
+    cbTotal += cbArguments;
+
+    /* Allocate the new argument vector */
+    lpVector = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbTotal);
+    if (lpVector == NULL)
+        return ERROR_NOT_ENOUGH_MEMORY;
 
-        memcpy(lpVector,
-               ((PBYTE)ControlPacket + ControlPacket->dwArgumentsOffset),
-               ControlPacket->dwSize - ControlPacket->dwArgumentsOffset);
+    /*
+     * The first argument is reserved for the service name, which
+     * will be appended to the end of the argument string list.
+     */
+
+    /* Copy the remaining arguments */
+    if (ControlPacket->dwArgumentsCount > 0)
+    {
+        memcpy(&lpVector[1],
+               (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwArgumentsOffset),
+               cbArguments);
 
-        lpArg = lpVector;
         for (i = 0; i < ControlPacket->dwArgumentsCount; i++)
         {
-            *lpArg = (LPWSTR)((ULONG_PTR)lpArg + (ULONG_PTR)*lpArg);
-            lpArg++;
+            lpVector[i + 1] = (PWSTR)((ULONG_PTR)&lpVector[1] + (ULONG_PTR)lpVector[i + 1]);
+            TRACE("Unicode lpVector[%lu] = '%ls'\n", i + 1, lpVector[i + 1]);
         }
-
-        *lpArgCount = ControlPacket->dwArgumentsCount;
-        *lpArgVector = lpVector;
     }
 
+    /* Now copy the service name */
+    lpVector[0] = (PWSTR)((ULONG_PTR)&lpVector[1] + cbArguments);
+    memcpy(lpVector[0], pszServiceName, cbServiceName);
+    TRACE("Unicode lpVector[%lu] = '%ls'\n", 0, lpVector[0]);
+
+    *lpArgCount  = ControlPacket->dwArgumentsCount + 1;
+    *lpArgVector = lpVector;
+
     return ERROR_SUCCESS;
 }
 
@@ -328,71 +401,49 @@ ScBuildAnsiArgsVector(PSCM_CONTROL_PACKET ControlPacket,
                       LPDWORD lpArgCount,
                       LPSTR **lpArgVector)
 {
-    LPSTR *lpVector;
-    LPSTR *lpPtr;
-    LPWSTR lpUnicodeString;
-    LPSTR lpAnsiString;
-    DWORD dwVectorSize;
-    DWORD dwUnicodeSize;
-    DWORD dwAnsiSize;
-    DWORD i;
+    DWORD dwError;
+    NTSTATUS Status;
+    DWORD ArgCount, i;
+    PWSTR *lpVectorW;
+    PSTR  *lpVectorA;
+    UNICODE_STRING UnicodeString;
+    ANSI_STRING AnsiString;
 
     if (ControlPacket == NULL || lpArgCount == NULL || lpArgVector == NULL)
         return ERROR_INVALID_PARAMETER;
 
-    *lpArgCount = 0;
+    *lpArgCount  = 0;
     *lpArgVector = NULL;
 
-    if (ControlPacket->dwArgumentsCount > 0)
-    {
-        dwVectorSize = ControlPacket->dwArgumentsCount * sizeof(LPWSTR);
-
-        lpUnicodeString = (LPWSTR)((PBYTE)ControlPacket +
-                                   ControlPacket->dwArgumentsOffset +
-                                   dwVectorSize);
-        dwUnicodeSize = (ControlPacket->dwSize -
-                         ControlPacket->dwArgumentsOffset -
-                         dwVectorSize) / sizeof(WCHAR);
-
-        dwAnsiSize = WideCharToMultiByte(CP_ACP,
-                                         0,
-                                         lpUnicodeString,
-                                         dwUnicodeSize,
-                                         NULL,
-                                         0,
-                                         NULL,
-                                         NULL);
-
-        lpVector = HeapAlloc(GetProcessHeap(),
-                             HEAP_ZERO_MEMORY,
-                             dwVectorSize + dwAnsiSize);
-        if (lpVector == NULL)
-            return ERROR_OUTOFMEMORY;
-
-        lpPtr = (LPSTR*)lpVector;
-        lpAnsiString = (LPSTR)((ULONG_PTR)lpVector + dwVectorSize);
-
-        WideCharToMultiByte(CP_ACP,
-                            0,
-                            lpUnicodeString,
-                            dwUnicodeSize,
-                            lpAnsiString,
-                            dwAnsiSize,
-                            NULL,
-                            NULL);
+    /* Build the UNICODE arguments vector */
+    dwError = ScBuildUnicodeArgsVector(ControlPacket, &ArgCount, &lpVectorW);
+    if (dwError != ERROR_SUCCESS)
+        return dwError;
 
-        for (i = 0; i < ControlPacket->dwArgumentsCount; i++)
+    /* Convert the vector to ANSI in place */
+    lpVectorA = (PSTR*)lpVectorW;
+    for (i = 0; i < ArgCount; i++)
+    {
+        RtlInitUnicodeString(&UnicodeString, lpVectorW[i]);
+        RtlInitEmptyAnsiString(&AnsiString, lpVectorA[i], UnicodeString.MaximumLength);
+        Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
+        if (!NT_SUCCESS(Status))
         {
-            *lpPtr = lpAnsiString;
-
-            lpPtr++;
-            lpAnsiString += (strlen(lpAnsiString) + 1);
+            /* Failed to convert to ANSI; free the allocated vector and return */
+            dwError = RtlNtStatusToDosError(Status);
+            HeapFree(GetProcessHeap(), 0, lpVectorW);
+            return dwError;
         }
 
-        *lpArgCount = ControlPacket->dwArgumentsCount;
-        *lpArgVector = lpVector;
+        /* NULL-terminate the string */
+        AnsiString.Buffer[AnsiString.Length / sizeof(CHAR)] = ANSI_NULL;
+
+        TRACE("Ansi lpVector[%lu] = '%s'\n", i, lpVectorA[i]);
     }
 
+    *lpArgCount  = ArgCount;
+    *lpArgVector = lpVectorA;
+
     return ERROR_SUCCESS;
 }
 
@@ -404,73 +455,93 @@ ScStartService(PACTIVE_SERVICE lpService,
     HANDLE ThreadHandle;
     DWORD ThreadId;
     DWORD dwError;
+    PSERVICE_THREAD_PARAMSA ThreadParamsA;
+    PSERVICE_THREAD_PARAMSW ThreadParamsW;
+
+    TRACE("ScStartService(%p %p)\n",
+          lpService, ControlPacket);
 
     if (lpService == NULL || ControlPacket == NULL)
         return ERROR_INVALID_PARAMETER;
 
-    TRACE("ScStartService() called\n");
     TRACE("Size: %lu\n", ControlPacket->dwSize);
-    TRACE("Service: %S\n", (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset));
+    TRACE("Service: %S\n", (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset));
 
     /* Set the service status handle */
     lpService->hServiceStatus = ControlPacket->hServiceStatus;
+    /* Set the service tag */
+    lpService->dwServiceTag = ControlPacket->dwServiceTag;
 
     /* Build the arguments vector */
-    if (lpService->bUnicode == TRUE)
+    if (lpService->bUnicode != FALSE)
     {
+        ThreadParamsW = HeapAlloc(GetProcessHeap(), 0, sizeof(*ThreadParamsW));
+        if (ThreadParamsW == NULL)
+            return ERROR_NOT_ENOUGH_MEMORY;
         dwError = ScBuildUnicodeArgsVector(ControlPacket,
-                                           &lpService->ThreadParams.W.dwArgCount,
-                                           &lpService->ThreadParams.W.lpArgVector);
+                                           &ThreadParamsW->dwArgCount,
+                                           &ThreadParamsW->lpArgVector);
+        if (dwError != ERROR_SUCCESS)
+        {
+            HeapFree(GetProcessHeap(), 0, ThreadParamsW);
+            return dwError;
+        }
+        ThreadParamsW->lpServiceMain = lpService->ServiceMain.W;
+        ThreadParamsW->dwServiceTag = ControlPacket->dwServiceTag;
+        ThreadHandle = CreateThread(NULL,
+                                    0,
+                                    ScServiceMainStubW,
+                                    ThreadParamsW,
+                                    0,
+                                    &ThreadId);
+        if (ThreadHandle == NULL)
+        {
+            if (ThreadParamsW->lpArgVector != NULL)
+            {
+                HeapFree(GetProcessHeap(), 0, ThreadParamsW->lpArgVector);
+            }
+            HeapFree(GetProcessHeap(), 0, ThreadParamsW);
+
+            return ERROR_SERVICE_NO_THREAD;
+        }
+
+        CloseHandle(ThreadHandle);
     }
     else
     {
+        ThreadParamsA = HeapAlloc(GetProcessHeap(), 0, sizeof(*ThreadParamsA));
+        if (ThreadParamsA == NULL)
+            return ERROR_NOT_ENOUGH_MEMORY;
         dwError = ScBuildAnsiArgsVector(ControlPacket,
-                                        &lpService->ThreadParams.A.dwArgCount,
-                                        &lpService->ThreadParams.A.lpArgVector);
-    }
-
-    if (dwError != ERROR_SUCCESS)
-        return dwError;
-
-    /* Invoke the services entry point and implement the command loop */
-    ThreadHandle = CreateThread(NULL,
-                                0,
-                                ScServiceMainStub,
-                                lpService,
-                                CREATE_SUSPENDED,
-                                &ThreadId);
-    if (ThreadHandle == NULL)
-    {
-        /* Free the arguments vector */
-        if (lpService->bUnicode)
+                                        &ThreadParamsA->dwArgCount,
+                                        &ThreadParamsA->lpArgVector);
+        if (dwError != ERROR_SUCCESS)
         {
-            if (lpService->ThreadParams.W.lpArgVector != NULL)
-            {
-                HeapFree(GetProcessHeap(),
-                         0,
-                         lpService->ThreadParams.W.lpArgVector);
-                lpService->ThreadParams.W.lpArgVector = NULL;
-                lpService->ThreadParams.W.dwArgCount = 0;
-            }
+            HeapFree(GetProcessHeap(), 0, ThreadParamsA);
+            return dwError;
         }
-        else
+        ThreadParamsA->lpServiceMain = lpService->ServiceMain.A;
+        ThreadParamsA->dwServiceTag = ControlPacket->dwServiceTag;
+        ThreadHandle = CreateThread(NULL,
+                                    0,
+                                    ScServiceMainStubA,
+                                    ThreadParamsA,
+                                    0,
+                                    &ThreadId);
+        if (ThreadHandle == NULL)
         {
-            if (lpService->ThreadParams.A.lpArgVector != NULL)
+            if (ThreadParamsA->lpArgVector != NULL)
             {
-                HeapFree(GetProcessHeap(),
-                         0,
-                         lpService->ThreadParams.A.lpArgVector);
-                lpService->ThreadParams.A.lpArgVector = NULL;
-                lpService->ThreadParams.A.dwArgCount = 0;
+                HeapFree(GetProcessHeap(), 0, ThreadParamsA->lpArgVector);
             }
+            HeapFree(GetProcessHeap(), 0, ThreadParamsA);
+
+            return ERROR_SERVICE_NO_THREAD;
         }
 
-        return ERROR_SERVICE_NO_THREAD;
+        CloseHandle(ThreadHandle);
     }
 
-    ResumeThread(ThreadHandle);
-    CloseHandle(ThreadHandle);
-
     return ERROR_SUCCESS;
 }
 
@@ -479,26 +550,58 @@ static DWORD
 ScControlService(PACTIVE_SERVICE lpService,
                  PSCM_CONTROL_PACKET ControlPacket)
 {
+    DWORD dwError = ERROR_SUCCESS;
+
+    TRACE("ScControlService(%p %p)\n",
+          lpService, ControlPacket);
+
     if (lpService == NULL || ControlPacket == NULL)
         return ERROR_INVALID_PARAMETER;
 
-    TRACE("ScControlService() called\n");
     TRACE("Size: %lu\n", ControlPacket->dwSize);
-    TRACE("Service: %S\n", (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset));
+    TRACE("Service: %S\n", (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset));
+
+    /* Set service tag */
+    NtCurrentTeb()->SubProcessTag = UlongToPtr(lpService->dwServiceTag);
 
     if (lpService->HandlerFunction)
     {
-        (lpService->HandlerFunction)(ControlPacket->dwControl);
+        _SEH2_TRY
+        {
+            (lpService->HandlerFunction)(ControlPacket->dwControl);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwError = ERROR_EXCEPTION_IN_SERVICE;
+        }
+        _SEH2_END;
     }
     else if (lpService->HandlerFunctionEx)
     {
-        /* FIXME: send correct params */
-        (lpService->HandlerFunctionEx)(ControlPacket->dwControl, 0, NULL, NULL);
+        _SEH2_TRY
+        {
+            /* FIXME: Send correct 2nd and 3rd parameters */
+            (lpService->HandlerFunctionEx)(ControlPacket->dwControl,
+                                           0, NULL,
+                                           lpService->HandlerContext);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwError = ERROR_EXCEPTION_IN_SERVICE;
+        }
+        _SEH2_END;
+    }
+    else
+    {
+        dwError = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
     }
 
-    TRACE("ScControlService() done\n");
+    /* Reset service tag */
+    NtCurrentTeb()->SubProcessTag = 0;
 
-    return ERROR_SUCCESS;
+    TRACE("ScControlService() done (Error %lu)\n", dwError);
+
+    return dwError;
 }
 
 
@@ -509,18 +612,19 @@ ScServiceDispatcher(HANDLE hPipe,
 {
     DWORD Count;
     BOOL bResult;
-    DWORD dwRunningServices = 0;
+    BOOL bRunning = TRUE;
     LPWSTR lpServiceName;
     PACTIVE_SERVICE lpService;
     SCM_REPLY_PACKET ReplyPacket;
     DWORD dwError;
 
-    TRACE("ScDispatcherLoop() called\n");
+    TRACE("ScServiceDispatcher(%p %p %lu)\n",
+          hPipe, ControlPacket, dwBufferSize);
 
     if (ControlPacket == NULL || dwBufferSize < sizeof(SCM_CONTROL_PACKET))
         return FALSE;
 
-    while (TRUE)
+    while (bRunning)
     {
         /* Read command from the control pipe */
         bResult = ReadFile(hPipe,
@@ -537,39 +641,45 @@ ScServiceDispatcher(HANDLE hPipe,
         lpServiceName = (LPWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
         TRACE("Service: %S\n", lpServiceName);
 
-        if (ControlPacket->dwControl == SERVICE_CONTROL_START_OWN)
-            lpActiveServices[0].bOwnProcess = TRUE;
-
-        lpService = ScLookupServiceByServiceName(lpServiceName);
-        if (lpService != NULL)
+        if ((ControlPacket->dwControl == SERVICE_CONTROL_STOP) &&
+            (lpServiceName[0] == UNICODE_NULL))
         {
-            /* Execute command */
-            switch (ControlPacket->dwControl)
-            {
-                case SERVICE_CONTROL_START_SHARE:
-                case SERVICE_CONTROL_START_OWN:
-                    TRACE("Start command - received SERVICE_CONTROL_START\n");
-                    dwError = ScStartService(lpService, ControlPacket);
-                    if (dwError == ERROR_SUCCESS)
-                        dwRunningServices++;
-                    break;
-
-                case SERVICE_CONTROL_STOP:
-                    TRACE("Stop command - received SERVICE_CONTROL_STOP\n");
-                    dwError = ScControlService(lpService, ControlPacket);
-                    if (dwError == ERROR_SUCCESS)
-                        dwRunningServices--;
-                    break;
-
-                default:
-                    TRACE("Command %lu received", ControlPacket->dwControl);
-                    dwError = ScControlService(lpService, ControlPacket);
-                    break;
-            }
+            TRACE("Stop dispatcher thread\n");
+            bRunning = FALSE;
+            dwError = ERROR_SUCCESS;
         }
         else
         {
-            dwError = ERROR_SERVICE_DOES_NOT_EXIST;
+            if (ControlPacket->dwControl == SERVICE_CONTROL_START_OWN)
+                lpActiveServices[0].bOwnProcess = TRUE;
+
+            lpService = ScLookupServiceByServiceName(lpServiceName);
+            if (lpService != NULL)
+            {
+                /* Execute command */
+                switch (ControlPacket->dwControl)
+                {
+                    case SERVICE_CONTROL_START_SHARE:
+                    case SERVICE_CONTROL_START_OWN:
+                        TRACE("Start command - received SERVICE_CONTROL_START\n");
+                        dwError = ScStartService(lpService, ControlPacket);
+                        break;
+
+                    case SERVICE_CONTROL_STOP:
+                        TRACE("Stop command - received SERVICE_CONTROL_STOP\n");
+                        dwError = ScControlService(lpService, ControlPacket);
+                        break;
+
+                    default:
+                        TRACE("Command %lu received\n", ControlPacket->dwControl);
+                        dwError = ScControlService(lpService, ControlPacket);
+                        break;
+                }
+            }
+            else
+            {
+                dwError = ERROR_SERVICE_NOT_IN_EXE;
+            }
         }
 
         ReplyPacket.dwError = dwError;
@@ -585,9 +695,6 @@ ScServiceDispatcher(HANDLE hPipe,
             ERR("Pipe write failed (Error: %lu)\n", GetLastError());
             return FALSE;
         }
-
-        if (dwRunningServices == 0)
-            break;
     }
 
     return TRUE;
@@ -595,7 +702,7 @@ ScServiceDispatcher(HANDLE hPipe,
 
 
 /**********************************************************************
- *     RegisterServiceCtrlHandlerA
+ *  RegisterServiceCtrlHandlerA
  *
  * @implemented
  */
@@ -605,26 +712,29 @@ RegisterServiceCtrlHandlerA(LPCSTR lpServiceName,
 {
     ANSI_STRING ServiceNameA;
     UNICODE_STRING ServiceNameU;
-    SERVICE_STATUS_HANDLE SHandle;
+    SERVICE_STATUS_HANDLE hServiceStatus;
+
+    TRACE("RegisterServiceCtrlHandlerA(%s %p)\n",
+          debugstr_a(lpServiceName), lpHandlerProc);
 
-    RtlInitAnsiString(&ServiceNameA, (LPSTR)lpServiceName);
+    RtlInitAnsiString(&ServiceNameA, lpServiceName);
     if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ServiceNameU, &ServiceNameA, TRUE)))
     {
-        SetLastError(ERROR_OUTOFMEMORY);
-        return (SERVICE_STATUS_HANDLE)0;
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return NULL;
     }
 
-    SHandle = RegisterServiceCtrlHandlerW(ServiceNameU.Buffer,
-                                          lpHandlerProc);
+    hServiceStatus = RegisterServiceCtrlHandlerW(ServiceNameU.Buffer,
+                                                 lpHandlerProc);
 
     RtlFreeUnicodeString(&ServiceNameU);
 
-    return SHandle;
+    return hServiceStatus;
 }
 
 
 /**********************************************************************
- *     RegisterServiceCtrlHandlerW
+ *  RegisterServiceCtrlHandlerW
  *
  * @implemented
  */
@@ -634,23 +744,33 @@ RegisterServiceCtrlHandlerW(LPCWSTR lpServiceName,
 {
     PACTIVE_SERVICE Service;
 
-    Service = ScLookupServiceByServiceName((LPWSTR)lpServiceName);
+    TRACE("RegisterServiceCtrlHandlerW(%s %p)\n",
+          debugstr_w(lpServiceName), lpHandlerProc);
+
+    Service = ScLookupServiceByServiceName(lpServiceName);
     if (Service == NULL)
     {
-        return (SERVICE_STATUS_HANDLE)NULL;
+        SetLastError(ERROR_SERVICE_NOT_IN_EXE);
+        return NULL;
+    }
+
+    if (!lpHandlerProc)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return NULL;
     }
 
     Service->HandlerFunction = lpHandlerProc;
     Service->HandlerFunctionEx = NULL;
 
-    TRACE("RegisterServiceCtrlHandler returning %lu\n", Service->hServiceStatus);
+    TRACE("RegisterServiceCtrlHandler returning %p\n", Service->hServiceStatus);
 
     return Service->hServiceStatus;
 }
 
 
 /**********************************************************************
- *     RegisterServiceCtrlHandlerExA
+ *  RegisterServiceCtrlHandlerExA
  *
  * @implemented
  */
@@ -661,27 +781,30 @@ RegisterServiceCtrlHandlerExA(LPCSTR lpServiceName,
 {
     ANSI_STRING ServiceNameA;
     UNICODE_STRING ServiceNameU;
-    SERVICE_STATUS_HANDLE SHandle;
+    SERVICE_STATUS_HANDLE hServiceStatus;
+
+    TRACE("RegisterServiceCtrlHandlerExA(%s %p %p)\n",
+          debugstr_a(lpServiceName), lpHandlerProc, lpContext);
 
-    RtlInitAnsiString(&ServiceNameA, (LPSTR)lpServiceName);
+    RtlInitAnsiString(&ServiceNameA, lpServiceName);
     if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ServiceNameU, &ServiceNameA, TRUE)))
     {
-        SetLastError(ERROR_OUTOFMEMORY);
-        return (SERVICE_STATUS_HANDLE)0;
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return NULL;
     }
 
-    SHandle = RegisterServiceCtrlHandlerExW(ServiceNameU.Buffer,
-                                            lpHandlerProc,
-                                            lpContext);
+    hServiceStatus = RegisterServiceCtrlHandlerExW(ServiceNameU.Buffer,
+                                                   lpHandlerProc,
+                                                   lpContext);
 
     RtlFreeUnicodeString(&ServiceNameU);
 
-    return SHandle;
+    return hServiceStatus;
 }
 
 
 /**********************************************************************
- *     RegisterServiceCtrlHandlerExW
+ *  RegisterServiceCtrlHandlerExW
  *
  * @implemented
  */
@@ -692,24 +815,81 @@ RegisterServiceCtrlHandlerExW(LPCWSTR lpServiceName,
 {
     PACTIVE_SERVICE Service;
 
+    TRACE("RegisterServiceCtrlHandlerExW(%s %p %p)\n",
+          debugstr_w(lpServiceName), lpHandlerProc, lpContext);
+
     Service = ScLookupServiceByServiceName(lpServiceName);
     if (Service == NULL)
     {
-        return (SERVICE_STATUS_HANDLE)NULL;
+        SetLastError(ERROR_SERVICE_NOT_IN_EXE);
+        return NULL;
+    }
+
+    if (!lpHandlerProc)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return NULL;
     }
 
     Service->HandlerFunction = NULL;
     Service->HandlerFunctionEx = lpHandlerProc;
     Service->HandlerContext = lpContext;
 
-    TRACE("RegisterServiceCtrlHandlerEx returning %lu\n", Service->hServiceStatus);
+    TRACE("RegisterServiceCtrlHandlerEx returning %p\n", Service->hServiceStatus);
 
     return Service->hServiceStatus;
 }
 
 
 /**********************************************************************
- *     I_ScSetServiceBitsA
+ *  I_ScIsSecurityProcess
+ *
+ * Undocumented
+ *
+ * @implemented
+ */
+VOID
+WINAPI
+I_ScIsSecurityProcess(VOID)
+{
+    TRACE("I_ScIsSecurityProcess()\n");
+    bSecurityServiceProcess = TRUE;
+}
+
+
+/**********************************************************************
+ *  I_ScPnPGetServiceName
+ *
+ * Undocumented
+ *
+ * @implemented
+ */
+DWORD
+WINAPI
+I_ScPnPGetServiceName(IN SERVICE_STATUS_HANDLE hServiceStatus,
+                      OUT LPWSTR lpServiceName,
+                      IN DWORD cchServiceName)
+{
+    DWORD i;
+
+    TRACE("I_ScPnPGetServiceName(%lu %p %lu)\n",
+          hServiceStatus, lpServiceName, cchServiceName);
+
+    for (i = 0; i < dwActiveServiceCount; i++)
+    {
+        if (lpActiveServices[i].hServiceStatus == hServiceStatus)
+        {
+            wcscpy(lpServiceName, lpActiveServices[i].ServiceName.Buffer);
+            return ERROR_SUCCESS;
+        }
+    }
+
+    return ERROR_SERVICE_NOT_IN_EXE;
+}
+
+
+/**********************************************************************
+ *  I_ScSetServiceBitsA
  *
  * Undocumented
  *
@@ -724,9 +904,12 @@ I_ScSetServiceBitsA(SERVICE_STATUS_HANDLE hServiceStatus,
 {
     BOOL bResult;
 
+    TRACE("I_ScSetServiceBitsA(%lu %lx %u %u %s)\n",
+          hServiceStatus, dwServiceBits, bSetBitsOn, bUpdateImmediately,
+          debugstr_a(lpString));
+
     RpcTryExcept
     {
-        /* Call to services.exe using RPC */
         bResult = RI_ScSetServiceBitsA((RPC_SERVICE_STATUS_HANDLE)hServiceStatus,
                                        dwServiceBits,
                                        bSetBitsOn,
@@ -745,7 +928,7 @@ I_ScSetServiceBitsA(SERVICE_STATUS_HANDLE hServiceStatus,
 
 
 /**********************************************************************
- *     I_ScSetServiceBitsW
+ *  I_ScSetServiceBitsW
  *
  * Undocumented
  *
@@ -760,9 +943,12 @@ I_ScSetServiceBitsW(SERVICE_STATUS_HANDLE hServiceStatus,
 {
     BOOL bResult;
 
+    TRACE("I_ScSetServiceBitsW(%lu %lx %u %u %s)\n",
+          hServiceStatus, dwServiceBits, bSetBitsOn, bUpdateImmediately,
+          debugstr_w(lpString));
+
     RpcTryExcept
     {
-        /* Call to services.exe using RPC */
         bResult = RI_ScSetServiceBitsW((RPC_SERVICE_STATUS_HANDLE)hServiceStatus,
                                        dwServiceBits,
                                        bSetBitsOn,
@@ -781,7 +967,7 @@ I_ScSetServiceBitsW(SERVICE_STATUS_HANDLE hServiceStatus,
 
 
 /**********************************************************************
- *     SetServiceBits
+ *  SetServiceBits
  *
  * @implemented
  */
@@ -791,6 +977,9 @@ SetServiceBits(SERVICE_STATUS_HANDLE hServiceStatus,
                BOOL bSetBitsOn,
                BOOL bUpdateImmediately)
 {
+    TRACE("SetServiceBits(%lu %lx %u %u)\n",
+          hServiceStatus, dwServiceBits, bSetBitsOn, bUpdateImmediately);
+
     return I_ScSetServiceBitsW(hServiceStatus,
                                dwServiceBits,
                                bSetBitsOn,
@@ -800,7 +989,7 @@ SetServiceBits(SERVICE_STATUS_HANDLE hServiceStatus,
 
 
 /**********************************************************************
- *     SetServiceStatus
+ *  SetServiceStatus
  *
  * @implemented
  */
@@ -810,12 +999,11 @@ SetServiceStatus(SERVICE_STATUS_HANDLE hServiceStatus,
 {
     DWORD dwError;
 
-    TRACE("SetServiceStatus() called\n");
-    TRACE("hServiceStatus %lu\n", hServiceStatus);
+    TRACE("SetServiceStatus(%lu %p)\n",
+          hServiceStatus, lpServiceStatus);
 
     RpcTryExcept
     {
-        /* Call to services.exe using RPC */
         dwError = RSetServiceStatus((RPC_SERVICE_STATUS_HANDLE)hServiceStatus,
                                     lpServiceStatus);
     }
@@ -827,19 +1015,19 @@ SetServiceStatus(SERVICE_STATUS_HANDLE hServiceStatus,
 
     if (dwError != ERROR_SUCCESS)
     {
-        ERR("ScmrSetServiceStatus() failed (Error %lu)\n", dwError);
+        ERR("RSetServiceStatus() failed (Error %lu)\n", dwError);
         SetLastError(dwError);
         return FALSE;
     }
 
-    TRACE("SetServiceStatus() done (ret %lu)\n", dwError);
+    TRACE("SetServiceStatus() done\n");
 
     return TRUE;
 }
 
 
 /**********************************************************************
- *     StartServiceCtrlDispatcherA
+ *  StartServiceCtrlDispatcherA
  *
  * @implemented
  */
@@ -853,7 +1041,8 @@ StartServiceCtrlDispatcherA(const SERVICE_TABLE_ENTRYA *lpServiceStartTable)
     DWORD dwBufSize;
     BOOL bRet = TRUE;
 
-    TRACE("StartServiceCtrlDispatcherA() called\n");
+    TRACE("StartServiceCtrlDispatcherA(%p)\n",
+          lpServiceStartTable);
 
     i = 0;
     while (lpServiceStartTable[i].lpServiceProc != NULL)
@@ -862,11 +1051,14 @@ StartServiceCtrlDispatcherA(const SERVICE_TABLE_ENTRYA *lpServiceStartTable)
     }
 
     dwActiveServiceCount = i;
+
+    /* Allocate the service table */
     lpActiveServices = RtlAllocateHeap(RtlGetProcessHeap(),
                                        HEAP_ZERO_MEMORY,
                                        dwActiveServiceCount * sizeof(ACTIVE_SERVICE));
     if (lpActiveServices == NULL)
     {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         return FALSE;
     }
 
@@ -875,12 +1067,13 @@ StartServiceCtrlDispatcherA(const SERVICE_TABLE_ENTRYA *lpServiceStartTable)
     {
         RtlCreateUnicodeStringFromAsciiz(&lpActiveServices[i].ServiceName,
                                          lpServiceStartTable[i].lpServiceName);
-        lpActiveServices[i].ThreadParams.A.lpServiceMain = lpServiceStartTable[i].lpServiceProc;
-        lpActiveServices[i].hServiceStatus = 0;
+        lpActiveServices[i].ServiceMain.A = lpServiceStartTable[i].lpServiceProc;
+        lpActiveServices[i].hServiceStatus = NULL;
         lpActiveServices[i].bUnicode = FALSE;
         lpActiveServices[i].bOwnProcess = FALSE;
     }
 
+    /* Connect to the SCM */
     dwError = ScConnectControlPipe(&hPipe);
     if (dwError != ERROR_SUCCESS)
     {
@@ -896,16 +1089,20 @@ StartServiceCtrlDispatcherA(const SERVICE_TABLE_ENTRYA *lpServiceStartTable)
                                     dwBufSize);
     if (ControlPacket == NULL)
     {
+        dwError = ERROR_NOT_ENOUGH_MEMORY;
         bRet = FALSE;
         goto done;
     }
 
     ScCreateStatusBinding();
 
+    /* Call the dispatcher loop */
     ScServiceDispatcher(hPipe, ControlPacket, dwBufSize);
 
+
     ScDestroyStatusBinding();
 
+    /* Close the connection */
     CloseHandle(hPipe);
 
     /* Free the control packet */
@@ -921,12 +1118,15 @@ done:
     lpActiveServices = NULL;
     dwActiveServiceCount = 0;
 
+    if (!bRet)
+        SetLastError(dwError);
+
     return bRet;
 }
 
 
 /**********************************************************************
- *     StartServiceCtrlDispatcherW
+ *  StartServiceCtrlDispatcherW
  *
  * @implemented
  */
@@ -940,7 +1140,8 @@ StartServiceCtrlDispatcherW(const SERVICE_TABLE_ENTRYW *lpServiceStartTable)
     DWORD dwBufSize;
     BOOL bRet = TRUE;
 
-    TRACE("StartServiceCtrlDispatcherW() called\n");
+    TRACE("StartServiceCtrlDispatcherW(%p)\n",
+          lpServiceStartTable);
 
     i = 0;
     while (lpServiceStartTable[i].lpServiceProc != NULL)
@@ -949,11 +1150,14 @@ StartServiceCtrlDispatcherW(const SERVICE_TABLE_ENTRYW *lpServiceStartTable)
     }
 
     dwActiveServiceCount = i;
+
+    /* Allocate the service table */
     lpActiveServices = RtlAllocateHeap(RtlGetProcessHeap(),
                                        HEAP_ZERO_MEMORY,
                                        dwActiveServiceCount * sizeof(ACTIVE_SERVICE));
     if (lpActiveServices == NULL)
     {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         return FALSE;
     }
 
@@ -962,12 +1166,13 @@ StartServiceCtrlDispatcherW(const SERVICE_TABLE_ENTRYW *lpServiceStartTable)
     {
         RtlCreateUnicodeString(&lpActiveServices[i].ServiceName,
                                lpServiceStartTable[i].lpServiceName);
-        lpActiveServices[i].ThreadParams.W.lpServiceMain = lpServiceStartTable[i].lpServiceProc;
-        lpActiveServices[i].hServiceStatus = 0;
+        lpActiveServices[i].ServiceMain.W = lpServiceStartTable[i].lpServiceProc;
+        lpActiveServices[i].hServiceStatus = NULL;
         lpActiveServices[i].bUnicode = TRUE;
         lpActiveServices[i].bOwnProcess = FALSE;
     }
 
+    /* Connect to the SCM */
     dwError = ScConnectControlPipe(&hPipe);
     if (dwError != ERROR_SUCCESS)
     {
@@ -983,16 +1188,19 @@ StartServiceCtrlDispatcherW(const SERVICE_TABLE_ENTRYW *lpServiceStartTable)
                                     dwBufSize);
     if (ControlPacket == NULL)
     {
+        dwError = ERROR_NOT_ENOUGH_MEMORY;
         bRet = FALSE;
         goto done;
     }
 
     ScCreateStatusBinding();
 
+    /* Call the dispatcher loop */
     ScServiceDispatcher(hPipe, ControlPacket, dwBufSize);
 
     ScDestroyStatusBinding();
 
+    /* Close the connection */
     CloseHandle(hPipe);
 
     /* Free the control packet */
@@ -1008,6 +1216,9 @@ done:
     lpActiveServices = NULL;
     dwActiveServiceCount = 0;
 
+    if (!bRet)
+        SetLastError(dwError);
+
     return bRet;
 }