diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c index 3ee0dd69c80..1728bfac40e 100644 --- a/dlls/kernel32/path.c +++ b/dlls/kernel32/path.c @@ -724,6 +724,97 @@ static inline BOOL contains_pathW (LPCWSTR name) return (name[1] == '.' && (name[2] == '/' || name[2] == '\\')); } +/*********************************************************************** + * find_actctx_dllpath + * + * Find the path (if any) of the dll from the activation context. + * Returned path doesn't include a name. + */ +static NTSTATUS find_actctx_dllpath(const WCHAR *libname, WCHAR **path) +{ + static const WCHAR winsxsW[] = {'\\','w','i','n','s','x','s','\\'}; + static const WCHAR dotManifestW[] = {'.','m','a','n','i','f','e','s','t',0}; + + ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION *info; + ACTCTX_SECTION_KEYED_DATA data; + UNICODE_STRING nameW; + NTSTATUS status; + SIZE_T needed, size = 1024; + WCHAR *p; + + RtlInitUnicodeString( &nameW, libname ); + data.cbSize = sizeof(data); + status = RtlFindActivationContextSectionString( FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL, + ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, + &nameW, &data ); + if (status != STATUS_SUCCESS) return status; + + for (;;) + { + if (!(info = HeapAlloc( GetProcessHeap(), 0, size ))) + { + status = STATUS_NO_MEMORY; + goto done; + } + status = RtlQueryInformationActivationContext( 0, data.hActCtx, &data.ulAssemblyRosterIndex, + AssemblyDetailedInformationInActivationContext, + info, size, &needed ); + if (status == STATUS_SUCCESS) break; + if (status != STATUS_BUFFER_TOO_SMALL) goto done; + HeapFree( GetProcessHeap(), 0, info ); + size = needed; + /* restart with larger buffer */ + } + + if (!info->lpAssemblyManifestPath || !info->lpAssemblyDirectoryName) + { + status = STATUS_SXS_KEY_NOT_FOUND; + goto done; + } + + if ((p = strrchrW( info->lpAssemblyManifestPath, '\\' ))) + { + DWORD dirlen = info->ulAssemblyDirectoryNameLength / sizeof(WCHAR); + + p++; + if (strncmpiW( p, info->lpAssemblyDirectoryName, dirlen ) || strcmpiW( p + dirlen, dotManifestW )) + { + /* manifest name does not match directory name, so it's not a global + * windows/winsxs manifest; use the manifest directory name instead */ + dirlen = p - info->lpAssemblyManifestPath; + needed = (dirlen + 1) * sizeof(WCHAR); + if (!(*path = p = HeapAlloc( GetProcessHeap(), 0, needed ))) + { + status = STATUS_NO_MEMORY; + goto done; + } + memcpy( p, info->lpAssemblyManifestPath, dirlen * sizeof(WCHAR) ); + *(p + dirlen) = 0; + goto done; + } + } + + needed = (strlenW( DIR_Windows ) * sizeof(WCHAR) + + sizeof(winsxsW) + info->ulAssemblyDirectoryNameLength + 2*sizeof(WCHAR)); + + if (!(*path = p = HeapAlloc( GetProcessHeap(), 0, needed ))) + { + status = STATUS_NO_MEMORY; + goto done; + } + strcpyW( p, DIR_Windows ); + p += strlenW(p); + memcpy( p, winsxsW, sizeof(winsxsW) ); + p += sizeof(winsxsW) / sizeof(WCHAR); + memcpy( p, info->lpAssemblyDirectoryName, info->ulAssemblyDirectoryNameLength ); + p += info->ulAssemblyDirectoryNameLength / sizeof(WCHAR); + *p++ = '\\'; + *p = 0; +done: + HeapFree( GetProcessHeap(), 0, info ); + RtlReleaseActivationContext( data.hActCtx ); + return status; +} /*********************************************************************** * SearchPathW [KERNEL32.@] @@ -802,20 +893,75 @@ DWORD WINAPI SearchPathW( LPCWSTR path, LPCWSTR name, LPCWSTR ext, DWORD buflen, ret = RtlDosSearchPath_U( path, name, ext, buflen * sizeof(WCHAR), buffer, lastpart ) / sizeof(WCHAR); } - else /* search in the default path */ + else /* search in active context and default path */ { - WCHAR *dll_path = MODULE_get_dll_load_path( NULL ); + WCHAR *dll_path = NULL, *search = NULL; + DWORD req_len, name_len; - if (dll_path) + req_len = name_len = strlenW(name); + + if (strchrW( name, '.' )) ext = NULL; + if (ext) { - ret = RtlDosSearchPath_U( dll_path, name, ext, buflen * sizeof(WCHAR), - buffer, lastpart ) / sizeof(WCHAR); + DWORD ext_len = strlenW(ext); + + req_len += ext_len; + name_len += ext_len; + + search = HeapAlloc( GetProcessHeap(), 0, (name_len + ext_len + 1) * sizeof(WCHAR) ); + if (!search) + { + SetLastError( ERROR_OUTOFMEMORY ); + HeapFree( GetProcessHeap(), 0, dll_path ); + return 0; + } + strcpyW( search, name ); + strcatW( search, ext ); + name = search; + + /* now that we have combined name we don't need extension any more */ + } + + /* When file is found with activation context no attempt is made + to check if it's really exist, path is returned only basing on context info. */ + if (find_actctx_dllpath( name, &dll_path ) == STATUS_SUCCESS) + { + DWORD path_len; + + path_len = strlenW(dll_path); + req_len += path_len; + + if (lastpart) *lastpart = NULL; + + /* count null termination char too */ + if (req_len + 1 <= buflen) + { + memcpy( buffer, dll_path, path_len * sizeof(WCHAR) ); + memcpy( &buffer[path_len], name, name_len * sizeof(WCHAR) ); + buffer[req_len] = 0; + if (lastpart) *lastpart = buffer + path_len; + ret = req_len; + } + else + ret = req_len + 1; + HeapFree( GetProcessHeap(), 0, dll_path ); + HeapFree( GetProcessHeap(), 0, search ); } else { - SetLastError( ERROR_OUTOFMEMORY ); - return 0; + if ((dll_path = MODULE_get_dll_load_path( NULL ))) + { + ret = RtlDosSearchPath_U( dll_path, name, NULL, buflen * sizeof(WCHAR), + buffer, lastpart ) / sizeof(WCHAR); + HeapFree( GetProcessHeap(), 0, dll_path ); + HeapFree( GetProcessHeap(), 0, search ); + } + else + { + SetLastError( ERROR_OUTOFMEMORY ); + return 0; + } } } diff --git a/dlls/kernel32/tests/path.c b/dlls/kernel32/tests/path.c index 601c2e56c88..e83e0a99561 100644 --- a/dlls/kernel32/tests/path.c +++ b/dlls/kernel32/tests/path.c @@ -1639,6 +1639,7 @@ static HANDLE test_create(const char *file) static void test_SearchPathA(void) { static const CHAR testdepA[] = "testdep.dll"; + static const CHAR testdeprelA[] = "./testdep.dll"; static const CHAR kernel32A[] = "kernel32.dll"; static const CHAR fileA[] = ""; CHAR pathA[MAX_PATH], buffA[MAX_PATH], path2A[MAX_PATH]; @@ -1691,13 +1692,28 @@ static void test_SearchPathA(void) /* works when activated */ ret = pSearchPathA(NULL, testdepA, NULL, sizeof(buffA)/sizeof(CHAR), buffA, NULL); -todo_wine ok(ret && ret == strlen(buffA), "got %d\n", ret); - /* path is redirect for wellknown names too */ + ret = pSearchPathA(NULL, "testdep.dll", ".ext", sizeof(buffA)/sizeof(CHAR), buffA, NULL); + ok(ret && ret == strlen(buffA), "got %d\n", ret); + + ret = pSearchPathA(NULL, "testdep", ".dll", sizeof(buffA)/sizeof(CHAR), buffA, NULL); + ok(ret && ret == strlen(buffA), "got %d\n", ret); + + ret = pSearchPathA(NULL, "testdep", ".ext", sizeof(buffA)/sizeof(CHAR), buffA, NULL); + ok(!ret, "got %d\n", ret); + + /* name contains path */ + ret = pSearchPathA(NULL, testdeprelA, NULL, sizeof(buffA)/sizeof(CHAR), buffA, NULL); + ok(!ret, "got %d\n", ret); + + /* fails with specified path that doesn't contain this file */ + ret = pSearchPathA(pathA, testdepA, NULL, sizeof(buffA)/sizeof(CHAR), buffA, NULL); + ok(!ret, "got %d\n", ret); + + /* path is redirected for wellknown names too */ ret = pSearchPathA(NULL, kernel32A, NULL, sizeof(buffA)/sizeof(CHAR), buffA, NULL); ok(ret && ret == strlen(buffA), "got %d\n", ret); -todo_wine ok(strcmp(buffA, path2A), "got wrong path %s, %s\n", buffA, path2A); ret = pDeactivateActCtx(0, cookie); @@ -1707,8 +1723,12 @@ todo_wine static void test_SearchPathW(void) { + static const WCHAR testdeprelW[] = {'.','/','t','e','s','t','d','e','p','.','d','l','l',0}; static const WCHAR testdepW[] = {'t','e','s','t','d','e','p','.','d','l','l',0}; + static const WCHAR testdep1W[] = {'t','e','s','t','d','e','p',0}; static const WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2','.','d','l','l',0}; + static const WCHAR extW[] = {'.','e','x','t',0}; + static const WCHAR dllW[] = {'.','d','l','l',0}; static const WCHAR fileW[] = { 0 }; WCHAR pathW[MAX_PATH], buffW[MAX_PATH], path2W[MAX_PATH]; WCHAR *ptrW = NULL; @@ -1728,6 +1748,8 @@ if (0) pSearchPathW(pathW, NULL, NULL, sizeof(buffW)/sizeof(WCHAR), buffW, &ptrW); } + GetWindowsDirectoryW(pathW, sizeof(pathW)/sizeof(WCHAR)); + /* empty filename */ SetLastError(0xdeadbeef); ret = pSearchPathW(pathW, fileW, NULL, sizeof(buffW)/sizeof(WCHAR), buffW, &ptrW); @@ -1757,13 +1779,28 @@ if (0) /* works when activated */ ret = pSearchPathW(NULL, testdepW, NULL, sizeof(buffW)/sizeof(WCHAR), buffW, NULL); -todo_wine ok(ret && ret == lstrlenW(buffW), "got %d\n", ret); + ret = pSearchPathW(NULL, testdepW, extW, sizeof(buffW)/sizeof(WCHAR), buffW, NULL); + ok(ret && ret == lstrlenW(buffW), "got %d\n", ret); + + ret = pSearchPathW(NULL, testdep1W, dllW, sizeof(buffW)/sizeof(WCHAR), buffW, NULL); + ok(ret && ret == lstrlenW(buffW), "got %d\n", ret); + + ret = pSearchPathW(NULL, testdep1W, extW, sizeof(buffW)/sizeof(WCHAR), buffW, NULL); + ok(!ret, "got %d\n", ret); + + /* name contains path */ + ret = pSearchPathW(NULL, testdeprelW, NULL, sizeof(buffW)/sizeof(WCHAR), buffW, NULL); + ok(!ret, "got %d\n", ret); + + /* fails with specified path that doesn't contain this file */ + ret = pSearchPathW(pathW, testdepW, NULL, sizeof(buffW)/sizeof(WCHAR), buffW, NULL); + ok(!ret, "got %d\n", ret); + /* path is redirect for wellknown names too */ ret = pSearchPathW(NULL, kernel32W, NULL, sizeof(buffW)/sizeof(WCHAR), buffW, NULL); ok(ret && ret == lstrlenW(buffW), "got %d\n", ret); -todo_wine ok(lstrcmpW(buffW, path2W), "got wrong path %s, %s\n", wine_dbgstr_w(buffW), wine_dbgstr_w(path2W)); ret = pDeactivateActCtx(0, cookie);