I was going to reimplement path resolution in ShellExecute, but half way through I realized that's very unnecessary.
Since what I wanted is a version of `PathResolve` that behaves differently _only_ for filespec paths, I ended up duplicating a lot of code, then I realized I can still pass filespec paths onto `PathResolve` and only deal with non-filespec paths.
-- v2: shell32: Fix ShellExecute for non-filespec paths.
From: Yuxuan Shui yshui@codeweavers.com
--- dlls/shell32/shlexec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/shell32/shlexec.c b/dlls/shell32/shlexec.c index d2fe3caed88..a14029f8b08 100644 --- a/dlls/shell32/shlexec.c +++ b/dlls/shell32/shlexec.c @@ -599,7 +599,7 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpVerb, WCHAR wBuffer[256]; /* Used to GetProfileString */ UINT retval = SE_ERR_NOASSOC; WCHAR *tok; /* token pointer */ - WCHAR xlpFile[256]; /* result of SearchPath */ + WCHAR xlpFile[MAX_PATH]; /* result of SearchPath */ DWORD attribs; /* file attributes */ WCHAR curdir[MAX_PATH]; const WCHAR *search_paths[3] = {0};
From: Yuxuan Shui yshui@codeweavers.com
--- dlls/shell32/shlexec.c | 61 +++++++++++++++++++++++++++--------- dlls/shell32/tests/shlexec.c | 13 +++++++- 2 files changed, 59 insertions(+), 15 deletions(-)
diff --git a/dlls/shell32/shlexec.c b/dlls/shell32/shlexec.c index a14029f8b08..41b73d6ff08 100644 --- a/dlls/shell32/shlexec.c +++ b/dlls/shell32/shlexec.c @@ -49,6 +49,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(exec); typedef UINT_PTR (*SHELL_ExecuteW32)(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, const SHELLEXECUTEINFOW *sei, LPSHELLEXECUTEINFOW sei_out); extern BOOL WINAPI PathResolveAW(void *path, const void **paths, DWORD flags); +extern BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich);
static inline BOOL isSpace(WCHAR c) { @@ -627,24 +628,56 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpVerb, return 33; }
- if (lpPath && *lpPath) + GetCurrentDirectoryW(MAX_PATH, curdir); + if (!PathIsFileSpecW(lpFile)) { - search_paths[0] = lpPath; - search_paths[1] = curdir; + BOOL found = FALSE; + if (lpPath && *lpPath) + { + PathCombineW(xlpFile, lpPath, lpFile); + if (PathFileExistsDefExtW(xlpFile, 0xbf)) + { + GetFullPathNameW(xlpFile, MAX_PATH, xlpFile, NULL); + found = TRUE; + } + } + if (!found) + { + lstrcpyW(xlpFile, lpFile); + if (PathFileExistsDefExtW(xlpFile, 0xbf)) + { + GetFullPathNameW(xlpFile, MAX_PATH, xlpFile, NULL); + found = TRUE; + } + } + if (found) + { + lpFile = xlpFile; + lstrcpyW(lpResult, xlpFile); + } + else + xlpFile[0] = '\0'; } - else - search_paths[0] = curdir; - GetCurrentDirectoryW(MAX_PATH, curdir); - lstrcpyW(xlpFile, lpFile); - if (PathResolveAW(xlpFile, (const void **)search_paths, PRF_TRYPROGRAMEXTENSIONS | PRF_VERIFYEXISTS)) + else { - TRACE("PathResolveAW returned non-zero\n"); - lpFile = xlpFile; - lstrcpyW(lpResult, xlpFile); - /* The file was found in lpPath or one of the directories in the system-wide search path */ + if (lpPath && *lpPath) + { + search_paths[0] = lpPath; + search_paths[1] = curdir; + } + else + search_paths[0] = curdir; + lstrcpyW(xlpFile, lpFile); + if (PathResolveAW(xlpFile, (const void **)search_paths, PRF_TRYPROGRAMEXTENSIONS | PRF_VERIFYEXISTS)) + { + TRACE("PathResolveAW returned non-zero\n"); + lpFile = xlpFile; + lstrcpyW(lpResult, xlpFile); + /* The file was found in lpPath or one of the directories in the system-wide search path */ + } + else + xlpFile[0] = '\0'; } - else - xlpFile[0] = '\0';
attribs = GetFileAttributesW(lpFile); if (attribs!=INVALID_FILE_ATTRIBUTES && (attribs&FILE_ATTRIBUTE_DIRECTORY)) diff --git a/dlls/shell32/tests/shlexec.c b/dlls/shell32/tests/shlexec.c index 0f537d829f4..1f979722e99 100644 --- a/dlls/shell32/tests/shlexec.c +++ b/dlls/shell32/tests/shlexec.c @@ -2267,9 +2267,11 @@ static void test_exes(void) char filename[2 * MAX_PATH + 17]; char params[1024]; char curdir[MAX_PATH]; - char *basename = strrchr(argv0, '\') + 1; + char relative_basename[MAX_PATH]; + char *basename = strrchr(argv0, '\') + 1, *dirname = strdup(argv0); INT_PTR rc;
+ *strrchr(dirname, '\') = '\0'; sprintf(params, "shlexec "%s" Exec", child_file);
/* We need NOZONECHECKS on Win2003 to block a dialog */ @@ -2279,6 +2281,15 @@ static void test_exes(void) okChildInt("argcA", 4); okChildString("argvA3", "Exec");
+ /* Check non-filespec paths */ + snprintf(relative_basename, MAX_PATH, ".\\%s", basename); + rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, relative_basename, params, + dirname, NULL); + okShell(rc > 32, "returned %Iu\n", rc); + okChildInt("argcA", 4); + okChildString("argvA3", "Exec"); + free(dirname); + rc=shell_execute_ex(SEE_MASK_NOZONECHECKS | SEE_MASK_CLASSNAME | SEE_MASK_FLAG_NO_UI, NULL, argv0, params, NULL, ".exe"); okShell(rc > 32, "returned %Iu\n", rc);
Huw Davies (@huw) commented about dlls/shell32/shlexec.c:
WCHAR wBuffer[256]; /* Used to GetProfileString */ UINT retval = SE_ERR_NOASSOC; WCHAR *tok; /* token pointer */
- WCHAR xlpFile[256]; /* result of SearchPath */
- WCHAR xlpFile[MAX_PATH]; /* result of SearchPath */
It would be nice (if we are using fixed sized buffers here) that the calls to `GetFullPathNameW()` (and `GetCurrentDirectoryW()`) below used `ARRAY_SIZE()` instead of hard-coding the size.
Huw Davies (@huw) commented about dlls/shell32/shlexec.c:
}
}
if (found)
{
lpFile = xlpFile;
lstrcpyW(lpResult, xlpFile);
}
else
}xlpFile[0] = '\0';
- else
search_paths[0] = curdir;
- GetCurrentDirectoryW(MAX_PATH, curdir);
- lstrcpyW(xlpFile, lpFile);
- if (PathResolveAW(xlpFile, (const void **)search_paths, PRF_TRYPROGRAMEXTENSIONS | PRF_VERIFYEXISTS))
- else
There's a trailing white-space here.