From: Eli Zaretskii Date: Fri, 21 Mar 2014 08:51:02 +0000 (+0200) Subject: Extend and improve w32-shell-execute on MS-Windows. X-Git-Tag: emacs-25.0.90~2640^2~344^2~26^2~5 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=6d731d4183a5a11b5885199d51c26f0ad84872e8;p=emacs.git Extend and improve w32-shell-execute on MS-Windows. src/w32fns.c (Fw32_shell_execute) [!CYGWIN]: Use ShellExecuteEx, to support more "verbs". --- diff --git a/src/ChangeLog b/src/ChangeLog index fa6d2650528..0de2025c0ef 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,8 @@ +2014-03-21 Eli Zaretskii + + * w32fns.c (Fw32_shell_execute) [!CYGWIN]: Use ShellExecuteEx, to + support more "verbs". + 2014-03-21 Daniel Colascione Always prohibit dumping a dumped Emacs. diff --git a/src/w32fns.c b/src/w32fns.c index a5b8abfa6b1..a79135374ed 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -6868,24 +6868,33 @@ operations: specified DOCUMENT. \"find\" - initiate search starting from DOCUMENT, which must specify a directory. + \"delete\" - move DOCUMENT, a file or a directory, to Recycle Bin. + \"copy\" - copy DOCUMENT, which must be a file or a directory, into + the clipboard. + \"cut\" - move DOCUMENT, a file or a directory, into the clipboard. + \"paste\" - paste the file whose name is in the clipboard into DOCUMENT, + which must be a directory. + \"pastelink\" + - create a shortcut in DOCUMENT (which must be a directory) + the file or directory whose name is in the clipboard. \"runas\" - run DOCUMENT, which must be an excutable file, with elevated privileges (a.k.a. \"as Administrator\"). \"properties\" - - open the the property sheet dialog for DOCUMENT; works - for *.lnk desktop shortcuts, and little or nothing else. + - open the the property sheet dialog for DOCUMENT. nil - invoke the default OPERATION, or \"open\" if default is not defined or unavailable. DOCUMENT is typically the name of a document file or a URL, but can also be an executable program to run, or a directory to open in the -Windows Explorer. If it is a file, it must be a local one; this -function does not support remote file names. +Windows Explorer. If it is a file or a directory, it must be a local +one; this function does not support remote file names. If DOCUMENT is an executable program, the optional third arg PARAMETERS -can be a string containing command line parameters that will be passed -to the program. Some values of OPERATION also require parameters (e.g., -\"printto\" requires the printer address). Otherwise, PARAMETERS should -be nil or unspecified. +can be a string containing command line parameters, separated by blanks, +that will be passed to the program. Some values of OPERATION also require +parameters (e.g., \"printto\" requires the printer address). Otherwise, +PARAMETERS should be nil or unspecified. Note that double quote characters +in PARAMETERS must each be enclosed in 2 additional quotes, as in \"\"\". Optional fourth argument SHOW-FLAG can be used to control how the application will be displayed when it is invoked. If SHOW-FLAG is nil @@ -6908,6 +6917,7 @@ a ShowWindow flag: int use_unicode = w32_unicode_filenames; char *doc_a = NULL, *params_a = NULL, *ops_a = NULL; Lisp_Object absdoc, handler; + BOOL success; struct gcpro gcpro1; #endif @@ -6935,7 +6945,48 @@ a ShowWindow flag: GUI_SDATA (current_dir), (INTEGERP (show_flag) ? XINT (show_flag) : SW_SHOWDEFAULT)); + + if (result > 32) + return Qt; + + switch (result) + { + case SE_ERR_ACCESSDENIED: + errstr = w32_strerror (ERROR_ACCESS_DENIED); + break; + case SE_ERR_ASSOCINCOMPLETE: + case SE_ERR_NOASSOC: + errstr = w32_strerror (ERROR_NO_ASSOCIATION); + break; + case SE_ERR_DDEBUSY: + case SE_ERR_DDEFAIL: + errstr = w32_strerror (ERROR_DDE_FAIL); + break; + case SE_ERR_DDETIMEOUT: + errstr = w32_strerror (ERROR_TIMEOUT); + break; + case SE_ERR_DLLNOTFOUND: + errstr = w32_strerror (ERROR_DLL_NOT_FOUND); + break; + case SE_ERR_FNF: + errstr = w32_strerror (ERROR_FILE_NOT_FOUND); + break; + case SE_ERR_OOM: + errstr = w32_strerror (ERROR_NOT_ENOUGH_MEMORY); + break; + case SE_ERR_PNF: + errstr = w32_strerror (ERROR_PATH_NOT_FOUND); + break; + case SE_ERR_SHARE: + errstr = w32_strerror (ERROR_SHARING_VIOLATION); + break; + default: + errstr = w32_strerror (0); + break; + } + #else /* !CYGWIN */ + current_dir = ENCODE_FILE (current_dir); /* We have a situation here. If DOCUMENT is a relative file name, but its name includes leading directories, i.e. it lives not in @@ -6965,6 +7016,7 @@ a ShowWindow flag: if (use_unicode) { wchar_t document_w[MAX_PATH], current_dir_w[MAX_PATH]; + SHELLEXECUTEINFOW shexinfo_w; /* Encode filename, current directory and parameters, and convert operation to UTF-16. */ @@ -6998,14 +7050,28 @@ a ShowWindow flag: *d++ = *s++; *d = 0; } - result = (intptr_t) ShellExecuteW (NULL, ops_w, doc_w, params_w, - current_dir_w, - (INTEGERP (show_flag) - ? XINT (show_flag) : SW_SHOWDEFAULT)); + + /* Using ShellExecuteEx and setting the SEE_MASK_INVOKEIDLIST + flag succeeds with more OPERATIONs (a.k.a. "verbs"), as it is + able to invoke verbs from shortcut menu extensions, not just + static verbs listed in the Registry. */ + memset (&shexinfo_w, 0, sizeof (shexinfo_w)); + shexinfo_w.cbSize = sizeof (shexinfo_w); + shexinfo_w.fMask = + SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; + shexinfo_w.hwnd = NULL; + shexinfo_w.lpVerb = ops_w; + shexinfo_w.lpFile = doc_w; + shexinfo_w.lpParameters = params_w; + shexinfo_w.lpDirectory = current_dir_w; + shexinfo_w.nShow = + (INTEGERP (show_flag) ? XINT (show_flag) : SW_SHOWDEFAULT); + success = ShellExecuteExW (&shexinfo_w); } else { char document_a[MAX_PATH], current_dir_a[MAX_PATH]; + SHELLEXECUTEINFOA shexinfo_a; filename_to_ansi (SSDATA (current_dir), current_dir_a); filename_to_ansi (SSDATA (document), document_a); @@ -7020,51 +7086,27 @@ a ShowWindow flag: /* Assume OPERATION is pure ASCII. */ ops_a = SSDATA (operation); } - result = (intptr_t) ShellExecuteA (NULL, ops_a, doc_a, params_a, - current_dir_a, - (INTEGERP (show_flag) - ? XINT (show_flag) : SW_SHOWDEFAULT)); + memset (&shexinfo_a, 0, sizeof (shexinfo_a)); + shexinfo_a.cbSize = sizeof (shexinfo_a); + shexinfo_a.fMask = + SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; + shexinfo_a.hwnd = NULL; + shexinfo_a.lpVerb = ops_a; + shexinfo_a.lpFile = doc_a; + shexinfo_a.lpParameters = params_a; + shexinfo_a.lpDirectory = current_dir_a; + shexinfo_a.nShow = + (INTEGERP (show_flag) ? XINT (show_flag) : SW_SHOWDEFAULT); + success = ShellExecuteExA (&shexinfo_a); } -#endif /* !CYGWIN */ - if (result > 32) + if (success) return Qt; - switch (result) - { - case SE_ERR_ACCESSDENIED: - errstr = w32_strerror (ERROR_ACCESS_DENIED); - break; - case SE_ERR_ASSOCINCOMPLETE: - case SE_ERR_NOASSOC: - errstr = w32_strerror (ERROR_NO_ASSOCIATION); - break; - case SE_ERR_DDEBUSY: - case SE_ERR_DDEFAIL: - errstr = w32_strerror (ERROR_DDE_FAIL); - break; - case SE_ERR_DDETIMEOUT: - errstr = w32_strerror (ERROR_TIMEOUT); - break; - case SE_ERR_DLLNOTFOUND: - errstr = w32_strerror (ERROR_DLL_NOT_FOUND); - break; - case SE_ERR_FNF: - errstr = w32_strerror (ERROR_FILE_NOT_FOUND); - break; - case SE_ERR_OOM: - errstr = w32_strerror (ERROR_NOT_ENOUGH_MEMORY); - break; - case SE_ERR_PNF: - errstr = w32_strerror (ERROR_PATH_NOT_FOUND); - break; - case SE_ERR_SHARE: - errstr = w32_strerror (ERROR_SHARING_VIOLATION); - break; - default: - errstr = w32_strerror (0); - break; - } + errstr = w32_strerror (0); + +#endif /* !CYGWIN */ + /* The error string might be encoded in the locale's encoding. */ if (!NILP (Vlocale_coding_system)) {