Author: hbelusca Date: Fri Dec 2 20:01:29 2016 New Revision: 73413 URL: http://svn.reactos.org/svn/reactos?rev=73413&view=rev Log: [ROSTESTS]: advapi32_apitest: Add a test to extensively test services command-line arguments. By Thomas Faber. [ADVAPI32]: Correctly set up both the ANSI and UNICODE service command-line arguments. Adapted from a patch by Thomas Faber. Thanks!
[SERVICES] - Correctly pack the service command-line arguments in the control packet structure. In particular, the offsets stored in the vector are relative to the beginning of the vector (and not relative to the previous offset ^^). Improve comments... - Fix the definition of the SCM_CONTROL_PACKET control packet structure to make it Win2k3-compatible, so that we can use Win2k3' advapi32.dll or services.exe on ReactOS and vice-versa. CORE-9235 CORE-9838 Added: trunk/rostests/apitests/advapi32/ServiceArgs.c (with props) Modified: trunk/reactos/base/system/services/database.c trunk/reactos/dll/win32/advapi32/service/sctrl.c trunk/reactos/sdk/include/reactos/services/services.h trunk/rostests/apitests/advapi32/CMakeLists.txt trunk/rostests/apitests/advapi32/testlist.c Modified: trunk/reactos/base/system/services/database.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/base/system/services/database.c?rev=73413&r1=73412&r2=73413&view=diff ============================================================================== --- trunk/reactos/base/system/services/database.c [iso-8859-1] (original) +++ trunk/reactos/base/system/services/database.c [iso-8859-1] Fri Dec 2 20:01:29 2016 @@ -1216,17 +1216,17 @@ DWORD argc, LPWSTR* argv) { + DWORD dwError = ERROR_SUCCESS; PSCM_CONTROL_PACKET ControlPacket; SCM_REPLY_PACKET ReplyPacket; DWORD PacketSize; + DWORD i; PWSTR Ptr; - DWORD dwWriteCount = 0; - DWORD dwReadCount = 0; - DWORD dwError = ERROR_SUCCESS; - DWORD i; PWSTR *pOffPtr; PWSTR pArgPtr; BOOL bResult; + DWORD dwWriteCount = 0; + DWORD dwReadCount = 0; #ifdef USE_ASYNCHRONOUS_IO OVERLAPPED Overlapped = {0}; #endif @@ -1234,26 +1234,32 @@ DPRINT("ScmSendStartCommand() called\n"); /* Calculate the total length of the start command line */ - PacketSize = sizeof(SCM_CONTROL_PACKET) + - (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR)); - - /* Calculate the required packet size for the start arguments */ + PacketSize = sizeof(SCM_CONTROL_PACKET); + PacketSize += (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR)); + + /* + * Calculate the required packet size for the start argument vector 'argv', + * composed of the list of pointer offsets, followed by UNICODE strings. + * The strings are stored continuously after the vector of offsets, with + * the offsets being relative to the beginning of the vector, as in the + * following layout (with N == argc): + * [argOff(0)]...[argOff(N-1)][str(0)]...[str(N-1)] . + */ if (argc > 0 && argv != NULL) { - PacketSize = ALIGN_UP(PacketSize, LPWSTR); + PacketSize = ALIGN_UP(PacketSize, PWSTR); + PacketSize += (argc * sizeof(PWSTR)); DPRINT("Argc: %lu\n", argc); for (i = 0; i < argc; i++) { DPRINT("Argv[%lu]: %S\n", i, argv[i]); - PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR) + sizeof(PWSTR)); + PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR)); } } /* Allocate a control packet */ - ControlPacket = HeapAlloc(GetProcessHeap(), - HEAP_ZERO_MEMORY, - PacketSize); + ControlPacket = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, PacketSize); if (ControlPacket == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -1262,22 +1268,23 @@ ? SERVICE_CONTROL_START_OWN : SERVICE_CONTROL_START_SHARE; ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service; + + /* Copy the start command line */ ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET); - - Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset); + Ptr = (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset); wcscpy(Ptr, Service->lpServiceName); - ControlPacket->dwArgumentsCount = 0; + ControlPacket->dwArgumentsCount = 0; ControlPacket->dwArgumentsOffset = 0; - /* Copy argument list */ + /* Copy the argument vector */ if (argc > 0 && argv != NULL) { Ptr += wcslen(Service->lpServiceName) + 1; pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR); pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR)); - ControlPacket->dwArgumentsCount = argc; + ControlPacket->dwArgumentsCount = argc; ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket); DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount); @@ -1285,12 +1292,10 @@ for (i = 0; i < argc; i++) { - wcscpy(pArgPtr, argv[i]); - *pOffPtr = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr); - DPRINT("offset: %p\n", *pOffPtr); - - pArgPtr += wcslen(argv[i]) + 1; - pOffPtr++; + wcscpy(pArgPtr, argv[i]); + pOffPtr[i] = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr); + DPRINT("offset[%lu]: %p\n", i, pOffPtr[i]); + pArgPtr += wcslen(argv[i]) + 1; } } Modified: trunk/reactos/dll/win32/advapi32/service/sctrl.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/advapi32/service/sctrl.c?rev=73413&r1=73412&r2=73413&view=diff ============================================================================== --- trunk/reactos/dll/win32/advapi32/service/sctrl.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/advapi32/service/sctrl.c [iso-8859-1] Fri Dec 2 20:01:29 2016 @@ -278,58 +278,88 @@ } +/* + * 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; - LPWSTR pszServiceName; + 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; - pszServiceName = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset); - cbServiceName = lstrlenW(pszServiceName) * sizeof(WCHAR) + sizeof(UNICODE_NULL); - - cbTotal = cbServiceName + sizeof(LPWSTR); + /* 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) - cbTotal += ControlPacket->dwSize - ControlPacket->dwArgumentsOffset; - - lpVector = HeapAlloc(GetProcessHeap(), - HEAP_ZERO_MEMORY, - cbTotal); + 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_OUTOFMEMORY; - - lpArg = lpVector; - *lpArg = (LPWSTR)(lpArg + 1); - lpArg++; - - memcpy(lpArg, pszServiceName, cbServiceName); - lpArg = (LPWSTR*)((ULONG_PTR)lpArg + cbServiceName); - + return ERROR_NOT_ENOUGH_MEMORY; + + /* + * 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(lpArg, - ((PBYTE)ControlPacket + ControlPacket->dwArgumentsOffset), - ControlPacket->dwSize - ControlPacket->dwArgumentsOffset); + memcpy(&lpVector[1], + (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwArgumentsOffset), + cbArguments); 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 + 1; + /* 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; @@ -341,101 +371,48 @@ LPDWORD lpArgCount, LPSTR **lpArgVector) { - LPSTR *lpVector; - LPSTR *lpPtr; - LPWSTR lpUnicodeString; - LPWSTR pszServiceName; - LPSTR lpAnsiString; - DWORD cbServiceName; - DWORD dwVectorSize; - DWORD dwUnicodeSize; - DWORD dwAnsiSize = 0; - DWORD dwAnsiNameSize = 0; - 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; - pszServiceName = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset); - cbServiceName = lstrlenW(pszServiceName) * sizeof(WCHAR) + sizeof(UNICODE_NULL); - - dwAnsiNameSize = WideCharToMultiByte(CP_ACP, - 0, - pszServiceName, - cbServiceName, - NULL, - 0, - NULL, - NULL); - - dwVectorSize = ControlPacket->dwArgumentsCount * sizeof(LPWSTR); - if (ControlPacket->dwArgumentsCount > 0) - { - 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); - } - - dwVectorSize += sizeof(LPWSTR); - - lpVector = HeapAlloc(GetProcessHeap(), - HEAP_ZERO_MEMORY, - dwVectorSize + dwAnsiNameSize + dwAnsiSize); - if (lpVector == NULL) - return ERROR_OUTOFMEMORY; - - lpPtr = (LPSTR*)lpVector; - lpAnsiString = (LPSTR)((ULONG_PTR)lpVector + dwVectorSize); - - WideCharToMultiByte(CP_ACP, - 0, - pszServiceName, - cbServiceName, - lpAnsiString, - dwAnsiNameSize, - NULL, - NULL); - - if (ControlPacket->dwArgumentsCount > 0) - { - lpAnsiString = (LPSTR)((ULONG_PTR)lpAnsiString + dwAnsiNameSize); - - WideCharToMultiByte(CP_ACP, - 0, - lpUnicodeString, - dwUnicodeSize, - lpAnsiString, - dwAnsiSize, - NULL, - NULL); - } - - lpAnsiString = (LPSTR)((ULONG_PTR)lpVector + dwVectorSize); - for (i = 0; i < ControlPacket->dwArgumentsCount + 1; i++) - { - *lpPtr = lpAnsiString; - - lpPtr++; - lpAnsiString += (strlen(lpAnsiString) + 1); - } - - *lpArgCount = ControlPacket->dwArgumentsCount + 1; - *lpArgVector = lpVector; + /* Build the UNICODE arguments vector */ + dwError = ScBuildUnicodeArgsVector(ControlPacket, &ArgCount, &lpVectorW); + if (dwError != ERROR_SUCCESS) + return dwError; + + /* 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)) + { + /* Failed to convert to ANSI; free the allocated vector and return */ + dwError = RtlNtStatusToDosError(Status); + HeapFree(GetProcessHeap(), 0, lpVectorW); + return dwError; + } + + /* 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; } Modified: trunk/reactos/sdk/include/reactos/services/services.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/sdk/include/reactos/services/services.h?rev=73413&r1=73412&r2=73413&view=diff ============================================================================== --- trunk/reactos/sdk/include/reactos/services/services.h [iso-8859-1] (original) +++ trunk/reactos/sdk/include/reactos/services/services.h [iso-8859-1] Fri Dec 2 20:01:29 2016 @@ -29,9 +29,9 @@ { DWORD dwSize; DWORD dwControl; + DWORD dwArgumentsCount; SERVICE_STATUS_HANDLE hServiceStatus; DWORD dwServiceNameOffset; - DWORD dwArgumentsCount; DWORD dwArgumentsOffset; } SCM_CONTROL_PACKET, *PSCM_CONTROL_PACKET; Modified: trunk/rostests/apitests/advapi32/CMakeLists.txt URL: http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/advapi32/CMakeLists.txt?rev=73413&r1=73412&r2=73413&view=diff ============================================================================== --- trunk/rostests/apitests/advapi32/CMakeLists.txt [iso-8859-1] (original) +++ trunk/rostests/apitests/advapi32/CMakeLists.txt [iso-8859-1] Fri Dec 2 20:01:29 2016 @@ -13,6 +13,7 @@ RegQueryValueExW.c RtlEncryptMemory.c SaferIdentifyLevel.c + ServiceArgs.c testlist.c) add_executable(advapi32_apitest ${SOURCE}) Added: trunk/rostests/apitests/advapi32/ServiceArgs.c URL: http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/advapi32/ServiceArgs.c?rev=73413 ============================================================================== --- trunk/rostests/apitests/advapi32/ServiceArgs.c (added) +++ trunk/rostests/apitests/advapi32/ServiceArgs.c [iso-8859-1] Fri Dec 2 20:01:29 2016 @@ -0,0 +1,475 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: Test for service arguments + * PROGRAMMER: Jacek Caban for CodeWeavers + * Thomas Faber <thomas.fa...@reactos.org> + */ + +#include <apitest.h> +#include <winnls.h> +#include <winsvc.h> +#include <strsafe.h> + +static char **argv; +static int argc; + +static HANDLE pipe_handle = INVALID_HANDLE_VALUE; +static char service_nameA[100]; +static WCHAR service_nameW[100]; +static WCHAR named_pipe_name[100]; + +/* Test process global variables */ +static SC_HANDLE scm_handle; +static SERVICE_STATUS_HANDLE service_handle; + +static void send_msg(const char *type, const char *msg) +{ + DWORD written = 0; + char buf[512]; + + StringCbPrintfA(buf, sizeof(buf), "%s:%s", type, msg); + WriteFile(pipe_handle, buf, strlen(buf)+1, &written, NULL); +} + +static inline void service_trace(const char *msg, ...) +{ + va_list valist; + char buf[512]; + + va_start(valist, msg); + StringCbVPrintfA(buf, sizeof(buf), msg, valist); + va_end(valist); + + send_msg("TRACE", buf); +} + +static void service_ok(int cnd, const char *msg, ...) +{ + va_list valist; + char buf[512]; + + va_start(valist, msg); + StringCbVPrintfA(buf, sizeof(buf), msg, valist); + va_end(valist); + + send_msg(cnd ? "OK" : "FAIL", buf); +} + +static VOID WINAPI service_handler(DWORD ctrl) +{ + SERVICE_STATUS status; + + status.dwServiceType = SERVICE_WIN32; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + status.dwWin32ExitCode = 0; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + + switch(ctrl) + { + case SERVICE_CONTROL_STOP: + status.dwCurrentState = SERVICE_STOP_PENDING; + status.dwControlsAccepted = 0; + SetServiceStatus(service_handle, &status); + default: + status.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(service_handle, &status); + } +} + +static void service_main_common(void) +{ + SERVICE_STATUS status; + BOOL res; + + service_handle = RegisterServiceCtrlHandlerW(service_nameW, service_handler); + service_ok(service_handle != NULL, "RegisterServiceCtrlHandler failed: %lu\n", GetLastError()); + if (!service_handle) + return; + + status.dwServiceType = SERVICE_WIN32; + status.dwCurrentState = SERVICE_RUNNING; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = 0; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 0; + status.dwWaitHint = 10000; + res = SetServiceStatus(service_handle, &status); + service_ok(res, "SetServiceStatus(SERVICE_RUNNING) failed: %lu", GetLastError()); + + Sleep(100); + + status.dwCurrentState = SERVICE_STOPPED; + status.dwControlsAccepted = 0; + res = SetServiceStatus(service_handle, &status); + service_ok(res, "SetServiceStatus(SERVICE_STOPPED) failed: %lu", GetLastError()); +} + +/* + * A/W argument layout XP/2003: + * [argv array of pointers][parameter 1][parameter 2]...[service name] + * + * A/W argument layout Vista: + * [argv array of pointers][align to 8 bytes][parameter 1][parameter 2]...[service name] + * + * A/W argument layout 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 + * the WCHAR version including null terminator. + */ + +static void WINAPI service_mainA(DWORD service_argc, char **service_argv) +{ + int i; + char *next_arg; + char *next_arg_aligned; + + service_ok(service_argc == argc - 3, "service_argc = %d, expected %d", service_argc, argc - 3); + if (service_argc == argc - 3) + { + service_ok(!strcmp(service_argv[0], service_nameA), "service_argv[0] = %s, expected %s", service_argv[0], service_nameA); + service_ok(service_argv[0] == (char *)&service_argv[service_argc] || + service_argv[1] == (char *)&service_argv[service_argc] || + service_argv[1] == (char *)(((ULONG_PTR)&service_argv[service_argc] + 7) & ~7), "service_argv[0] = %p, [1] = %p, expected one of them to be %p", service_argv[0], service_argv[1], &service_argv[service_argc]); + //service_trace("service_argv[0] = %p", service_argv[0]); + next_arg_aligned = next_arg = NULL; + for (i = 1; i < service_argc; i++) + { + //service_trace("service_argv[%d] = %p", i, service_argv[i]); + service_ok(!strcmp(service_argv[i], argv[i + 3]), "service_argv[%d] = %s, expected %s", i, service_argv[i], argv[i + 3]); + service_ok(next_arg == NULL || + service_argv[i] == next_arg || + service_argv[i] == next_arg_aligned, "service_argv[%d] = %p, expected %p or %p", i, service_argv[i], next_arg, next_arg_aligned); + next_arg = service_argv[i]; + next_arg += 2 * (strlen(next_arg) + 1); + next_arg_aligned = (char *)(((ULONG_PTR)next_arg + 3) & ~3); + } + } + + service_main_common(); +} + +static void WINAPI service_mainW(DWORD service_argc, WCHAR **service_argv) +{ + int i; + WCHAR argW[32]; + WCHAR *next_arg; + WCHAR *next_arg_aligned; + + service_ok(service_argc == argc - 3, "service_argc = %d, expected %d", service_argc, argc - 3); + if (service_argc == argc - 3) + { + service_ok(!wcscmp(service_argv[0], service_nameW), "service_argv[0] = %ls, expected %ls", service_argv[0], service_nameW); + service_ok(service_argv[0] == (WCHAR *)&service_argv[service_argc] || + service_argv[1] == (WCHAR *)&service_argv[service_argc] || + service_argv[1] == (WCHAR *)(((ULONG_PTR)&service_argv[service_argc] + 7) & ~7), "service_argv[0] = %p, [1] = %p, expected one of them to be %p", service_argv[0], service_argv[1], &service_argv[service_argc]); + //service_trace("service_argv[0] = %p", service_argv[0]); + next_arg_aligned = next_arg = NULL; + for (i = 1; i < service_argc; i++) + { + //service_trace("service_argv[%d] = %p", i, service_argv[i]); + MultiByteToWideChar(CP_ACP, 0, argv[i + 3], -1, argW, _countof(argW)); + service_ok(!wcscmp(service_argv[i], argW), "service_argv[%d] = %ls, expected %ls", i, service_argv[i], argW); + service_ok(next_arg == NULL || + service_argv[i] == next_arg || + service_argv[i] == next_arg_aligned, "service_argv[%d] = %p, expected %p or %p", i, service_argv[i], next_arg, next_arg_aligned); + next_arg = service_argv[i]; + next_arg += wcslen(next_arg) + 1; + next_arg_aligned = (WCHAR *)(((ULONG_PTR)next_arg + 3) & ~3); + } + } + + service_main_common(); +} + +static void service_process(BOOLEAN unicode) +{ + BOOL res; + + SERVICE_TABLE_ENTRYA servtblA[] = + { + { service_nameA, service_mainA }, + { NULL, NULL } + }; + SERVICE_TABLE_ENTRYW servtblW[] = + { + { service_nameW, service_mainW }, + { NULL, NULL } + }; + + res = WaitNamedPipeW(named_pipe_name, NMPWAIT_USE_DEFAULT_WAIT); + if (!res) + return; + + pipe_handle = CreateFileW(named_pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (pipe_handle == INVALID_HANDLE_VALUE) + return; + + //service_trace("Starting..."); + if (unicode) + { + res = StartServiceCtrlDispatcherW(servtblW); + service_ok(res, "StartServiceCtrlDispatcherW failed: %lu\n", GetLastError()); + } + else + { + res = StartServiceCtrlDispatcherA(servtblA); + service_ok(res, "StartServiceCtrlDispatcherA failed: %lu\n", GetLastError()); + } + + CloseHandle(pipe_handle); +} + +static SC_HANDLE register_service(PCWSTR extra_args) +{ + WCHAR service_cmd[MAX_PATH+150]; + SC_HANDLE service; + + GetModuleFileNameW(NULL, service_cmd, MAX_PATH); + + StringCbCatW(service_cmd, sizeof(service_cmd), L" ServiceArgs "); + StringCbCatW(service_cmd, sizeof(service_cmd), service_nameW); + StringCbCatW(service_cmd, sizeof(service_cmd), extra_args); + + trace("service_cmd \"%ls\"\n", service_cmd); + + service = CreateServiceW(scm_handle, service_nameW, service_nameW, GENERIC_ALL, + SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, + service_cmd, NULL, NULL, NULL, NULL, NULL); + if (!service && GetLastError() == ERROR_ACCESS_DENIED) + { + skip("Not enough access right to create service\n"); + return NULL; + } + + ok(service != NULL, "CreateService failed: %lu\n", GetLastError()); + return service; +} + +static DWORD WINAPI pipe_thread(void *arg) +{ + char buf[512]; + DWORD read; + BOOL res; + + res = ConnectNamedPipe(pipe_handle, NULL); + ok(res || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe failed: %lu\n", GetLastError()); + + while (1) + { + res = ReadFile(pipe_handle, buf, sizeof(buf), &read, NULL); + if (!res) + { + ok(GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_INVALID_HANDLE, + "ReadFile failed: %lu\n", GetLastError()); + break; + } + + if (!strncmp(buf, "TRACE:", 6)) + { + trace("service trace: %s\n", buf+6); + } + else if (!strncmp(buf, "OK:", 3)) + { + ok(1, "service: %s\n", buf+3); + } + else if (!strncmp(buf, "FAIL:", 5)) + { + ok(0, "service: %s\n", buf+5); + } + else + { + ok(0, "malformed service message: %s\n", buf); + } + } + + DisconnectNamedPipe(pipe_handle); + //trace("pipe disconnected\n"); + return 0; +} + +static void test_startA(SC_HANDLE service_handle, int service_argc, const char **service_argv) +{ + SERVICE_STATUS status; + BOOL res; + + res = StartServiceA(service_handle, service_argc, service_argv); + ok(res, "StartService failed: %lu\n", GetLastError()); + if (!res) + return; + + do + { + Sleep(100); + ZeroMemory(&status, sizeof(status)); + res = QueryServiceStatus(service_handle, &status); + } while (res && status.dwCurrentState != SERVICE_STOPPED); + ok(res, "QueryServiceStatus failed: %lu\n", GetLastError()); + ok(status.dwCurrentState == SERVICE_STOPPED, "status.dwCurrentState = %lx\n", status.dwCurrentState); +} + +static void test_startW(SC_HANDLE service_handle, int service_argc, const WCHAR **service_argv) +{ + SERVICE_STATUS status; + BOOL res; + + res = StartServiceW(service_handle, service_argc, service_argv); + ok(res, "StartService failed: %lu\n", GetLastError()); + if (!res) + return; + + do + { + Sleep(100); + ZeroMemory(&status, sizeof(status)); + res = QueryServiceStatus(service_handle, &status); + } while (res && status.dwCurrentState != SERVICE_STOPPED); + ok(res, "QueryServiceStatus failed: %lu\n", GetLastError()); + ok(status.dwCurrentState == SERVICE_STOPPED, "status.dwCurrentState = %lx\n", status.dwCurrentState); +} + +static void test_runner(BOOLEAN unicode, PCWSTR extra_args, int service_argc, void *service_argv) +{ + HANDLE thread; + SC_HANDLE service_handle; + BOOL res; + + StringCbPrintfW(service_nameW, sizeof(service_nameW), L"WineTestService%lu", GetTickCount()); + WideCharToMultiByte(CP_ACP, 0, service_nameW, -1, service_nameA, _countof(service_nameA), NULL, NULL); + //trace("service_name: %ls\n", service_nameW); + StringCbPrintfW(named_pipe_name, sizeof(named_pipe_name), L"\\\\.\\pipe\\%ls_pipe", service_nameW); + + pipe_handle = CreateNamedPipeW(named_pipe_name, PIPE_ACCESS_INBOUND, + PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT, 10, 2048, 2048, 10000, NULL); + ok(pipe_handle != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError()); + if (pipe_handle == INVALID_HANDLE_VALUE) + return; + + thread = CreateThread(NULL, 0, pipe_thread, NULL, 0, NULL); + ok(thread != NULL, "CreateThread failed: %lu\n", GetLastError()); + if (!thread) + return; + + service_handle = register_service(extra_args); + if (!service_handle) + return; + + //trace("starting...\n"); + + if (unicode) + test_startW(service_handle, service_argc, service_argv); + else + test_startA(service_handle, service_argc, service_argv); + + res = DeleteService(service_handle); + ok(res, "DeleteService failed: %lu\n", GetLastError()); + + CloseServiceHandle(service_handle); + + ok(WaitForSingleObject(thread, 10000) == WAIT_OBJECT_0, "Timeout waiting for thread\n"); + CloseHandle(thread); + CloseHandle(pipe_handle); +} + +START_TEST(ServiceArgs) +{ + argc = winetest_get_mainargs(&argv); + + scm_handle = OpenSCManagerW(NULL, NULL, GENERIC_ALL); + ok(scm_handle != NULL, "OpenSCManager failed: %lu\n", GetLastError()); + if (!scm_handle) + { + skip("Failed to open service control manager. Skipping test\n"); + return; + } + + if (argc < 3) + { + char *service_argvA[10]; + WCHAR *service_argvW[10]; + + test_runner(FALSE, L" A", 0, NULL); + test_runner(FALSE, L" W", 0, NULL); + test_runner(TRUE, L" A", 0, NULL); + test_runner(TRUE, L" W", 0, NULL); + + service_argvA[0] = "x"; + service_argvW[0] = L"x"; + test_runner(FALSE, L" A x", 1, service_argvA); + test_runner(FALSE, L" W x", 1, service_argvA); + test_runner(TRUE, L" A x", 1, service_argvW); + test_runner(TRUE, L" W x", 1, service_argvW); + + service_argvA[1] = "y"; + service_argvW[1] = L"y"; + test_runner(FALSE, L" A x y", 2, service_argvA); + test_runner(FALSE, L" W x y", 2, service_argvA); + test_runner(TRUE, L" A x y", 2, service_argvW); + test_runner(TRUE, L" W x y", 2, service_argvW); + + service_argvA[0] = "ab"; + service_argvW[0] = L"ab"; + test_runner(FALSE, L" A ab y", 2, service_argvA); + test_runner(FALSE, L" W ab y", 2, service_argvA); + test_runner(TRUE, L" A ab y", 2, service_argvW); + test_runner(TRUE, L" W ab y", 2, service_argvW); + + service_argvA[0] = "abc"; + service_argvW[0] = L"abc"; + test_runner(FALSE, L" A abc y", 2, service_argvA); + test_runner(FALSE, L" W abc y", 2, service_argvA); + test_runner(TRUE, L" A abc y", 2, service_argvW); + test_runner(TRUE, L" W abc y", 2, service_argvW); + + service_argvA[0] = "abcd"; + service_argvW[0] = L"abcd"; + test_runner(FALSE, L" A abcd y", 2, service_argvA); + test_runner(FALSE, L" W abcd y", 2, service_argvA); + test_runner(TRUE, L" A abcd y", 2, service_argvW); + test_runner(TRUE, L" W abcd y", 2, service_argvW); + + service_argvA[0] = "abcde"; + service_argvW[0] = L"abcde"; + test_runner(FALSE, L" A abcde y", 2, service_argvA); + test_runner(FALSE, L" W abcde y", 2, service_argvA); + test_runner(TRUE, L" A abcde y", 2, service_argvW); + test_runner(TRUE, L" W abcde y", 2, service_argvW); + + service_argvA[0] = "abcdef"; + service_argvW[0] = L"abcdef"; + test_runner(FALSE, L" A abcdef y", 2, service_argvA); + test_runner(FALSE, L" W abcdef y", 2, service_argvA); + test_runner(TRUE, L" A abcdef y", 2, service_argvW); + test_runner(TRUE, L" W abcdef y", 2, service_argvW); + + service_argvA[0] = "abcdefg"; + service_argvW[0] = L"abcdefg"; + test_runner(FALSE, L" A abcdefg y", 2, service_argvA); + test_runner(FALSE, L" W abcdefg y", 2, service_argvA); + test_runner(TRUE, L" A abcdefg y", 2, service_argvW); + test_runner(TRUE, L" W abcdefg y", 2, service_argvW); + + service_argvA[0] = ""; + service_argvW[0] = L""; + test_runner(FALSE, L" A \"\" y", 2, service_argvA); + test_runner(FALSE, L" W \"\" y", 2, service_argvA); + test_runner(TRUE, L" A \"\" y", 2, service_argvW); + test_runner(TRUE, L" W \"\" y", 2, service_argvW); + } + else + { + strcpy(service_nameA, argv[2]); + MultiByteToWideChar(CP_ACP, 0, service_nameA, -1, service_nameW, _countof(service_nameW)); + StringCbPrintfW(named_pipe_name, sizeof(named_pipe_name), L"\\\\.\\pipe\\%ls_pipe", service_nameW); + if (!strcmp(argv[3], "A")) + service_process(FALSE); + else + service_process(TRUE); + } + + CloseServiceHandle(scm_handle); +} Propchange: trunk/rostests/apitests/advapi32/ServiceArgs.c ------------------------------------------------------------------------------ svn:eol-style = native Modified: trunk/rostests/apitests/advapi32/testlist.c URL: http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/advapi32/testlist.c?rev=73413&r1=73412&r2=73413&view=diff ============================================================================== --- trunk/rostests/apitests/advapi32/testlist.c [iso-8859-1] (original) +++ trunk/rostests/apitests/advapi32/testlist.c [iso-8859-1] Fri Dec 2 20:01:29 2016 @@ -16,6 +16,7 @@ extern void func_RegQueryValueExW(void); extern void func_RtlEncryptMemory(void); extern void func_SaferIdentifyLevel(void); +extern void func_ServiceArgs(void); const struct test winetest_testlist[] = { @@ -32,7 +33,7 @@ { "RegQueryValueExW", func_RegQueryValueExW }, { "RtlEncryptMemory", func_RtlEncryptMemory }, { "SaferIdentifyLevel", func_SaferIdentifyLevel }, - + { "ServiceArgs", func_ServiceArgs }, { 0, 0 } };