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`
-- v5: shell32: Restore the ability of running native unix programs with ShellExecute
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 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 */ }
From: Yuxuan Shui yshui@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 */
From: Yuxuan Shui yshui@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 | 18 ++++++++++++++++-- dlls/shell32/tests/shlexec.c | 5 ++++- 2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/dlls/shell32/shlexec.c b/dlls/shell32/shlexec.c index 9704f9d8f06..c915d1f6490 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,19 @@ 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) + { + /* File found, but no association. And no other cases fit, this could be a + unix programs, try running it. */ + 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); diff --git a/dlls/shell32/tests/shlexec.c b/dlls/shell32/tests/shlexec.c index 0f537d829f4..6969846687a 100644 --- a/dlls/shell32/tests/shlexec.c +++ b/dlls/shell32/tests/shlexec.c @@ -2330,8 +2330,11 @@ static void test_exes(void) sprintf(filename, "%s\test file.noassoc", tmpdir); if (CopyFileA(argv0, filename, FALSE)) { + /* Because of wine's unix integration, we will try running "test file.noassoc" because + * it could be a unix program. And it will succeed because it happens to be a valid PE. + */ rc=shell_execute(NULL, filename, params, NULL); - okShell(rc==SE_ERR_NOASSOC, "returned %Iu\n", rc); + todo_wine okShell(rc==SE_ERR_NOASSOC, "returned %Iu\n", rc); } } else
On Fri Mar 29 18:13:51 2024 +0000, Yuxuan Shui wrote:
what about "linuxbinary." ?
This could be any compiled binary. Any sample "hello world" would work
This still doesn't work for my example above.
On Sun Apr 7 22:14:15 2024 +0000, Alistair Leslie-Hughes wrote:
This could be any compiled binary. Any sample "hello world" would work
Did you try calling `ShellExecute( NULL, NULL, "linuxbinary.", "1", "c:\temp", SW_SHOW);`?
However, `file.noassoc` could actually be a valid PE file, and running it would succeed. Is this a desirable outcome?
It would be better to avoid it if possible.
On Wed Apr 10 13:45:11 2024 +0000, Yuxuan Shui wrote:
Did you try calling `ShellExecute( NULL, NULL, "linuxbinary.", "1", "c:\temp", SW_SHOW);`?
@yshui I believe what @alesliehughes is referring to is running a file that has no extension (and no trailing dot).
On Wed Apr 17 18:42:13 2024 +0000, Myah Caron wrote:
@yshui I believe what @alesliehughes is referring to is running a file that has no extension (and no trailing dot).
@qsniyg if using the procedure described here: https://wiki.winehq.org/FAQ#How_do_I_launch_native_applications_from_a_Windo... then `start` or `cmd` will append the '.' to the file name.
On Wed Apr 10 14:25:30 2024 +0000, Alexandre Julliard wrote:
However, `file.noassoc` could actually be a valid PE file, and running
it would succeed. Is this a desirable outcome? It would be better to avoid it if possible.
But how? Should we add a internal API to "launch this only if it's not a PE file"? what if user has binfmt set up?
On Mon Apr 22 08:52:45 2024 +0000, Yuxuan Shui wrote:
But how? Should we add a internal API to "launch this only if it's not a PE file"? what if user has binfmt set up?
You should be able to use something like `SHGetFileInfoW(SHGFI_EXETYPE)`.