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/datab…
==============================================================================
--- 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…
==============================================================================
--- 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/servic…
==============================================================================
--- 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/CMakeLi…
==============================================================================
--- 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/Service…
==============================================================================
--- 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.faber(a)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/testlis…
==============================================================================
--- 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 }
};