[PATCH] wtsapi32: Implement WTSEnumerateProcessesW().

Zebediah Figura z.figura12 at gmail.com
Wed May 26 00:13:42 CDT 2021


Based on a patch by Sebastian Lackner.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=29903
Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/wtsapi32/tests/Makefile.in |   2 +-
 dlls/wtsapi32/tests/wtsapi.c    | 141 ++++++++++++++++++++++++++-----
 dlls/wtsapi32/wtsapi32.c        | 143 +++++++++++++++++++++++++++++---
 include/wtsapi32.h              |   1 +
 4 files changed, 252 insertions(+), 35 deletions(-)

diff --git a/dlls/wtsapi32/tests/Makefile.in b/dlls/wtsapi32/tests/Makefile.in
index be0fa8d6772..57014aea938 100644
--- a/dlls/wtsapi32/tests/Makefile.in
+++ b/dlls/wtsapi32/tests/Makefile.in
@@ -1,5 +1,5 @@
 TESTDLL   = wtsapi32.dll
-IMPORTS   = wtsapi32 advapi32
+IMPORTS   = wtsapi32 advapi32 psapi
 
 C_SRCS = \
 	wtsapi.c
diff --git a/dlls/wtsapi32/tests/wtsapi.c b/dlls/wtsapi32/tests/wtsapi.c
index 8ce1dc8bf9c..3eec81d8556 100644
--- a/dlls/wtsapi32/tests/wtsapi.c
+++ b/dlls/wtsapi32/tests/wtsapi.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include <ntstatus.h>
+#define WIN32_NO_STATUS
 #include <stdarg.h>
 #include <stdlib.h>
 #include <windef.h>
@@ -23,25 +25,93 @@
 #include <winternl.h>
 #include <lmcons.h>
 #include <wtsapi32.h>
+#define PSAPI_VERSION 1
+#include <psapi.h>
 
 #include "wine/test.h"
 
+static BOOL (WINAPI *pWTSEnumerateProcessesExW)(HANDLE server, DWORD *level, DWORD session, WCHAR **info, DWORD *count);
+static BOOL (WINAPI *pWTSFreeMemoryExW)(WTS_TYPE_CLASS class, void *memory, ULONG count);
+
+static const SYSTEM_PROCESS_INFORMATION *find_nt_process_info(const SYSTEM_PROCESS_INFORMATION *head, DWORD pid)
+{
+    for (;;)
+    {
+        if ((DWORD)(DWORD_PTR)head->UniqueProcessId == pid)
+            return head;
+        if (!head->NextEntryOffset)
+            break;
+        head = (SYSTEM_PROCESS_INFORMATION *)((char *)head + head->NextEntryOffset);
+    }
+    return NULL;
+}
+
+static void check_wts_process_info(const WTS_PROCESS_INFOW *info, DWORD count)
+{
+    ULONG nt_length = 1024;
+    SYSTEM_PROCESS_INFORMATION *nt_info = malloc(nt_length);
+    WCHAR process_name[MAX_PATH], *process_filepart;
+    BOOL ret, found = FALSE;
+    NTSTATUS status;
+    DWORD i;
+
+    GetModuleFileNameW(NULL, process_name, MAX_PATH);
+    process_filepart = wcsrchr(process_name, '\\') + 1;
+
+    while ((status = NtQuerySystemInformation(SystemProcessInformation, nt_info,
+            nt_length, NULL)) == STATUS_INFO_LENGTH_MISMATCH)
+    {
+        nt_length *= 2;
+        nt_info = realloc(nt_info, nt_length);
+    }
+    ok(!status, "got %#x\n", status);
+
+    for (i = 0; i < count; i++)
+    {
+        char sid_buffer[50];
+        SID_AND_ATTRIBUTES *sid = (SID_AND_ATTRIBUTES *)sid_buffer;
+        const SYSTEM_PROCESS_INFORMATION *nt_process;
+        HANDLE process, token;
+        DWORD size;
+
+        nt_process = find_nt_process_info(nt_info, info[i].ProcessId);
+        ok(!!nt_process, "failed to find pid %#x\n", info[i].ProcessId);
+
+        winetest_push_context("pid %#x", info[i].ProcessId);
+
+        ok(info[i].SessionId == nt_process->SessionId, "expected session id %#x, got %#x\n",
+                nt_process->SessionId, info[i].SessionId);
+
+        ok(!memcmp(info[i].pProcessName, nt_process->ProcessName.Buffer, nt_process->ProcessName.Length),
+                "expected process name %s, got %s\n",
+                debugstr_w(nt_process->ProcessName.Buffer), debugstr_w(info[i].pProcessName));
+
+        if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, info[i].ProcessId)))
+        {
+            ret = OpenProcessToken(process, TOKEN_QUERY, &token);
+            ok(ret, "failed to open token, error %u\n", GetLastError());
+            ret = GetTokenInformation(token, TokenUser, sid_buffer, sizeof(sid_buffer), &size);
+            ok(ret, "failed to get token user, error %u\n", GetLastError());
+            ok(EqualSid(info[i].pUserSid, sid->Sid), "SID did not match\n");
+            CloseHandle(token);
+            CloseHandle(process);
+        }
+
+        winetest_pop_context();
+
+        found = found || !wcscmp(info[i].pProcessName, process_filepart);
+    }
+
+    ok(found, "did not find current process\n");
+
+    free(nt_info);
+}
+
 static void test_WTSEnumerateProcessesW(void)
 {
-    BOOL found = FALSE, ret;
-    DWORD count, i;
     PWTS_PROCESS_INFOW info;
-    WCHAR *pname, nameW[MAX_PATH];
-
-    GetModuleFileNameW(NULL, nameW, MAX_PATH);
-    for (pname = nameW + lstrlenW(nameW); pname > nameW; pname--)
-    {
-        if(*pname == '/' || *pname == '\\')
-        {
-            pname++;
-            break;
-        }
-    }
+    DWORD count, level;
+    BOOL ret;
 
     info = NULL;
     SetLastError(0xdeadbeef);
@@ -80,15 +150,41 @@ static void test_WTSEnumerateProcessesW(void)
     info = NULL;
     SetLastError(0xdeadbeef);
     ret = WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &info, &count);
