This series implements kernelbase.EnumProcessModulesEx().
This series: - implements EnumProcessModulesEx() - ensure it passes a bunch of new tests - fix GetModuleBaseName, GetModuleFileNameEx, GetModuleInformation() to cope with HMODULES returned from EnumProcessModulesEx().
Notes: as EnumProcessModules() doesn't return the same list as Windows on a Wow64 setup, and that there's no use of EnumProcessModulesEx() in Wine code, this first series will implement EnumProcessModulesEx() and if accepted, it will be followed by a second serie which, firstly, adapt all places in Wine code with bad expectations on EnumProcessModules(), and secondly reimlement EnumProcessModules() using EnumProcessModulesEx().
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- include/psapi.h | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/include/psapi.h b/include/psapi.h index 66e660b2435..6dcb783934e 100644 --- a/include/psapi.h +++ b/include/psapi.h @@ -137,6 +137,12 @@ extern "C" { #define GetProcessImageFileNameW K32GetProcessImageFileNameW #endif
+/* filter flags for EnumProcessModulesEx */ +#define LIST_MODULES_32BIT 0x01 +#define LIST_MODULES_64BIT 0x02 +#define LIST_MODULES_ALL 0x03 +#define LIST_MODULES_DEFAULT 0x00 + BOOL WINAPI EnumProcesses(DWORD*, DWORD, DWORD*); BOOL WINAPI EnumProcessModules(HANDLE, HMODULE*, DWORD, LPDWORD); BOOL WINAPI EnumProcessModulesEx(HANDLE, HMODULE*, DWORD, LPDWORD, DWORD);
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/psapi/tests/psapi_main.c | 62 ++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 27 deletions(-)
diff --git a/dlls/psapi/tests/psapi_main.c b/dlls/psapi/tests/psapi_main.c index 185a4062092..cd260a844b5 100644 --- a/dlls/psapi/tests/psapi_main.c +++ b/dlls/psapi/tests/psapi_main.c @@ -143,33 +143,41 @@ static void test_EnumProcessModules(void)
strcpy(buffer, "C:\windows\syswow64\notepad.exe"); ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); - ok(ret, "CreateProcess failed: %lu\n", GetLastError()); - - ret = WaitForInputIdle(pi.hProcess, 5000); - ok(!ret, "wait timed out\n"); - - SetLastError(0xdeadbeef); - hMod = NULL; - ret = EnumProcessModules(pi.hProcess, &hMod, sizeof(HMODULE), &cbNeeded); - ok(ret == 1, "got %ld, error %lu\n", ret, GetLastError()); - ok(!!hMod, "expected non-NULL module\n"); - ok(cbNeeded % sizeof(hMod) == 0, "got %lu\n", cbNeeded); - - ret = GetModuleBaseNameA(pi.hProcess, hMod, name, sizeof(name)); - ok(ret, "got error %lu\n", GetLastError()); - ok(!strcmp(name, "notepad.exe"), "got %s\n", name); - - ret = GetModuleFileNameExA(pi.hProcess, hMod, name, sizeof(name)); - ok(ret, "got error %lu\n", GetLastError()); - ok(!strcmp(name, buffer), "got %s\n", name); - - ret = GetModuleInformation(pi.hProcess, hMod, &info, sizeof(info)); - ok(ret, "got error %lu\n", GetLastError()); - ok(info.lpBaseOfDll == hMod, "expected %p, got %p\n", hMod, info.lpBaseOfDll); - ok(info.SizeOfImage, "image size was 0\n"); - ok(info.EntryPoint >= info.lpBaseOfDll, "got entry point %p\n", info.EntryPoint); - - TerminateProcess(pi.hProcess, 0); + if (ret) + { + ret = WaitForInputIdle(pi.hProcess, 5000); + ok(!ret, "wait timed out\n"); + + SetLastError(0xdeadbeef); + hMod = NULL; + ret = EnumProcessModules(pi.hProcess, &hMod, sizeof(HMODULE), &cbNeeded); + ok(ret == 1, "got %ld, error %lu\n", ret, GetLastError()); + ok(!!hMod, "expected non-NULL module\n"); + ok(cbNeeded % sizeof(hMod) == 0, "got %lu\n", cbNeeded); + + ret = GetModuleBaseNameA(pi.hProcess, hMod, name, sizeof(name)); + ok(ret, "got error %lu\n", GetLastError()); + ok(!strcmp(name, "notepad.exe"), "got %s\n", name); + + ret = GetModuleFileNameExA(pi.hProcess, hMod, name, sizeof(name)); + ok(ret, "got error %lu\n", GetLastError()); + ok(!strcmp(name, buffer), "got %s\n", name); + + ret = GetModuleInformation(pi.hProcess, hMod, &info, sizeof(info)); + ok(ret, "got error %lu\n", GetLastError()); + ok(info.lpBaseOfDll == hMod, "expected %p, got %p\n", hMod, info.lpBaseOfDll); + ok(info.SizeOfImage, "image size was 0\n"); + ok(info.EntryPoint >= info.lpBaseOfDll, "got entry point %p\n", info.EntryPoint); + + TerminateProcess(pi.hProcess, 0); + } + else + { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + skip("Skip wow64 test on non compatible platform\n"); + else + ok(ret, "CreateProcess failed: %lu\n", GetLastError()); + } } else if (wow64) {
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/psapi/tests/psapi_main.c | 315 ++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+)
diff --git a/dlls/psapi/tests/psapi_main.c b/dlls/psapi/tests/psapi_main.c index cd260a844b5..3ab5a2a5e7d 100644 --- a/dlls/psapi/tests/psapi_main.c +++ b/dlls/psapi/tests/psapi_main.c @@ -43,6 +43,7 @@ static BOOL (WINAPI *pWow64RevertWow64FsRedirection)(void *); static BOOL (WINAPI *pQueryWorkingSetEx)(HANDLE, PVOID, DWORD);
static BOOL wow64; +static char** main_argv;
static BOOL init_func_ptrs(void) { @@ -199,6 +200,318 @@ static void test_EnumProcessModules(void) } }
+struct moduleex_snapshot +{ + unsigned list; + DWORD num_modules; + HMODULE modules[128]; +}; + +static BOOL test_EnumProcessModulesEx_snapshot(HANDLE proc, struct moduleex_snapshot* mxsnap, + unsigned numsnap) +{ + DWORD needed; + char buffer[80]; + MODULEINFO info; + int i, j; + BOOL ret; + + for (i = 0; i < numsnap; i++) + { + winetest_push_context("%d", mxsnap[i].list); + SetLastError(0xdeadbeef); + mxsnap[i].modules[0] = (void *)0xdeadbeef; + ret = EnumProcessModulesEx(proc, mxsnap[i].modules, sizeof(mxsnap[i].modules), &needed, mxsnap[i].list); + ok(ret, "didn't succeed %lu\n", GetLastError()); + ok(needed % sizeof(HMODULE) == 0, "not a multiple of sizeof(HMODULE) cbNeeded=%ld\n", needed); + mxsnap[i].num_modules = min(needed, sizeof(mxsnap[i].modules)) / sizeof(HMODULE); + for (j = 0; j < mxsnap[i].num_modules; j++) + { + ret = GetModuleBaseNameA(proc, mxsnap[i].modules[j], buffer, sizeof(buffer)); + ok(ret, "GetModuleBaseName failed: %lu (%u/%lu=%p)\n", GetLastError(), j, mxsnap[i].num_modules, mxsnap[i].modules[j]); + ret = GetModuleFileNameExA(proc, mxsnap[i].modules[j], buffer, sizeof(buffer)); + ok(ret, "GetModuleFileNameEx failed: %lu (%u/%lu=%p)\n", GetLastError(), j, mxsnap[i].num_modules, mxsnap[i].modules[j]); + memset(&info, 0, sizeof(info)); + ret = GetModuleInformation(proc, mxsnap[i].modules[j], &info, sizeof(info)); + ok(ret, "GetModuleInformation failed: %lu\n", GetLastError()); + ok(info.lpBaseOfDll == mxsnap[i].modules[j], "expected %p, got %p\n", mxsnap[i].modules[j], info.lpBaseOfDll); + ok(info.SizeOfImage, "image size was 0\n"); + /* info.EntryPoint to be checked */ + } + winetest_pop_context(); + } + return ret; +} + +static BOOL snapshot_is_empty(const struct moduleex_snapshot* snap) +{ + return snap->num_modules == 0; +} + +static BOOL snapshot_contains(const struct moduleex_snapshot* snap, HMODULE mod) +{ + int i; + + for (i = 0; i < snap->num_modules; i++) + if (snap->modules[i] == mod) return TRUE; + return FALSE; +} + +/* It happens (experienced on Windows) that the considered process still loads modules, + * meaning that the number of loaded modules can increase between consecutive calls to EnumProcessModulesEx. + * In order to cope with this, we're testing for modules list being included into the next one (instead of + * equality) + */ +static BOOL snapshot_is_subset(const struct moduleex_snapshot* subset, const struct moduleex_snapshot* superset) +{ + int i; + + for (i = 0; i < subset->num_modules; i++) + if (!snapshot_contains(superset, subset->modules[i])) return FALSE; + return TRUE; +} + +static BOOL snapshot_is_equal(const struct moduleex_snapshot* seta, const struct moduleex_snapshot* setb) +{ + return snapshot_is_subset(seta, setb) && seta->num_modules == setb->num_modules; +} + +static BOOL snapshot_are_disjoint(const struct moduleex_snapshot* seta, const struct moduleex_snapshot* setb, unsigned start) +{ + int i, j; + for (i = start; i < seta->num_modules; i++) + for (j = start; j < setb->num_modules; j++) + if (seta->modules[i] == setb->modules[j]) return FALSE; + return TRUE; +} + +static void snapshot_check_first_main_module(const struct moduleex_snapshot* snap, HANDLE proc, + const char* filename) +{ + char buffer[80]; + MODULEINFO info; + const char* modname; + BOOL ret; + + if (!(modname = strrchr(filename, '\'))) modname = filename; else modname++; + winetest_push_context("%d", snap->list); + ret = GetModuleBaseNameA(proc, snap->modules[0], buffer, sizeof(buffer)); + ok(ret, "got error %lu\n", GetLastError()); + ok(!strcasecmp(buffer, modname), "expecting %s but got %s\n", modname, buffer); + ret = GetModuleFileNameExA(proc, snap->modules[0], buffer, sizeof(buffer)); + ok(ret, "got error %lu\n", GetLastError()); + ok(!strcasecmp(filename, buffer), "expecting %s but got %s\n", filename, buffer); + + ret = GetModuleInformation(proc, snap->modules[0], &info, sizeof(info)); + ok(ret, "got error %lu\n", GetLastError()); + ok(info.lpBaseOfDll == snap->modules[0], "expected %p, got %p\n", snap->modules[0], info.lpBaseOfDll); + ok(info.SizeOfImage, "image size was 0\n"); + ok(info.EntryPoint >= info.lpBaseOfDll, "got entry point %p\n", info.EntryPoint); + winetest_pop_context(); +} + +static void test_EnumProcessModulesEx(void) +{ + char buffer[200] = "C:\windows\system32\notepad.exe"; + PROCESS_INFORMATION pi = {0}; + STARTUPINFOA si = {0}; + void *cookie; + HMODULE hMod; + DWORD ret, cbNeeded = 0xdeadbeef; + struct moduleex_snapshot snap[4] = {{LIST_MODULES_32BIT}, {LIST_MODULES_64BIT}, {LIST_MODULES_DEFAULT}, {LIST_MODULES_ALL}}; + int i; + + SetLastError(0xdeadbeef); + EnumProcessModulesEx(NULL, NULL, 0, &cbNeeded, LIST_MODULES_ALL); + ok(GetLastError() == ERROR_INVALID_HANDLE, "expected error=ERROR_INVALID_HANDLE but got %ld\n", GetLastError()); + + SetLastError(0xdeadbeef); + EnumProcessModulesEx(hpQI, NULL, 0, &cbNeeded, LIST_MODULES_ALL); + ok(GetLastError() == ERROR_ACCESS_DENIED, "expected error=ERROR_ACCESS_DENIED but got %ld\n", GetLastError()); + + SetLastError(0xdeadbeef); + hMod = (void *)0xdeadbeef; + ret = EnumProcessModulesEx(hpQI, &hMod, sizeof(HMODULE), NULL, LIST_MODULES_ALL); + ok(!ret, "succeeded\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "expected error=ERROR_ACCESS_DENIED but got %ld\n", GetLastError()); + + SetLastError(0xdeadbeef); + hMod = (void *)0xdeadbeef; + ret = EnumProcessModulesEx(hpQV, &hMod, sizeof(HMODULE), NULL, LIST_MODULES_ALL); + ok(!ret, "succeeded\n"); + ok(GetLastError() == ERROR_NOACCESS, "expected error=ERROR_NOACCESS but got %ld\n", GetLastError()); + ok(hMod == GetModuleHandleA(NULL), + "hMod=%p GetModuleHandleA(NULL)=%p\n", hMod, GetModuleHandleA(NULL)); + + SetLastError(0xdeadbeef); + ret = EnumProcessModulesEx(hpQV, NULL, 0, &cbNeeded, LIST_MODULES_ALL); + ok(ret == 1, "failed with %ld\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = EnumProcessModulesEx(hpQV, NULL, sizeof(HMODULE), &cbNeeded, LIST_MODULES_ALL); + ok(!ret, "succeeded\n"); + ok(GetLastError() == ERROR_NOACCESS, "expected error=ERROR_NOACCESS but got %ld\n", GetLastError()); + + winetest_push_context("self"); + if (sizeof(void *) == 8) + { + test_EnumProcessModulesEx_snapshot(hpQV, snap, ARRAY_SIZE(snap)); + todo_wine + ok(snapshot_is_empty(&snap[0]), "didn't expect 32bit module\n"); + ok(snapshot_is_equal(&snap[1], &snap[2]), "mismatch in modules count\n"); + ok(snapshot_is_equal(&snap[2], &snap[3]), "mismatch in modules count\n"); + snapshot_check_first_main_module(&snap[1], hpQV, main_argv[0]); + snapshot_check_first_main_module(&snap[2], hpQV, main_argv[0]); + snapshot_check_first_main_module(&snap[3], hpQV, main_argv[0]); + + /* in fact, this error is only returned when (list & 3 == 0), otherwise the corresponding + * list is returned without errors + */ + SetLastError(0xdeadbeef); + ret = EnumProcessModulesEx(hpQV, &hMod, sizeof(HMODULE), &cbNeeded, 0x400); + todo_wine + ok(!ret, "succeeded\n"); + todo_wine + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected error=ERROR_INVALID_PARAMETER but got %ld\n", GetLastError()); + } + else if (wow64) + { + test_EnumProcessModulesEx_snapshot(hpQV, snap, ARRAY_SIZE(snap)); + ok(snapshot_is_equal(&snap[0], &snap[1]), "mismatch in modules count\n"); + ok(snapshot_is_equal(&snap[1], &snap[2]), "mismatch in modules count\n"); + ok(snapshot_is_equal(&snap[2], &snap[3]), "mismatch in modules count\n"); + snapshot_check_first_main_module(&snap[0], hpQV, main_argv[0]); + snapshot_check_first_main_module(&snap[1], hpQV, main_argv[0]); + snapshot_check_first_main_module(&snap[2], hpQV, main_argv[0]); + snapshot_check_first_main_module(&snap[3], hpQV, main_argv[0]); + } + winetest_pop_context(); + + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "CreateProcess failed: %lu\n", GetLastError()); + + ret = WaitForInputIdle(pi.hProcess, 5000); + ok(!ret, "wait timed out\n"); + + if (sizeof(void *) == 8) + { + winetest_push_context("pcs-6464"); + test_EnumProcessModulesEx_snapshot(pi.hProcess, snap, ARRAY_SIZE(snap)); + todo_wine + ok(snapshot_is_empty(&snap[0]), "didn't expect 32bit module\n"); + ok(snapshot_is_subset(&snap[1], &snap[2]), "64bit and default module lists should match\n"); + ok(snapshot_is_subset(&snap[2], &snap[3]), "default and all module lists should match\n"); + snapshot_check_first_main_module(&snap[1], pi.hProcess, buffer); + snapshot_check_first_main_module(&snap[2], pi.hProcess, buffer); + snapshot_check_first_main_module(&snap[3], pi.hProcess, buffer); + winetest_pop_context(); + + /* in fact, this error is only returned when (list & 3 == 0), otherwise the corresponding + * list is returned without errors + */ + SetLastError(0xdeadbeef); + ret = EnumProcessModulesEx(hpQV, &hMod, sizeof(HMODULE), &cbNeeded, 0x400); + todo_wine + ok(!ret, "succeeded\n"); + todo_wine + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected error=ERROR_INVALID_PARAMETER but got %ld\n", GetLastError()); + } + else if (wow64) + { + winetest_push_context("pcs-3232"); + test_EnumProcessModulesEx_snapshot(pi.hProcess, snap, ARRAY_SIZE(snap)); + /* some windows version return 64bit modules, others don't... */ + /* ok(snapshot_is_empty(&snap[1]), "didn't expect 64bit module\n"); */ + ok(snapshot_is_subset(&snap[1], &snap[3]), "64 and all module lists should match\n"); + + ok(snapshot_is_subset(&snap[0], &snap[2]), "32bit and default module lists should match\n"); + ok(snapshot_is_subset(&snap[2], &snap[3]), "default and all module lists should match\n"); + snapshot_check_first_main_module(&snap[0], pi.hProcess, "c:\windows\syswow64\notepad.exe"); + snapshot_check_first_main_module(&snap[2], pi.hProcess, "c:\windows\syswow64\notepad.exe"); + snapshot_check_first_main_module(&snap[3], pi.hProcess, "c:\windows\syswow64\notepad.exe"); + winetest_pop_context(); + } + + TerminateProcess(pi.hProcess, 0); + + if (sizeof(void *) == 8) + { + strcpy(buffer, "C:\windows\syswow64\notepad.exe"); + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + if (ret) + { + ret = WaitForInputIdle(pi.hProcess, 5000); + ok(!ret, "wait timed out\n"); + + winetest_push_context("pcs-6432"); + test_EnumProcessModulesEx_snapshot(pi.hProcess, snap, ARRAY_SIZE(snap)); + ok(!snapshot_is_empty(&snap[0]), "expecting 32bit modules\n"); + ok(!snapshot_is_empty(&snap[1]), "expecting 64bit modules\n"); + ok(snapshot_is_subset(&snap[1], &snap[2]), "64bit and default module lists should match\n"); + todo_wine + ok(snapshot_are_disjoint(&snap[0], &snap[1], 0), "32bit and 64bit list should be disjoint\n"); + /* Main module (even 32bit) is present in both 32bit (makes sense) but also default + * (even if all the other modules are 64bit) + */ + todo_wine + ok(snapshot_are_disjoint(&snap[0], &snap[2], 1), "32bit and default list should be disjoint\n"); + ok(snapshot_is_subset(&snap[0], &snap[3]), "32bit and all module lists should match\n"); + ok(snapshot_is_subset(&snap[1], &snap[3]), "64bit and all module lists should match\n"); + ok(snapshot_is_subset(&snap[2], &snap[3]), "default and all module list should match\n"); + snapshot_check_first_main_module(&snap[0], pi.hProcess, buffer); + todo_wine + ok(!snapshot_contains(&snap[1], snap[0].modules[0]), "main module shouldn't be present in 64bit list\n"); + snapshot_check_first_main_module(&snap[2], pi.hProcess, buffer); + snapshot_check_first_main_module(&snap[3], pi.hProcess, buffer); + + /* in fact, this error is only returned when (list & 3 == 0), otherwise the corresponding + * list is returned without errors. + */ + SetLastError(0xdeadbeef); + ret = EnumProcessModulesEx(hpQV, &hMod, sizeof(HMODULE), &cbNeeded, 0x400); + todo_wine + ok(!ret, "succeeded\n"); + todo_wine + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected error=ERROR_INVALID_PARAMETER but got %ld\n", GetLastError()); + + winetest_pop_context(); + + TerminateProcess(pi.hProcess, 0); + } + else + { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + skip("Skip wow64 test on non compatible platform\n"); + else + ok(ret, "CreateProcess failed: %lu\n", GetLastError()); + } + + } + else if (wow64) + { + pWow64DisableWow64FsRedirection(&cookie); + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + pWow64RevertWow64FsRedirection(cookie); + ok(ret, "CreateProcess failed: %lu\n", GetLastError()); + + ret = WaitForInputIdle(pi.hProcess, 5000); + ok(!ret, "wait timed out\n"); + + winetest_push_context("pcs-3264"); + for (i = 0; i < ARRAY_SIZE(snap); i++) + { + SetLastError(0xdeadbeef); + ret = EnumProcessModulesEx(pi.hProcess, &hMod, sizeof(HMODULE), &cbNeeded, snap[i].list); + ok(!ret, "succeeded\n"); + todo_wine + ok(GetLastError() == ERROR_PARTIAL_COPY, "expected error=ERROR_PARTIAL_COPY but got %ld\n", GetLastError()); + } + winetest_pop_context(); + + TerminateProcess(pi.hProcess, 0); + } +} + static void test_GetModuleInformation(void) { HMODULE hMod = GetModuleHandleA(NULL); @@ -862,6 +1175,7 @@ START_TEST(psapi_main) { DWORD pid = GetCurrentProcessId();
+ winetest_get_mainargs(&main_argv); init_func_ptrs();
if (pIsWow64Process) @@ -878,6 +1192,7 @@ START_TEST(psapi_main)
test_EnumProcesses(); test_EnumProcessModules(); + test_EnumProcessModulesEx(); test_GetModuleInformation(); test_GetPerformanceInfo(); test_GetProcessMemoryInfo();
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/kernelbase/debug.c | 149 ++++++++++++++++++++++++++++++++-- dlls/psapi/tests/psapi_main.c | 31 ++++--- 2 files changed, 159 insertions(+), 21 deletions(-)
diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c index cd8e0d7f87d..6645b82a660 100644 --- a/dlls/kernelbase/debug.c +++ b/dlls/kernelbase/debug.c @@ -772,19 +772,19 @@ struct module_iterator };
-static BOOL init_module_iterator( struct module_iterator *iter, HANDLE process ) +/* Caller must ensure that wow64=TRUE is only passed from 64bit for 'process' being a wow64 process */ +static BOOL init_module_iterator( struct module_iterator *iter, HANDLE process, BOOL wow64 ) { PROCESS_BASIC_INFORMATION pbi; PPEB_LDR_DATA ldr_data;
- if (!IsWow64Process( process, &iter->wow64 )) return FALSE; - /* get address of PEB */ if (!set_ntstatus( NtQueryInformationProcess( process, ProcessBasicInformation, &pbi, sizeof(pbi), NULL ))) return FALSE;
- if (is_win64 && iter->wow64) + iter->wow64 = wow64; + if (wow64) { PEB_LDR_DATA32 *ldr_data32_ptr; DWORD ldr_data32, first_module; @@ -807,6 +807,12 @@ static BOOL init_module_iterator( struct module_iterator *iter, HANDLE process ) if (!ReadProcessMemory( process, &pbi.PebBaseAddress->LdrData, &ldr_data, sizeof(ldr_data), NULL )) return FALSE;
+ /* This happens when running "old" wow64 configuration. Mark it as such. */ + if (!ldr_data) + { + SetLastError( ERROR_EMPTY ); + return FALSE; + } /* read address of first module from LdrData */ if (!ReadProcessMemory( process, &ldr_data->InLoadOrderModuleList.Flink, &iter->current, sizeof(iter->current), NULL )) @@ -849,7 +855,7 @@ static BOOL get_ldr_module( HANDLE process, HMODULE module, LDR_DATA_TABLE_ENTRY struct module_iterator iter; INT ret;
- if (!init_module_iterator( &iter, process )) return FALSE; + if (!init_module_iterator( &iter, process, FALSE )) return FALSE;
while ((ret = module_iterator_next( &iter )) > 0) /* When hModule is NULL we return the process image - which will be @@ -870,7 +876,7 @@ static BOOL get_ldr_module32( HANDLE process, HMODULE module, LDR_DATA_TABLE_ENT struct module_iterator iter; INT ret;
- if (!init_module_iterator( &iter, process )) return FALSE; + if (!init_module_iterator( &iter, process, TRUE )) return FALSE;
while ((ret = module_iterator_next( &iter )) > 0) /* When hModule is NULL we return the process image - which will be @@ -939,6 +945,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH EnumProcessModules( HANDLE process, HMODULE *modul { struct module_iterator iter; DWORD size = 0; + BOOL target_wow64; INT ret;
if (process == GetCurrentProcess()) @@ -972,7 +979,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH EnumProcessModules( HANDLE process, HMODULE *modul return TRUE; }
- if (!init_module_iterator( &iter, process )) return FALSE; + if (!IsWow64Process( process, &target_wow64 )) return FALSE; + if (!init_module_iterator( &iter, process, is_win64 && target_wow64 )) return FALSE;
if (count && !module) { @@ -1003,6 +1011,41 @@ BOOL WINAPI DECLSPEC_HOTPATCH EnumProcessModules( HANDLE process, HMODULE *modul }
+struct module_push +{ + HMODULE *module; + unsigned count; + unsigned size; +}; + +static void module_push( struct module_push *mp, HMODULE module ) +{ + if (mp->count >= sizeof(HMODULE)) + { + *mp->module++ = module; + mp->count -= sizeof(HMODULE); + } + mp->size += sizeof(HMODULE); +} + +static void module_push_iter( struct module_push *mp, struct module_iterator *iter ) +{ + if (sizeof(void *) == 8 && iter->wow64) + module_push( mp, (HMODULE) (DWORD_PTR)iter->ldr_module32.BaseAddress ); + else + module_push( mp, iter->ldr_module.DllBase ); +} + +static int module_push_all( struct module_push *mp, struct module_iterator *iter ) +{ + int ret; + + while ((ret = module_iterator_next( iter )) > 0) + module_push_iter( mp, iter ); + + return ret; +} + /*********************************************************************** * EnumProcessModulesEx (kernelbase.@) * K32EnumProcessModulesEx (kernelbase.@) @@ -1010,8 +1053,96 @@ BOOL WINAPI DECLSPEC_HOTPATCH EnumProcessModules( HANDLE process, HMODULE *modul BOOL WINAPI EnumProcessModulesEx( HANDLE process, HMODULE *module, DWORD count, DWORD *needed, DWORD filter ) { - FIXME( "(%p, %p, %ld, %p, %ld) semi-stub\n", process, module, count, needed, filter ); - return EnumProcessModules( process, module, count, needed ); + struct module_push mp = {module, count, 0}; + unsigned list_mode; + BOOL target_wow64; + INT ret = 0; + + TRACE( "(%p, %p, %ld, %p, %ld)\n", process, module, count, needed, filter ); + + if (process != GetCurrentProcess()) + { + if (!IsWow64Process( process, &target_wow64 )) return FALSE; + } + else target_wow64 = is_wow64; + + if (filter & ~LIST_MODULES_ALL) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + list_mode = filter & LIST_MODULES_ALL; + /* Can't access 64bit process from (wow64) 32bit */ + if (is_wow64 && !target_wow64) + { + SetLastError( ERROR_PARTIAL_COPY ); + return FALSE; + } + if (count && !module) + { + SetLastError( ERROR_NOACCESS ); + return FALSE; + } + + if (process == GetCurrentProcess()) + { + if (!(is_win64 && list_mode == LIST_MODULES_32BIT)) + { + PPEB_LDR_DATA ldr_data = NtCurrentTeb()->Peb->LdrData; + PLIST_ENTRY head = &ldr_data->InLoadOrderModuleList; + PLIST_ENTRY entry = head->Flink; + + while (entry != head) + { + LDR_DATA_TABLE_ENTRY *ldr = CONTAINING_RECORD( entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks ); + module_push( &mp, ldr->DllBase ); + entry = entry->Flink; + } + } + } + else + { + struct module_iterator iter; + + if (is_win64 && target_wow64 && (list_mode & LIST_MODULES_32BIT)) + { + if (!init_module_iterator( &iter, process, TRUE ) || module_push_all( &mp, &iter ) < 0) + return FALSE; + } + if (!(is_win64 && list_mode == LIST_MODULES_32BIT)) + { + if (init_module_iterator( &iter, process, FALSE )) + { + if (is_win64 && target_wow64 && (list_mode & LIST_MODULES_64BIT)) + /* Don't add main module twice in _ALL mode */ + ret = module_iterator_next( &iter ); + if (ret >= 0) ret = module_push_all( &mp, &iter ); + } + else if (GetLastError() == ERROR_EMPTY) + { + /* We're running on "old" wow configuration. + * Fallback to PEB32 to get at least main module if requested. + */ + if (list_mode == LIST_MODULES_DEFAULT) + { + if (init_module_iterator( &iter, process, TRUE ) && module_iterator_next( &iter ) > 0) + module_push_iter( &mp, &iter ); + else + ret = -1; + } + } + else + return FALSE; + } + } + + if (!needed) + { + SetLastError( ERROR_NOACCESS ); + return FALSE; + } + *needed = mp.size; + return ret == 0; }
diff --git a/dlls/psapi/tests/psapi_main.c b/dlls/psapi/tests/psapi_main.c index 3ab5a2a5e7d..71eadc7114e 100644 --- a/dlls/psapi/tests/psapi_main.c +++ b/dlls/psapi/tests/psapi_main.c @@ -215,6 +215,10 @@ static BOOL test_EnumProcessModulesEx_snapshot(HANDLE proc, struct moduleex_snap MODULEINFO info; int i, j; BOOL ret; + BOOL fail, wow64; + + ret = IsWow64Process(proc, &wow64); + ok(ret, "IsWow64Process failed: %lu\n", GetLastError());
for (i = 0; i < numsnap; i++) { @@ -227,16 +231,29 @@ static BOOL test_EnumProcessModulesEx_snapshot(HANDLE proc, struct moduleex_snap mxsnap[i].num_modules = min(needed, sizeof(mxsnap[i].modules)) / sizeof(HMODULE); for (j = 0; j < mxsnap[i].num_modules; j++) { + /* temporary todo until GetModuleBaseName and friends are fixed */ + if ((fail = sizeof(void*) == 8 && wow64)) + switch (mxsnap[i].list) + { + case LIST_MODULES_32BIT: fail = FALSE; break; + case LIST_MODULES_DEFAULT: fail = j >= 1; break; + case LIST_MODULES_ALL: fail = j >= mxsnap[0].num_modules; break; + case LIST_MODULES_64BIT: break; + } ret = GetModuleBaseNameA(proc, mxsnap[i].modules[j], buffer, sizeof(buffer)); + todo_wine_if(fail) ok(ret, "GetModuleBaseName failed: %lu (%u/%lu=%p)\n", GetLastError(), j, mxsnap[i].num_modules, mxsnap[i].modules[j]); ret = GetModuleFileNameExA(proc, mxsnap[i].modules[j], buffer, sizeof(buffer)); + todo_wine_if(fail) ok(ret, "GetModuleFileNameEx failed: %lu (%u/%lu=%p)\n", GetLastError(), j, mxsnap[i].num_modules, mxsnap[i].modules[j]); memset(&info, 0, sizeof(info)); ret = GetModuleInformation(proc, mxsnap[i].modules[j], &info, sizeof(info)); + todo_wine_if(fail) { ok(ret, "GetModuleInformation failed: %lu\n", GetLastError()); ok(info.lpBaseOfDll == mxsnap[i].modules[j], "expected %p, got %p\n", mxsnap[i].modules[j], info.lpBaseOfDll); ok(info.SizeOfImage, "image size was 0\n"); /* info.EntryPoint to be checked */ + } } winetest_pop_context(); } @@ -356,7 +373,6 @@ static void test_EnumProcessModulesEx(void) if (sizeof(void *) == 8) { test_EnumProcessModulesEx_snapshot(hpQV, snap, ARRAY_SIZE(snap)); - todo_wine ok(snapshot_is_empty(&snap[0]), "didn't expect 32bit module\n"); ok(snapshot_is_equal(&snap[1], &snap[2]), "mismatch in modules count\n"); ok(snapshot_is_equal(&snap[2], &snap[3]), "mismatch in modules count\n"); @@ -369,9 +385,7 @@ static void test_EnumProcessModulesEx(void) */ SetLastError(0xdeadbeef); ret = EnumProcessModulesEx(hpQV, &hMod, sizeof(HMODULE), &cbNeeded, 0x400); - todo_wine ok(!ret, "succeeded\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected error=ERROR_INVALID_PARAMETER but got %ld\n", GetLastError()); } else if (wow64) @@ -397,7 +411,6 @@ static void test_EnumProcessModulesEx(void) { winetest_push_context("pcs-6464"); test_EnumProcessModulesEx_snapshot(pi.hProcess, snap, ARRAY_SIZE(snap)); - todo_wine ok(snapshot_is_empty(&snap[0]), "didn't expect 32bit module\n"); ok(snapshot_is_subset(&snap[1], &snap[2]), "64bit and default module lists should match\n"); ok(snapshot_is_subset(&snap[2], &snap[3]), "default and all module lists should match\n"); @@ -411,9 +424,7 @@ static void test_EnumProcessModulesEx(void) */ SetLastError(0xdeadbeef); ret = EnumProcessModulesEx(hpQV, &hMod, sizeof(HMODULE), &cbNeeded, 0x400); - todo_wine ok(!ret, "succeeded\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected error=ERROR_INVALID_PARAMETER but got %ld\n", GetLastError()); } else if (wow64) @@ -446,20 +457,19 @@ static void test_EnumProcessModulesEx(void) winetest_push_context("pcs-6432"); test_EnumProcessModulesEx_snapshot(pi.hProcess, snap, ARRAY_SIZE(snap)); ok(!snapshot_is_empty(&snap[0]), "expecting 32bit modules\n"); + /* FIXME: this tests fails on Wine "old" wow configuration, but succceeds in "multi-arch" and Windows */ + todo_wine_if(snapshot_is_empty(&snap[1])) ok(!snapshot_is_empty(&snap[1]), "expecting 64bit modules\n"); ok(snapshot_is_subset(&snap[1], &snap[2]), "64bit and default module lists should match\n"); - todo_wine ok(snapshot_are_disjoint(&snap[0], &snap[1], 0), "32bit and 64bit list should be disjoint\n"); /* Main module (even 32bit) is present in both 32bit (makes sense) but also default * (even if all the other modules are 64bit) */ - todo_wine ok(snapshot_are_disjoint(&snap[0], &snap[2], 1), "32bit and default list should be disjoint\n"); ok(snapshot_is_subset(&snap[0], &snap[3]), "32bit and all module lists should match\n"); ok(snapshot_is_subset(&snap[1], &snap[3]), "64bit and all module lists should match\n"); ok(snapshot_is_subset(&snap[2], &snap[3]), "default and all module list should match\n"); snapshot_check_first_main_module(&snap[0], pi.hProcess, buffer); - todo_wine ok(!snapshot_contains(&snap[1], snap[0].modules[0]), "main module shouldn't be present in 64bit list\n"); snapshot_check_first_main_module(&snap[2], pi.hProcess, buffer); snapshot_check_first_main_module(&snap[3], pi.hProcess, buffer); @@ -469,9 +479,7 @@ static void test_EnumProcessModulesEx(void) */ SetLastError(0xdeadbeef); ret = EnumProcessModulesEx(hpQV, &hMod, sizeof(HMODULE), &cbNeeded, 0x400); - todo_wine ok(!ret, "succeeded\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected error=ERROR_INVALID_PARAMETER but got %ld\n", GetLastError());
winetest_pop_context(); @@ -503,7 +511,6 @@ static void test_EnumProcessModulesEx(void) SetLastError(0xdeadbeef); ret = EnumProcessModulesEx(pi.hProcess, &hMod, sizeof(HMODULE), &cbNeeded, snap[i].list); ok(!ret, "succeeded\n"); - todo_wine ok(GetLastError() == ERROR_PARTIAL_COPY, "expected error=ERROR_PARTIAL_COPY but got %ld\n", GetLastError()); } winetest_pop_context();
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/kernelbase/debug.c | 23 ++++++++++++++++------- dlls/psapi/tests/psapi_main.c | 1 - 2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c index 6645b82a660..ae287b885ce 100644 --- a/dlls/kernelbase/debug.c +++ b/dlls/kernelbase/debug.c @@ -876,6 +876,13 @@ static BOOL get_ldr_module32( HANDLE process, HMODULE module, LDR_DATA_TABLE_ENT struct module_iterator iter; INT ret;
+#ifdef _WIN64 + if ((ULONG_PTR)module >> 32) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } +#endif if (!init_module_iterator( &iter, process, TRUE )) return FALSE;
while ((ret = module_iterator_next( &iter )) > 0) @@ -1320,7 +1327,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetModuleBaseNameA( HANDLE process, HMODULE modul DWORD WINAPI DECLSPEC_HOTPATCH GetModuleBaseNameW( HANDLE process, HMODULE module, WCHAR *name, DWORD size ) { - BOOL wow64; + BOOL wow64, found = FALSE;
if (!IsWow64Process( process, &wow64 )) return 0;
@@ -1328,13 +1335,15 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetModuleBaseNameW( HANDLE process, HMODULE modul { LDR_DATA_TABLE_ENTRY32 ldr_module32;
- if (!get_ldr_module32(process, module, &ldr_module32)) return 0; - size = min( ldr_module32.BaseDllName.Length / sizeof(WCHAR), size ); - if (!ReadProcessMemory( process, (void *)(DWORD_PTR)ldr_module32.BaseDllName.Buffer, - name, size * sizeof(WCHAR), NULL )) - return 0; + if (get_ldr_module32(process, module, &ldr_module32)) + { + size = min( ldr_module32.BaseDllName.Length / sizeof(WCHAR), size ); + if (ReadProcessMemory( process, (void *)(DWORD_PTR)ldr_module32.BaseDllName.Buffer, + name, size * sizeof(WCHAR), NULL )) + found = TRUE; + } } - else + if (!found) { LDR_DATA_TABLE_ENTRY ldr_module;
diff --git a/dlls/psapi/tests/psapi_main.c b/dlls/psapi/tests/psapi_main.c index 71eadc7114e..15b168cf83f 100644 --- a/dlls/psapi/tests/psapi_main.c +++ b/dlls/psapi/tests/psapi_main.c @@ -241,7 +241,6 @@ static BOOL test_EnumProcessModulesEx_snapshot(HANDLE proc, struct moduleex_snap case LIST_MODULES_64BIT: break; } ret = GetModuleBaseNameA(proc, mxsnap[i].modules[j], buffer, sizeof(buffer)); - todo_wine_if(fail) ok(ret, "GetModuleBaseName failed: %lu (%u/%lu=%p)\n", GetLastError(), j, mxsnap[i].num_modules, mxsnap[i].modules[j]); ret = GetModuleFileNameExA(proc, mxsnap[i].modules[j], buffer, sizeof(buffer)); todo_wine_if(fail)
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/kernelbase/debug.c | 16 +++++++++------- dlls/psapi/tests/psapi_main.c | 1 - 2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c index ae287b885ce..221da47ded5 100644 --- a/dlls/kernelbase/debug.c +++ b/dlls/kernelbase/debug.c @@ -1409,7 +1409,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameExA( HANDLE process, HMODULE mod DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameExW( HANDLE process, HMODULE module, WCHAR *name, DWORD size ) { - BOOL wow64; + BOOL wow64, found = FALSE; DWORD len;
if (!size) return 0; @@ -1420,13 +1420,15 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameExW( HANDLE process, HMODULE mod { LDR_DATA_TABLE_ENTRY32 ldr_module32;
- if (!get_ldr_module32( process, module, &ldr_module32 )) return 0; - len = ldr_module32.FullDllName.Length / sizeof(WCHAR); - if (!ReadProcessMemory( process, (void *)(DWORD_PTR)ldr_module32.FullDllName.Buffer, - name, min( len, size ) * sizeof(WCHAR), NULL )) - return 0; + if (get_ldr_module32( process, module, &ldr_module32 )) + { + len = ldr_module32.FullDllName.Length / sizeof(WCHAR); + if (ReadProcessMemory( process, (void *)(DWORD_PTR)ldr_module32.FullDllName.Buffer, + name, min( len, size ) * sizeof(WCHAR), NULL )) + found = TRUE; + } } - else + if (!found) { LDR_DATA_TABLE_ENTRY ldr_module;
diff --git a/dlls/psapi/tests/psapi_main.c b/dlls/psapi/tests/psapi_main.c index 15b168cf83f..2237bad3665 100644 --- a/dlls/psapi/tests/psapi_main.c +++ b/dlls/psapi/tests/psapi_main.c @@ -243,7 +243,6 @@ static BOOL test_EnumProcessModulesEx_snapshot(HANDLE proc, struct moduleex_snap ret = GetModuleBaseNameA(proc, mxsnap[i].modules[j], buffer, sizeof(buffer)); ok(ret, "GetModuleBaseName failed: %lu (%u/%lu=%p)\n", GetLastError(), j, mxsnap[i].num_modules, mxsnap[i].modules[j]); ret = GetModuleFileNameExA(proc, mxsnap[i].modules[j], buffer, sizeof(buffer)); - todo_wine_if(fail) ok(ret, "GetModuleFileNameEx failed: %lu (%u/%lu=%p)\n", GetLastError(), j, mxsnap[i].num_modules, mxsnap[i].modules[j]); memset(&info, 0, sizeof(info)); ret = GetModuleInformation(proc, mxsnap[i].modules[j], &info, sizeof(info));
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/kernelbase/debug.c | 15 +++++++++------ dlls/psapi/tests/psapi_main.c | 15 --------------- 2 files changed, 9 insertions(+), 21 deletions(-)
diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c index 221da47ded5..8a76a45a061 100644 --- a/dlls/kernelbase/debug.c +++ b/dlls/kernelbase/debug.c @@ -1458,7 +1458,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameExW( HANDLE process, HMODULE mod */ BOOL WINAPI GetModuleInformation( HANDLE process, HMODULE module, MODULEINFO *modinfo, DWORD count ) { - BOOL wow64; + BOOL wow64, found = FALSE;
if (count < sizeof(MODULEINFO)) { @@ -1472,12 +1472,15 @@ BOOL WINAPI GetModuleInformation( HANDLE process, HMODULE module, MODULEINFO *mo { LDR_DATA_TABLE_ENTRY32 ldr_module32;
- if (!get_ldr_module32( process, module, &ldr_module32 )) return FALSE; - modinfo->lpBaseOfDll = (void *)(DWORD_PTR)ldr_module32.BaseAddress; - modinfo->SizeOfImage = ldr_module32.SizeOfImage; - modinfo->EntryPoint = (void *)(DWORD_PTR)ldr_module32.EntryPoint; + if (get_ldr_module32( process, module, &ldr_module32 )) + { + modinfo->lpBaseOfDll = (void *)(DWORD_PTR)ldr_module32.BaseAddress; + modinfo->SizeOfImage = ldr_module32.SizeOfImage; + modinfo->EntryPoint = (void *)(DWORD_PTR)ldr_module32.EntryPoint; + found = TRUE; + } } - else + if (!found) { LDR_DATA_TABLE_ENTRY ldr_module;
diff --git a/dlls/psapi/tests/psapi_main.c b/dlls/psapi/tests/psapi_main.c index 2237bad3665..a106628a9ed 100644 --- a/dlls/psapi/tests/psapi_main.c +++ b/dlls/psapi/tests/psapi_main.c @@ -215,10 +215,6 @@ static BOOL test_EnumProcessModulesEx_snapshot(HANDLE proc, struct moduleex_snap MODULEINFO info; int i, j; BOOL ret; - BOOL fail, wow64; - - ret = IsWow64Process(proc, &wow64); - ok(ret, "IsWow64Process failed: %lu\n", GetLastError());
for (i = 0; i < numsnap; i++) { @@ -231,27 +227,16 @@ static BOOL test_EnumProcessModulesEx_snapshot(HANDLE proc, struct moduleex_snap mxsnap[i].num_modules = min(needed, sizeof(mxsnap[i].modules)) / sizeof(HMODULE); for (j = 0; j < mxsnap[i].num_modules; j++) { - /* temporary todo until GetModuleBaseName and friends are fixed */ - if ((fail = sizeof(void*) == 8 && wow64)) - switch (mxsnap[i].list) - { - case LIST_MODULES_32BIT: fail = FALSE; break; - case LIST_MODULES_DEFAULT: fail = j >= 1; break; - case LIST_MODULES_ALL: fail = j >= mxsnap[0].num_modules; break; - case LIST_MODULES_64BIT: break; - } ret = GetModuleBaseNameA(proc, mxsnap[i].modules[j], buffer, sizeof(buffer)); ok(ret, "GetModuleBaseName failed: %lu (%u/%lu=%p)\n", GetLastError(), j, mxsnap[i].num_modules, mxsnap[i].modules[j]); ret = GetModuleFileNameExA(proc, mxsnap[i].modules[j], buffer, sizeof(buffer)); ok(ret, "GetModuleFileNameEx failed: %lu (%u/%lu=%p)\n", GetLastError(), j, mxsnap[i].num_modules, mxsnap[i].modules[j]); memset(&info, 0, sizeof(info)); ret = GetModuleInformation(proc, mxsnap[i].modules[j], &info, sizeof(info)); - todo_wine_if(fail) { ok(ret, "GetModuleInformation failed: %lu\n", GetLastError()); ok(info.lpBaseOfDll == mxsnap[i].modules[j], "expected %p, got %p\n", mxsnap[i].modules[j], info.lpBaseOfDll); ok(info.SizeOfImage, "image size was 0\n"); /* info.EntryPoint to be checked */ - } } winetest_pop_context(); }