[PATCH v7 0/3] MR5400: shell32: Tying up some loose ends
Follow up to !5342. This adds the last missing piece for fixing how `ShellExecute` finds files, and restore the ability of running native unix programs with `ShellExecute` -- v7: shell32: Restore the ability of running native unix programs with ShellExecute https://gitlab.winehq.org/wine/wine/-/merge_requests/5400
From: Yuxuan Shui <yshui(a)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 f09b6d4daf3..804aa38a904 100644 --- a/dlls/shell32/shlexec.c +++ b/dlls/shell32/shlexec.c @@ -637,7 +637,7 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpVerb, lstrcpyW(xlpFile, lpFile); if (PathResolveAW(xlpFile, (const void **)search_paths, PRF_TRYPROGRAMEXTENSIONS | PRF_VERIFYEXISTS)) { - TRACE("SearchPathW returned non-zero\n"); + TRACE("PathResolveAW returned non-zero\n"); lpFile = xlpFile; /* The file was found in lpPath or one of the directories in the system-wide search path */ } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/5400
From: Yuxuan Shui <yshui(a)codeweavers.com> So that the path returned by SHELL_FindExecutable would be fully qualified, otherwise CreateProcess will do its own path resolution which is not what we want. --- dlls/shell32/shlexec.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/dlls/shell32/shlexec.c b/dlls/shell32/shlexec.c index 804aa38a904..9704f9d8f06 100644 --- a/dlls/shell32/shlexec.c +++ b/dlls/shell32/shlexec.c @@ -1797,10 +1797,10 @@ static BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) if (*sei_tmp.lpDirectory) { + LPWSTR buf; len = ExpandEnvironmentStringsW(sei_tmp.lpDirectory, NULL, 0); if (len > 0) { - LPWSTR buf; len++; buf = malloc(len * sizeof(WCHAR)); ExpandEnvironmentStringsW(sei_tmp.lpDirectory, buf, len); @@ -1809,6 +1809,18 @@ static BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) wszDir = buf; sei_tmp.lpDirectory = wszDir; } + + len = GetFullPathNameW(sei_tmp.lpDirectory, 0, NULL, NULL); + if (len > 0) + { + len++; + buf = malloc(len * sizeof(WCHAR)); + GetFullPathNameW(sei_tmp.lpDirectory, len, buf, NULL); + if (wszDir != dirBuffer) + free(wszDir); + wszDir = buf; + sei_tmp.lpDirectory = wszDir; + } } /* Else, try to execute the filename */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/5400
From: Yuxuan Shui <yshui(a)codeweavers.com> For ShellExecute, if the specified file is found, we will try running it anyway, even if it doesn't have a "program" extension. Windows associations will take precedence over this. --- dlls/shell32/shlexec.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/dlls/shell32/shlexec.c b/dlls/shell32/shlexec.c index 9704f9d8f06..91b5e10cbea 100644 --- a/dlls/shell32/shlexec.c +++ b/dlls/shell32/shlexec.c @@ -579,6 +579,7 @@ static UINT SHELL_FindExecutableByVerb(LPCWSTR lpVerb, LPWSTR key, LPWSTR classn * * Utility for code sharing between FindExecutable and ShellExecute * in: + * lpPath the path to search for the file * lpFile the name of a file * lpVerb the operation on it (open) * out: @@ -639,6 +640,7 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpVerb, { 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 @@ -696,7 +698,6 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpVerb, if (wcsicmp(tok, &extension[1]) == 0) /* have to skip the leading "." */ { - lstrcpyW(lpResult, xlpFile); /* Need to perhaps check that the file has a path * attached */ TRACE("found %s\n", debugstr_w(lpResult)); @@ -781,7 +782,7 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpVerb, } } - TRACE("returning %s\n", debugstr_w(lpResult)); + TRACE("returning path %s, retval %d\n", debugstr_w(lpResult), retval); return retval; } @@ -1885,6 +1886,22 @@ static BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) lstrcatW(lpstrTmpFile, lpFile); retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0); } + else if (retval == SE_ERR_NOASSOC && SHGetFileInfoW(wcmd, 0, NULL, 0, SHGFI_EXETYPE) == 0) + { + /* File found, but no association. And no other cases fit, this could be a + unix programs, try running it. We have to do this in a "catch-all" fashion because + unix programs can have any extensions. However, things get more complicated because + the file we find could be a Windows executable without the proper extensions, it could + be seen as unexpected if we start it, so we special case it here. */ + UINT exec_retval; + TRACE("No association found, trying as Unix binary %s\n", debugstr_w(wcmd)); + exec_retval = SHELL_quote_and_execute( wcmd, wszParameters, wszKeyname, + wszApplicationName, env, &sei_tmp, + sei, execfunc ); + TRACE("Unix binary returned %u\n", exec_retval); + if (exec_retval > 32) + retval = exec_retval; + } end: TRACE("retval %Iu\n", retval); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/5400
This is still not working for me So, calling this from my application. ShellExecute( NULL, NULL, "linuxbinary", "1", "C:\\Temp\\Application\\linux", SW_SHOW); Produces. 0024:trace:shell:PathFindOnPathExW (L"linuxbin",077D78A4,000000bf) 0024:trace:shell:PathFileExistsDefExtW (L"C:\\Temp\\Application\\linux\\linuxbin",191) 0024:trace:shell:PathFileExistsDefExtW (L"C:\\Temp\\Application\\linuxbin",191) 0024:trace:shell:SHLWAPI_PathFindInOtherDirs (L"linuxbin",000000bf) 0024:trace:shell:PathFileExistsDefExtW (L"C:\\windows\\system32\\linuxbin",191 ..... The first call to PathFileExistsDefExtW, is the exact location but for some reason it wants to continue looking though every search path. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/5400#note_68667
On Wed Apr 24 13:55:44 2024 +0000, Alistair Leslie-Hughes wrote:
This is still not working for me So, calling this from my application. ShellExecute( NULL, NULL, "linuxbinary", "1", "C:\\Temp\\Application\\linux", SW_SHOW); Produces. 0024:trace:shell:PathFindOnPathExW (L"linuxbin",077D78A4,000000bf) 0024:trace:shell:PathFileExistsDefExtW (L"C:\\Temp\\Application\\linux\\linuxbin",191) 0024:trace:shell:PathFileExistsDefExtW (L"C:\\Temp\\Application\\linuxbin",191) 0024:trace:shell:SHLWAPI_PathFindInOtherDirs (L"linuxbin",000000bf) 0024:trace:shell:PathFileExistsDefExtW (L"C:\\windows\\system32\\linuxbin",191 ..... The first call to PathFileExistsDefExtW, is the exact location but for some reason it wants to continue looking though every search path. Like I said, you need to call `ShellExecute( NULL, NULL, "linuxbinary.", "1", "C:\Temp\Application\linux", SW_SHOW);`, which is how `start.exe` would be doing.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/5400#note_68731
Huw Davies (@huw) commented about dlls/shell32/shlexec.c:
wszDir = buf; sei_tmp.lpDirectory = wszDir; } + + len = GetFullPathNameW(sei_tmp.lpDirectory, 0, NULL, NULL); + if (len > 0) + { + len++; + buf = malloc(len * sizeof(WCHAR)); + GetFullPathNameW(sei_tmp.lpDirectory, len, buf, NULL); + if (wszDir != dirBuffer) + free(wszDir);
This seems to be overly indented. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/5400#note_69104
Huw Davies (@huw) commented about dlls/shell32/shlexec.c:
lstrcatW(lpstrTmpFile, lpFile); retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0); } + else if (retval == SE_ERR_NOASSOC && SHGetFileInfoW(wcmd, 0, NULL, 0, SHGFI_EXETYPE) == 0) + { + /* File found, but no association. And no other cases fit, this could be a + unix programs, try running it. We have to do this in a "catch-all" fashion because + unix programs can have any extensions. However, things get more complicated because
EOL space. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/5400#note_69105
Huw Davies (@huw) commented about dlls/shell32/shlexec.c:
lstrcatW(lpstrTmpFile, lpFile); retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0); } + else if (retval == SE_ERR_NOASSOC && SHGetFileInfoW(wcmd, 0, NULL, 0, SHGFI_EXETYPE) == 0) + { + /* File found, but no association. And no other cases fit, this could be a + unix programs, try running it. We have to do this in a "catch-all" fashion because
program -- https://gitlab.winehq.org/wine/wine/-/merge_requests/5400#note_69106
participants (4)
-
Alistair Leslie-Hughes (@alesliehughes) -
Huw Davies (@huw) -
Yuxuan Shui -
Yuxuan Shui (@yshui)