-    ok(ret || broken(!ret), /* fails on Win2K with error ERROR_APP_WRONG_OS */
-        "expected WTSEnumerateProcessesW to succeed; failed with %d\n", GetLastError());
-    for(i = 0; ret && i < count; i++)
-    {
-        found = found || !lstrcmpW(pname, info[i].pProcessName);
-    }
-    todo_wine
-    ok(found || broken(!ret), "process name %s not found\n", wine_dbgstr_w(pname));
+    ok(ret, "expected success\n");
+    ok(!GetLastError(), "got error %u\n", GetLastError());
+    check_wts_process_info(info, count);
     WTSFreeMemory(info);
+
+    if (!pWTSEnumerateProcessesExW)
+    {
+        skip("WTSEnumerateProcessesEx is not available\n");
+        return;
+    }
+
+    level = 0;
+
+    SetLastError(0xdeadbeef);
+    count = 0xdeadbeef;
+    ret = pWTSEnumerateProcessesExW(WTS_CURRENT_SERVER_HANDLE, &level, WTS_ANY_SESSION, NULL, &count);
+    ok(!ret, "expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %u\n", GetLastError());
+    ok(count == 0xdeadbeef, "got count %u\n", count);
+
+    info = (void *)0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = pWTSEnumerateProcessesExW(WTS_CURRENT_SERVER_HANDLE, &level, WTS_ANY_SESSION, (WCHAR **)&info, NULL);
+    ok(!ret, "expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %u\n", GetLastError());
+    ok(info == (void *)0xdeadbeef, "got info %p\n", info);
+
+    info = NULL;
+    count = 0;
+    SetLastError(0xdeadbeef);
+    ret = pWTSEnumerateProcessesExW(WTS_CURRENT_SERVER_HANDLE, &level, WTS_ANY_SESSION, (WCHAR **)&info, &count);
+    ok(ret, "expected success\n");
+    ok(!GetLastError(), "got error %u\n", GetLastError());
+    check_wts_process_info(info, count);
+    pWTSFreeMemoryExW(WTSTypeProcessInfoLevel0, info, count);
 }
 
 static void test_WTSQuerySessionInformation(void)
