Added cmd-launch-association patchset

This commit is contained in:
Alistair Leslie-Hughes 2019-07-16 14:18:17 +10:00
parent 0bdc57245a
commit 394c51bd20
4 changed files with 306 additions and 0 deletions

View File

@ -0,0 +1,203 @@
From 25b5e818272cf6fc52a8707c80b9ade3a5ca5df5 Mon Sep 17 00:00:00 2001
From: Jason Edmeades <us@edmeades.me.uk>
Date: Tue, 16 Jul 2019 13:49:18 +1000
Subject: [PATCH 1/2] cmd: Support for launching programs based on file
association
cmd already handles exe, cmd, bat etc but if you run a file with another extension,
then use the associations set in the registry (for example via ftype / assoc) to
launch a program. This enables you to run test.txt and notepad to pop up, or
fred.msi for msiexec to be launched.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=18154
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=36646
---
programs/cmd/wcmdmain.c | 140 +++++++++++++++++++++++++++++-----------
1 file changed, 104 insertions(+), 36 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index 29e498a48d..2a9e4c75ee 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -1142,8 +1142,10 @@ void WCMD_run_program (WCHAR *command, BOOL called)
/* 1. If extension supplied, see if that file exists */
if (extensionsupplied) {
- if (GetFileAttributesW(thisDir) != INVALID_FILE_ATTRIBUTES) {
+ DWORD attribs = GetFileAttributesW(thisDir);
+ if (attribs != INVALID_FILE_ATTRIBUTES && !(attribs&FILE_ATTRIBUTE_DIRECTORY)) {
found = TRUE;
+ WINE_TRACE("Found as file with extension as '%s'\n", wine_dbgstr_w(thisDir));
}
}
@@ -1174,6 +1176,7 @@ void WCMD_run_program (WCHAR *command, BOOL called)
}
if (GetFileAttributesW(thisDir) != INVALID_FILE_ATTRIBUTES) {
+ WINE_TRACE("Found via search and pathext as '%s'\n", wine_dbgstr_w(thisDir));
found = TRUE;
thisExt = NULL;
}
@@ -1191,58 +1194,123 @@ void WCMD_run_program (WCHAR *command, BOOL called)
WCHAR *ext = wcsrchr( thisDir, '.' );
static const WCHAR batExt[] = {'.','b','a','t','\0'};
static const WCHAR cmdExt[] = {'.','c','m','d','\0'};
+ static const WCHAR exeExt[] = {'.','e','x','e','\0'};
+ static const WCHAR comExt[] = {'.','c','o','m','\0'};
WINE_TRACE("Found as %s\n", wine_dbgstr_w(thisDir));
/* Special case BAT and CMD */
if (ext && (!wcsicmp(ext, batExt) || !wcsicmp(ext, cmdExt))) {
BOOL oldinteractive = interactive;
+ WINE_TRACE("Calling batch program\n");
interactive = FALSE;
WCMD_batch (thisDir, command, called, NULL, INVALID_HANDLE_VALUE);
interactive = oldinteractive;
+ WINE_TRACE("Back from call to batch program\n");
return;
- } else {
+ }
- /* thisDir contains the file to be launched, but with what?
- eg. a.exe will require a.exe to be launched, a.html may be iexplore */
- hinst = FindExecutableW (thisDir, NULL, temp);
- if ((INT_PTR)hinst < 32)
- console = 0;
- else
- console = SHGetFileInfoW(temp, 0, &psfi, sizeof(psfi), SHGFI_EXETYPE);
-
- ZeroMemory (&st, sizeof(STARTUPINFOW));
- st.cb = sizeof(STARTUPINFOW);
- init_msvcrt_io_block(&st);
-
- /* Launch the process and if a CUI wait on it to complete
- Note: Launching internal wine processes cannot specify a full path to exe */
- status = CreateProcessW(thisDir,
- command, NULL, NULL, TRUE, 0, NULL, NULL, &st, &pe);
- heap_free(st.lpReserved2);
- if ((opt_c || opt_k) && !opt_s && !status
- && GetLastError()==ERROR_FILE_NOT_FOUND && command[0]=='\"') {
- /* strip first and last quote WCHARacters and try again */
- WCMD_strip_quotes(command);
- opt_s = TRUE;
- WCMD_run_program(command, called);
+ /* Calculate what program will be launched, and whether it is a
+ console application or not. Note the program may be different
+ from the parameter (eg running a .txt file will launch notepad.exe) */
+ hinst = FindExecutableW (thisDir, NULL, temp);
+ if ((INT_PTR)hinst < 32)
+ console = 0; /* Assume not console app by default */
+ else
+ console = SHGetFileInfoW(temp, 0, &psfi, sizeof(psfi), SHGFI_EXETYPE);
+
+
+ /* If it is not a .com or .exe, try to launch through ShellExecuteExW
+ which takes into account the association for the extension. */
+ if (ext && (wcsicmp(ext, exeExt) && wcsicmp(ext, comExt))) {
+
+ SHELLEXECUTEINFOW shexw;
+ BOOL rc;
+ WCHAR *rawarg;
+
+ WCMD_parameter(command, 1, &rawarg, FALSE, TRUE);
+ WINE_TRACE("Launching via ShellExecuteEx\n");
+ memset(&shexw, 0x00, sizeof(shexw));
+ shexw.cbSize = sizeof(SHELLEXECUTEINFOW);
+ shexw.fMask = SEE_MASK_NO_CONSOLE | /* Run in same console as currently using */
+ SEE_MASK_NOCLOSEPROCESS; /* We need a process handle to possibly wait on */
+ shexw.lpFile = thisDir;
+ shexw.lpParameters = rawarg;
+ shexw.nShow = SW_SHOWNORMAL;
+
+ /* Try to launch the binary or its associated program */
+ rc = ShellExecuteExW(&shexw);
+
+ if (rc && (INT_PTR)shexw.hInstApp >= 32) {
+
+ WINE_TRACE("Successfully launched\n");
+
+ /* It worked... Always wait when non-interactive (cmd /c or in
+ batch program), or for console applications */
+ if (!interactive || (console && !HIWORD(console))) {
+ WINE_TRACE("Waiting for process to end\n");
+ WaitForSingleObject (shexw.hProcess, INFINITE);
+ }
+
+ GetExitCodeProcess (shexw.hProcess, &errorlevel);
+ if (errorlevel == STILL_ACTIVE) {
+ WINE_TRACE("Process still running, but returning anyway\n");
+ errorlevel = 0;
+ } else {
+ WINE_TRACE("Process ended, errorlevel %d\n", errorlevel);
+ }
+
+ CloseHandle(pe.hProcess);
return;
+
}
+ }
- if (!status)
- break;
+ /* If its a .exe or .com or the shellexecute failed due to no association,
+ CreateProcess directly */
+ ZeroMemory (&st, sizeof(STARTUPINFOW));
+ st.cb = sizeof(STARTUPINFOW);
+ init_msvcrt_io_block(&st);
+
+ /* Launch the process and if a CUI wait on it to complete
+ Note: Launching internal wine processes cannot specify a full path to exe */
+ WINE_TRACE("Launching via CreateProcess\n");
+ status = CreateProcessW(thisDir,
+ command, NULL, NULL, TRUE, 0, NULL, NULL, &st, &pe);
+ heap_free(st.lpReserved2);
+ if ((opt_c || opt_k) && !opt_s && !status
+ && GetLastError()==ERROR_FILE_NOT_FOUND && command[0]=='\"') {
+ /* strip first and last quote WCHARacters and try again */
+ WCMD_strip_quotes(command);
+ opt_s = TRUE;
+ WCMD_run_program(command, called);
+ return;
+ }
- /* Always wait when non-interactive (cmd /c or in batch program),
- or for console applications */
- if (!interactive || (console && !HIWORD(console)))
- WaitForSingleObject (pe.hProcess, INFINITE);
- GetExitCodeProcess (pe.hProcess, &errorlevel);
- if (errorlevel == STILL_ACTIVE) errorlevel = 0;
+ if (!status) {
+ WINE_TRACE("Failed to launch via CreateProcess, rc %d (%d)\n",
+ status, GetLastError());
+ break;
+ }
- CloseHandle(pe.hProcess);
- CloseHandle(pe.hThread);
- return;
+ /* Always wait when non-interactive (cmd /c or in batch program),
+ or for console applications */
+ if (!interactive || (console && !HIWORD(console))) {
+ WINE_TRACE("Waiting for process to end\n");
+ WaitForSingleObject (pe.hProcess, INFINITE);
}
+
+ GetExitCodeProcess (pe.hProcess, &errorlevel);
+ if (errorlevel == STILL_ACTIVE) {
+ WINE_TRACE("Process still running, but returning anyway\n");
+ errorlevel = 0;
+ } else {
+ WINE_TRACE("Process ended, errorlevel %d\n", errorlevel);
+ }
+
+ CloseHandle(pe.hProcess);
+ CloseHandle(pe.hThread);
+ return;
}
}
--
2.17.1

