diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index 18b97374a5d..a73ea1b1b4e 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -865,6 +865,8 @@ @ stdcall QueryDepthSList(ptr) ntdll.RtlQueryDepthSList @ stdcall QueryDosDeviceA(str ptr long) @ stdcall QueryDosDeviceW(wstr ptr long) +@ stub QueryFullProcessImageNameA +@ stdcall QueryFullProcessImageNameW(ptr long wstr ptr) @ stdcall QueryInformationJobObject(long long ptr long ptr) # @ stub QueryMemoryResourceNotification @ stub QueryNumberOfEventLogRecords diff --git a/dlls/kernel32/process.c b/dlls/kernel32/process.c index fea63a87986..f2d856761bb 100644 --- a/dlls/kernel32/process.c +++ b/dlls/kernel32/process.c @@ -3110,6 +3110,59 @@ BOOL WINAPI GetProcessHandleCount(HANDLE hProcess, DWORD *cnt) return !status; } +/****************************************************************** + * QueryFullProcessImageNameW (KERNEL32.@) + */ +BOOL WINAPI QueryFullProcessImageNameW(HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD pdwSize) +{ + BYTE buffer[sizeof(UNICODE_STRING) + MAX_PATH*sizeof(WCHAR)]; /* this buffer should be enough */ + UNICODE_STRING *dynamic_buffer = NULL; + UNICODE_STRING nt_path; + UNICODE_STRING *result = NULL; + NTSTATUS status; + DWORD needed; + + RtlInitUnicodeStringEx(&nt_path, NULL); + /* FIXME: On Windows, ProcessImageFileName return an NT path. We rely that it being a DOS path, + * as this is on Wine. */ + status = NtQueryInformationProcess(hProcess, ProcessImageFileName, buffer, sizeof(buffer), &needed); + if (status == STATUS_INFO_LENGTH_MISMATCH) + { + dynamic_buffer = HeapAlloc(GetProcessHeap(), 0, needed); + status = NtQueryInformationProcess(hProcess, ProcessImageFileName, (LPBYTE)dynamic_buffer, needed, &needed); + result = dynamic_buffer; + } + else + result = (PUNICODE_STRING)buffer; + + if (status) goto cleanup; + + if (dwFlags & PROCESS_NAME_NATIVE) + { + if (!RtlDosPathNameToNtPathName_U(result->Buffer, &nt_path, NULL, NULL)) + { + status = STATUS_OBJECT_PATH_NOT_FOUND; + goto cleanup; + } + result = &nt_path; + } + + if (result->Length/sizeof(WCHAR) + 1 > *pdwSize) + { + status = STATUS_BUFFER_TOO_SMALL; + goto cleanup; + } + + lstrcpynW(lpExeName, result->Buffer, result->Length/sizeof(WCHAR) + 1); + *pdwSize = result->Length/sizeof(WCHAR); + +cleanup: + HeapFree(GetProcessHeap(), 0, dynamic_buffer); + RtlFreeUnicodeString(&nt_path); + if (status) SetLastError( RtlNtStatusToDosError(status) ); + return !status; +} + /*********************************************************************** * ProcessIdToSessionId (KERNEL32.@) * This function is available on Terminal Server 4SP4 and Windows 2000 diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index 21dff3cc709..7cc58416b26 100644 --- a/dlls/kernel32/tests/process.c +++ b/dlls/kernel32/tests/process.c @@ -35,9 +35,40 @@ #include "wine/test.h" +#define expect_eq_d(expected, actual) \ + do { \ + int value = (actual); \ + ok((expected) == value, "Expected " #actual " to be %d (" #expected ") is %d\n", \ + (expected), value); \ + } while (0) +#define expect_eq_ws_i(expected, actual) \ + do { \ + LPCWSTR value = (actual); \ + ok(lstrcmpiW((expected), value) == 0, "Expected " #actual " to be L\"%s\" (" #expected ") is L\"%s\"\n", \ + wine_dbgstr_w(expected), wine_dbgstr_w(value)); \ + } while (0) + +/* A simpler version of wine_dbgstr_w. Note that the returned buffer will be + * invalid after 16 calls to this funciton. */ +static const char *wine_dbgstr_w(LPCWSTR wstr) +{ + static char *buffers[16]; + static int curr_buffer = 0; + + int size; + + curr_buffer = (curr_buffer + 1) % 16; + HeapFree(GetProcessHeap(), 0, buffers[curr_buffer]); + size = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL); + buffers[curr_buffer] = HeapAlloc(GetProcessHeap(), 0, size); + size = WideCharToMultiByte(CP_ACP, 0, wstr, -1, buffers[curr_buffer], size, NULL, NULL); + return buffers[curr_buffer]; +} + static HINSTANCE hkernel32; static LPVOID (WINAPI *pVirtualAllocEx)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD); static BOOL (WINAPI *pVirtualFreeEx)(HANDLE, LPVOID, SIZE_T, DWORD); +static BOOL (WINAPI *pQueryFullProcessImageNameW)(HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD lpdwSize); /* ############################### */ static char base[MAX_PATH]; @@ -175,6 +206,7 @@ static int init(void) hkernel32 = GetModuleHandleA("kernel32"); pVirtualAllocEx = (void *) GetProcAddress(hkernel32, "VirtualAllocEx"); pVirtualFreeEx = (void *) GetProcAddress(hkernel32, "VirtualFreeEx"); + pQueryFullProcessImageNameW = (void *) GetProcAddress(hkernel32, "QueryFullProcessImageNameW"); return 1; } @@ -1520,6 +1552,76 @@ static void test_GetProcessVersion(void) CloseHandle(pi.hThread); } +static void test_ProcessName(void) +{ + HANDLE hSelf; + WCHAR module_name[1024]; + WCHAR deviceW[] = {'\\','D', 'e','v','i','c','e',0}; + WCHAR buf[1024]; + DWORD size; + + if (!pQueryFullProcessImageNameW) + { + win_skip("QueryFullProcessImageNameW unavailable (added in Windows Vista)\n"); + return; + } + + ok(GetModuleFileNameW(NULL, module_name, 1024), "GetModuleFileNameW(NULL, ...) failed\n"); + + /* GetCurrentProcess pseudo-handle */ + size = sizeof(buf) / sizeof(buf[0]); + expect_eq_d(TRUE, pQueryFullProcessImageNameW(GetCurrentProcess(), 0, buf, &size)); + expect_eq_d(lstrlenW(buf), size); + expect_eq_ws_i(buf, module_name); + + hSelf = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); + /* Real handle */ + size = sizeof(buf) / sizeof(buf[0]); + expect_eq_d(TRUE, pQueryFullProcessImageNameW(hSelf, 0, buf, &size)); + expect_eq_d(lstrlenW(buf), size); + expect_eq_ws_i(buf, module_name); + + /* Buffer too small */ + size = lstrlenW(module_name)/2; + lstrcpyW(buf, deviceW); + SetLastError(0xdeadbeef); + expect_eq_d(FALSE, pQueryFullProcessImageNameW(hSelf, 0, buf, &size)); + expect_eq_d(lstrlenW(module_name)/2, size); /* size not changed(!) */ + expect_eq_d(ERROR_INSUFFICIENT_BUFFER, GetLastError()); + expect_eq_ws_i(deviceW, buf); /* buffer not changed */ + + /* Too small - not space for NUL terminator */ + size = lstrlenW(module_name); + SetLastError(0xdeadbeef); + expect_eq_d(FALSE, pQueryFullProcessImageNameW(hSelf, 0, buf, &size)); + expect_eq_d(lstrlenW(module_name), size); /* size not changed(!) */ + expect_eq_d(ERROR_INSUFFICIENT_BUFFER, GetLastError()); + + /* NULL buffer */ + size = 0; + expect_eq_d(FALSE, pQueryFullProcessImageNameW(hSelf, 0, NULL, &size)); + expect_eq_d(0, size); + expect_eq_d(ERROR_INSUFFICIENT_BUFFER, GetLastError()); + + /* native path */ + size = sizeof(buf) / sizeof(buf[0]); + expect_eq_d(TRUE, pQueryFullProcessImageNameW(hSelf, PROCESS_NAME_NATIVE, buf, &size)); + expect_eq_d(lstrlenW(buf), size); + ok(buf[0] == '\\', "NT path should begin with '\\'\n"); + todo_wine ok(memcmp(buf, deviceW, sizeof(WCHAR)*lstrlenW(deviceW)) == 0, "NT path should begin with \\Device\n"); + + /* Buffer too small */ + size = lstrlenW(module_name)/2; + SetLastError(0xdeadbeef); + lstrcpyW(buf, module_name); + expect_eq_d(FALSE, pQueryFullProcessImageNameW(hSelf, 0, buf, &size)); + expect_eq_d(lstrlenW(module_name)/2, size); /* size not changed(!) */ + expect_eq_d(ERROR_INSUFFICIENT_BUFFER, GetLastError()); + expect_eq_ws_i(module_name, buf); /* buffer not changed */ + + CloseHandle(hSelf); +} + static void test_Handles(void) { HANDLE handle = GetCurrentProcess(); @@ -1573,6 +1675,7 @@ START_TEST(process) test_ExitCode(); test_OpenProcess(); test_GetProcessVersion(); + test_ProcessName(); test_Handles(); /* things that can be tested: * lookup: check the way program to be executed is searched diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c index 82bc6a744b6..76c841088a1 100644 --- a/dlls/ntdll/process.c +++ b/dlls/ntdll/process.c @@ -313,6 +313,8 @@ NTSTATUS WINAPI NtQueryInformationProcess( else ret = STATUS_INFO_LENGTH_MISMATCH; break; case ProcessImageFileName: + /* FIXME: this will return a DOS path. Windows returns an NT path. Changing this would require also changing kernel32.QueryFullProcessImageName. + * The latter may be harder because of the lack of RtlNtPathNameToDosPathName. */ SERVER_START_REQ(get_dll_info) { UNICODE_STRING *image_file_name_str = ProcessInformation; diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index b118bd3c088..22284a4ea71 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -818,6 +818,7 @@ static void test_query_process_image_file_name(void) file_nameA[len] = '\0'; HeapFree(GetProcessHeap(), 0, buffer); trace("process image file name: %s\n", file_nameA); + todo_wine ok(strncmp(file_nameA, "\\Device\\", 8) == 0, "Process image name should be an NT path beginning with \\Device\\ (is %s)", file_nameA); HeapFree(GetProcessHeap(), 0, file_nameA); } diff --git a/include/winbase.h b/include/winbase.h index 650344f10ca..5a95793bb2a 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -1181,6 +1181,8 @@ typedef struct tagCOMMTIMEOUTS { #define SET_TAPE_MEDIA_INFORMATION 0 #define SET_TAPE_DRIVE_INFORMATION 1 +#define PROCESS_NAME_NATIVE 1 + typedef void (CALLBACK *PAPCFUNC)(ULONG_PTR); typedef void (CALLBACK *PTIMERAPCROUTINE)(LPVOID,DWORD,DWORD); @@ -1962,6 +1964,9 @@ WINBASEAPI USHORT WINAPI QueryDepthSList(PSLIST_HEADER); WINBASEAPI DWORD WINAPI QueryDosDeviceA(LPCSTR,LPSTR,DWORD); WINBASEAPI DWORD WINAPI QueryDosDeviceW(LPCWSTR,LPWSTR,DWORD); #define QueryDosDevice WINELIB_NAME_AW(QueryDosDevice) +WINBASEAPI BOOL WINAPI QueryFullProcessImageNameA(HANDLE,DWORD,LPSTR,PDWORD); +WINBASEAPI BOOL WINAPI QueryFullProcessImageNameW(HANDLE,DWORD,LPWSTR,PDWORD); +#define QueryFullProcessImageName WINELIB_NAME_AW(QueryFullProcessImageName) WINBASEAPI BOOL WINAPI QueryInformationJobObject(HANDLE,JOBOBJECTINFOCLASS,LPVOID,DWORD,DWORD*); WINBASEAPI BOOL WINAPI QueryPerformanceCounter(LARGE_INTEGER*); WINBASEAPI BOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER*);