@@ -201,6 +297,9 @@ static void test_WTSQueryUserToken(void)
 
 START_TEST (wtsapi)
 {
+    pWTSEnumerateProcessesExW = (void *)GetProcAddress(GetModuleHandleA("wtsapi32"), "WTSEnumerateProcessesExW");
+    pWTSFreeMemoryExW = (void *)GetProcAddress(GetModuleHandleA("wtsapi32"), "WTSFreeMemoryExW");
+
     test_WTSEnumerateProcessesW();
     test_WTSQuerySessionInformation();
     test_WTSQueryUserToken();
diff --git a/dlls/wtsapi32/wtsapi32.c b/dlls/wtsapi32/wtsapi32.c
index a5c9bf6519f..d766223df99 100644
--- a/dlls/wtsapi32/wtsapi32.c
+++ b/dlls/wtsapi32/wtsapi32.c
@@ -15,10 +15,13 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
 #include <stdarg.h>
 #include <stdlib.h>
 #include "windef.h"
 #include "winbase.h"
+#include "winternl.h"
 #include "winnls.h"
 #include "lmcons.h"
 #include "wtsapi32.h"
@@ -76,11 +79,127 @@ BOOL WINAPI WTSEnableChildSessions(BOOL enable)
 /************************************************************
  *                WTSEnumerateProcessesExW  (WTSAPI32.@)
  */
-BOOL WINAPI WTSEnumerateProcessesExW(HANDLE server, DWORD *level, DWORD session_id, WCHAR **info, DWORD *count)
+BOOL WINAPI WTSEnumerateProcessesExW(HANDLE server, DWORD *level, DWORD session_id,
+        WCHAR **ret_info, DWORD *ret_count)
 {
-    FIXME("Stub %p %p %d %p %p\n", server, level, session_id, info, count);
-    if (count) *count = 0;
-    return FALSE;
+    SYSTEM_PROCESS_INFORMATION *nt_info, *nt_process;
+    WTS_PROCESS_INFOW *info;
+    ULONG nt_size = 4096;
+    DWORD count, size;
+    NTSTATUS status;
+    char *p;
+
+    TRACE("server %p, level %u, session_id %#x, ret_info %p, ret_count %p\n",
+            server, *level, session_id, ret_info, ret_count);
+
+    if (!ret_info || !ret_count)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    if (session_id != WTS_ANY_SESSION)
+        FIXME("ignoring session id %#x\n", session_id);
+
+    if (*level)
+    {
+        FIXME("unhandled level %u\n", *level);
+        SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+        return FALSE;
+    }
+
+    if (!(nt_info = malloc(nt_size)))
+    {
+        SetLastError(ERROR_OUTOFMEMORY);
+        return FALSE;
+    }
+
+    while ((status = NtQuerySystemInformation(SystemProcessInformation, nt_info,
+            nt_size, NULL)) == STATUS_INFO_LENGTH_MISMATCH)
+    {
+        SYSTEM_PROCESS_INFORMATION *new_info;
+
+        nt_size *= 2;
+        if (!(new_info = realloc(nt_info, nt_size)))
+        {
+            free(nt_info);
+            SetLastError(ERROR_OUTOFMEMORY);
+            return FALSE;
+        }
+        nt_info = new_info;
+    }
+    if (status)
+    {
+        free(nt_info);
+        SetLastError(RtlNtStatusToDosError(status));
+        return FALSE;
+    }
+
+    size = 0;
+    count = 0;
+    nt_process = nt_info;
+    for (;;)
+    {
+        size += sizeof(WTS_PROCESS_INFOW) + nt_process->ProcessName.Length + sizeof(WCHAR);
+        size += offsetof(SID, SubAuthority[SID_MAX_SUB_AUTHORITIES]);
+        ++count;
+
+        if (!nt_process->NextEntryOffset)
+            break;
+        nt_process = (SYSTEM_PROCESS_INFORMATION *)((char *)nt_process + nt_process->NextEntryOffset);
+    }
+
+    if (!(info = heap_alloc(size)))
+    {
+        free(nt_info);
+        SetLastError(ERROR_OUTOFMEMORY);
+        return FALSE;
+    }
+    p = (char *)(info + count);
+
+    count = 0;
+    nt_process = nt_info;
+    for (;;)
+    {
+        HANDLE process, token;
+
+        info[count].SessionId = nt_process->SessionId;
+        info[count].ProcessId = (DWORD_PTR)nt_process->UniqueProcessId;
+
+        info[count].pProcessName = (WCHAR *)p;
+        memcpy(p, nt_process->ProcessName.Buffer, nt_process->ProcessName.Length);
+        info[count].pProcessName[nt_process->ProcessName.Length / sizeof(WCHAR)] = 0;
+        p += nt_process->ProcessName.Length + sizeof(WCHAR);
+
+        info[count].pUserSid = NULL;
+        if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, info[count].ProcessId)))
+        {
+            if (OpenProcessToken(process, TOKEN_QUERY, &token))
+            {
+                char buffer[sizeof(TOKEN_USER) + offsetof(SID, SubAuthority[SID_MAX_SUB_AUTHORITIES])];
+                TOKEN_USER *user = (TOKEN_USER *)buffer;
+                DWORD size;
+
+                GetTokenInformation(token, TokenUser, buffer, sizeof(buffer), &size);
+                info[count].pUserSid = p;
+                size = GetLengthSid(user->User.Sid);
+                memcpy(p, user->User.Sid, size);
+                p += size;
+                CloseHandle(token);
+            }
+            CloseHandle(process);
+        }
+
+        ++count;
+        if (!nt_process->NextEntryOffset)
+            break;
+        nt_process = (SYSTEM_PROCESS_INFORMATION *)((char *)nt_process + nt_process->NextEntryOffset);
+    }
+
+    *ret_info = (WCHAR *)info;
+    *ret_count = count;
+    SetLastError(0);
+    return TRUE;
 }
 
 /************************************************************
@@ -113,22 +232,20 @@ BOOL WINAPI WTSEnumerateProcessesA(HANDLE hServer, DWORD Reserved, DWORD Version
 /************************************************************
  *                WTSEnumerateProcessesW  (WTSAPI32.@)
  */