View File

@ -0,0 +1,79 @@
From 7bb9825d921f91be01144110f6478a0c017cbd79 Mon Sep 17 00:00:00 2001
From: Jason Edmeades <us@edmeades.me.uk>
Date: Tue, 16 Jul 2019 13:51:58 +1000
Subject: [PATCH 2/2] cmd: ftype failed to clear file associations
If a file association was set (e.g. ftype fred=xxx), ftype fred= needs to clear it,
but previously it failed to do so.
---
programs/cmd/builtins.c | 6 +++---
programs/cmd/tests/test_builtins.cmd | 9 +++++++++
programs/cmd/tests/test_builtins.cmd.exp | 5 ++++-
3 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 62ae19a494..e85e553c05 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -4939,11 +4939,11 @@ void WCMD_assoc (const WCHAR *args, BOOL assoc) {
/* If nothing after '=' then clear value - only valid for ASSOC */
if (*newValue == 0x00) {
- if (assoc) rc = RegDeleteKeyW(key, args);
- if (assoc && rc == ERROR_SUCCESS) {
+ rc = RegDeleteTreeW(key, args);
+ if (rc == ERROR_SUCCESS) {
WINE_TRACE("HKCR Key '%s' deleted\n", wine_dbgstr_w(args));
- } else if (assoc && rc != ERROR_FILE_NOT_FOUND) {
+ } else if (rc != ERROR_FILE_NOT_FOUND) {
WCMD_print_error();
errorlevel = 2;
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd
index f4dfc9f26d..0ef049a4ab 100644
--- a/programs/cmd/tests/test_builtins.cmd
+++ b/programs/cmd/tests/test_builtins.cmd
@@ -2440,6 +2440,12 @@ echo echo +++>> tmp.cmd
echo ftype footype>> tmp.cmd
cmd /c tmp.cmd
+echo --- testing association
+ftype footype=cmd.exe /c "echo '%%1'"
+echo dummy>test.foo
+test.foo
+del test.foo
+
echo --- resetting association
assoc .foo=
@@ -2471,6 +2477,9 @@ echo .foo=footype
echo footype=foo_opencmd
echo +++
echo footype=foo_opencmd
+echo --- testing association
+echo footype=cmd.exe /c "echo '%%1'"
+echo Skipped as not enough permissions
echo --- resetting association
echo original value
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp
index d78d91a6cf..e6ebe06714 100644
--- a/programs/cmd/tests/test_builtins.cmd.exp
+++ b/programs/cmd/tests/test_builtins.cmd.exp
@@ -1444,8 +1444,11 @@ footype=foo_opencmd
footype=foo_opencmd
+++
footype=foo_opencmd
+--- testing association
+footype=cmd.exe /c "echo '%1'"
+'@drive@@path@foobar\test.foo'@or_broken@Skipped as not enough permissions
--- resetting association
-@todo_wine@original value@or_broken@buggyXP@or_broken@!WINE_FOO!
+original value@or_broken@buggyXP@or_broken@!WINE_FOO!
------------ Testing CALL ------------
--- external script
foo@space@
--
2.17.1

View File

@ -0,0 +1,2 @@
Fixes: [18154] cmd: Support for launching programs based on file association
# Fixes: [36646]

View File

@ -92,6 +92,7 @@ patch_enable_all ()
enable_advapi32_Token_Integrity_Level="$1"
enable_api_ms_win_Stub_DLLs="$1"
enable_atl_AtlAxDialogBox="$1"
enable_cmd_launch_association="$1"
enable_combase_GetRestrictedErrorInfo="$1"
enable_comctl32_Listview_DrawItem="$1"
enable_comctl32_alpha_bitmaps="$1"
@ -407,6 +408,9 @@ patch_enable ()
atl-AtlAxDialogBox)
enable_atl_AtlAxDialogBox="$2"
;;
cmd-launch-association)
enable_cmd_launch_association="$2"
;;
combase-GetRestrictedErrorInfo)
enable_combase_GetRestrictedErrorInfo="$2"
;;
@ -2335,6 +2339,24 @@ if test "$enable_atl_AtlAxDialogBox" -eq 1; then
) >> "$patchlist"
fi
# Patchset cmd-launch-association
# |
# | This patchset fixes the following Wine bugs:
# | * [#18154] cmd: Support for launching programs based on file association
# |
# | Modified files:
# | * programs/cmd/builtins.c, programs/cmd/tests/test_builtins.cmd, programs/cmd/tests/test_builtins.cmd.exp,
# | programs/cmd/wcmdmain.c
# |
if test "$enable_cmd_launch_association" -eq 1; then
patch_apply cmd-launch-association/0001-cmd-Support-for-launching-programs-based-on-file-ass.patch
patch_apply cmd-launch-association/0002-cmd-ftype-failed-to-clear-file-associations.patch
(
printf '%s\n' '+ { "Jason Edmeades", "cmd: Support for launching programs based on file association.", 1 },';
printf '%s\n' '+ { "Jason Edmeades", "cmd: Ftype failed to clear file associations.", 1 },';
) >> "$patchlist"
fi
# Patchset combase-GetRestrictedErrorInfo
# |
# | This patchset fixes the following Wine bugs: