Extend and improve w32-shell-execute on MS-Windows.

src/w32fns.c (Fw32_shell_execute) [!CYGWIN]: Use ShellExecuteEx, to
 support more "verbs".
This commit is contained in:
Eli Zaretskii 2014-03-21 10:51:02 +02:00
parent f58269c4b2
commit 6d731d4183
2 changed files with 146 additions and 99 deletions

View file

@ -1,3 +1,8 @@
2014-03-21 Eli Zaretskii <eliz@gnu.org>
* w32fns.c (Fw32_shell_execute) [!CYGWIN]: Use ShellExecuteEx, to
support more "verbs".
2014-03-21 Daniel Colascione <dancol@dancol.org>
Always prohibit dumping a dumped Emacs.

View file

@ -6868,24 +6868,33 @@ handler application, but typically it is one of the following common
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,97 +6945,6 @@ a ShowWindow flag:
GUI_SDATA (current_dir),
(INTEGERP (show_flag)
? XINT (show_flag) : SW_SHOWDEFAULT));
#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
CURRENT_DIR, but in its subdirectory, then ShellExecute below
will fail to find it. So we need to make the file name is
absolute. But DOCUMENT does not have to be a file, it can be a
URL, for example. So we make it absolute only if it is an
existing file; if it is a file that does not exist, tough. */
GCPRO1 (absdoc);
absdoc = Fexpand_file_name (document, Qnil);
/* Don't call file handlers for file-exists-p, since they might
attempt to access the file, which could fail or produce undesired
consequences, see bug#16558 for an example. */
handler = Ffind_file_name_handler (absdoc, Qfile_exists_p);
if (NILP (handler))
{
Lisp_Object absdoc_encoded = ENCODE_FILE (absdoc);
if (faccessat (AT_FDCWD, SSDATA (absdoc_encoded), F_OK, AT_EACCESS) == 0)
document = absdoc_encoded;
else
document = ENCODE_FILE (document);
}
else
document = ENCODE_FILE (document);
UNGCPRO;
if (use_unicode)
{
wchar_t document_w[MAX_PATH], current_dir_w[MAX_PATH];
/* Encode filename, current directory and parameters, and
convert operation to UTF-16. */
filename_to_utf16 (SSDATA (current_dir), current_dir_w);
filename_to_utf16 (SSDATA (document), document_w);
doc_w = document_w;
if (STRINGP (parameters))
{
int len;
parameters = ENCODE_SYSTEM (parameters);
len = pMultiByteToWideChar (CP_ACP, MB_ERR_INVALID_CHARS,
SSDATA (parameters), -1, NULL, 0);
if (len > 32768)
len = 32768;
params_w = alloca (len * sizeof (wchar_t));
pMultiByteToWideChar (CP_ACP, MB_ERR_INVALID_CHARS,
SSDATA (parameters), -1, params_w, len);
}
if (STRINGP (operation))
{
/* Assume OPERATION is pure ASCII. */
const char *s = SSDATA (operation);
wchar_t *d;
int len = SBYTES (operation) + 1;
if (len > 32768)
len = 32768;
d = ops_w = alloca (len * sizeof (wchar_t));
while (d < ops_w + len - 1)
*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));
}
else
{
char document_a[MAX_PATH], current_dir_a[MAX_PATH];
filename_to_ansi (SSDATA (current_dir), current_dir_a);
filename_to_ansi (SSDATA (document), document_a);
doc_a = document_a;
if (STRINGP (parameters))
{
parameters = ENCODE_SYSTEM (parameters);
params_a = SSDATA (parameters);
}
if (STRINGP (operation))
{
/* 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));
}
#endif /* !CYGWIN */
if (result > 32)
return Qt;
@ -7065,6 +6984,129 @@ a ShowWindow flag:
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
CURRENT_DIR, but in its subdirectory, then ShellExecute below
will fail to find it. So we need to make the file name is
absolute. But DOCUMENT does not have to be a file, it can be a
URL, for example. So we make it absolute only if it is an
existing file; if it is a file that does not exist, tough. */
GCPRO1 (absdoc);
absdoc = Fexpand_file_name (document, Qnil);
/* Don't call file handlers for file-exists-p, since they might
attempt to access the file, which could fail or produce undesired
consequences, see bug#16558 for an example. */
handler = Ffind_file_name_handler (absdoc, Qfile_exists_p);
if (NILP (handler))
{
Lisp_Object absdoc_encoded = ENCODE_FILE (absdoc);
if (faccessat (AT_FDCWD, SSDATA (absdoc_encoded), F_OK, AT_EACCESS) == 0)
document = absdoc_encoded;
else
document = ENCODE_FILE (document);
}
else
document = ENCODE_FILE (document);
UNGCPRO;
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. */
filename_to_utf16 (SSDATA (current_dir), current_dir_w);
filename_to_utf16 (SSDATA (document), document_w);
doc_w = document_w;
if (STRINGP (parameters))
{
int len;
parameters = ENCODE_SYSTEM (parameters);
len = pMultiByteToWideChar (CP_ACP, MB_ERR_INVALID_CHARS,
SSDATA (parameters), -1, NULL, 0);
if (len > 32768)
len = 32768;
params_w = alloca (len * sizeof (wchar_t));
pMultiByteToWideChar (CP_ACP, MB_ERR_INVALID_CHARS,
SSDATA (parameters), -1, params_w, len);
}
if (STRINGP (operation))
{
/* Assume OPERATION is pure ASCII. */
const char *s = SSDATA (operation);
wchar_t *d;
int len = SBYTES (operation) + 1;
if (len > 32768)
len = 32768;
d = ops_w = alloca (len * sizeof (wchar_t));
while (d < ops_w + len - 1)
*d++ = *s++;
*d = 0;
}
/* 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);
doc_a = document_a;
if (STRINGP (parameters))
{
parameters = ENCODE_SYSTEM (parameters);
params_a = SSDATA (parameters);
}
if (STRINGP (operation))
{
/* Assume OPERATION is pure ASCII. */
ops_a = SSDATA (operation);
}
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);
}
if (success)
return Qt;
errstr = w32_strerror (0);
#endif /* !CYGWIN */
/* The error string might be encoded in the locale's encoding. */
if (!NILP (Vlocale_coding_system))
{