-BOOL WINAPI WTSEnumerateProcessesW(HANDLE hServer, DWORD Reserved, DWORD Version,
-    PWTS_PROCESS_INFOW* ppProcessInfo, DWORD* pCount)
+BOOL WINAPI WTSEnumerateProcessesW(HANDLE server, DWORD reserved, DWORD version,
+        WTS_PROCESS_INFOW **info, DWORD *count)
 {
-    FIXME("Stub %p 0x%08x 0x%08x %p %p\n", hServer, Reserved, Version,
-          ppProcessInfo, pCount);
+    DWORD level = 0;
 
-    if (!ppProcessInfo || !pCount || Reserved != 0 || Version != 1)
+    TRACE("server %p, reserved %#x, version %u, info %p, count %p\n", server, reserved, version, info, count);
+
+    if (reserved || version != 1)
     {
         SetLastError(ERROR_INVALID_PARAMETER);
         return FALSE;
     }
 
-    *pCount = 0;
-    *ppProcessInfo = NULL;
-
-    return TRUE;
+    return WTSEnumerateProcessesExW(server, &level, WTS_ANY_SESSION, (WCHAR **)info, count);
 }
 
 /************************************************************
diff --git a/include/wtsapi32.h b/include/wtsapi32.h
index d9b40ca519c..1bd5b6cd791 100644
--- a/include/wtsapi32.h
+++ b/include/wtsapi32.h
@@ -171,6 +171,7 @@ DECL_WINELIB_TYPE_AW(PWTS_SERVER_INFO)
 
 #define WTS_CURRENT_SERVER_HANDLE ((HANDLE)NULL)
 #define WTS_CURRENT_SESSION (~0u)
+#define WTS_ANY_SESSION ((DWORD)-2)
 
 void WINAPI WTSCloseServer(HANDLE);
 BOOL WINAPI WTSConnectSessionA(ULONG, ULONG, PSTR, BOOL);
-- 
2.30.2




More information about the wine-devel mailing list