1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
// **************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
#ifndef SHELL_EXECUTE_H_23482134578134134
#define SHELL_EXECUTE_H_23482134578134134
#include "file_error.h"
#ifdef ZEN_WIN
#include "scope_guard.h"
#include "win.h" //includes "windows.h"
#elif defined ZEN_LINUX || defined ZEN_MAC
#include "thread.h"
#include <stdlib.h> //::system()
#endif
namespace zen
{
//launch commandline and report errors via popup dialog
//windows: COM needs to be initialized before calling this function!
enum ExecutionType
{
EXEC_TYPE_SYNC,
EXEC_TYPE_ASYNC
};
namespace
{
#ifdef ZEN_WIN
template <class Function>
bool shellExecuteImpl(Function fillExecInfo, ExecutionType type)
{
SHELLEXECUTEINFO execInfo = {};
execInfo.cbSize = sizeof(execInfo);
execInfo.lpVerb = nullptr;
execInfo.nShow = SW_SHOWNORMAL;
execInfo.fMask = type == EXEC_TYPE_SYNC ? (SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC) : 0;
//don't use SEE_MASK_ASYNCOK -> different async mode than the default which returns successful despite errors!
execInfo.fMask |= SEE_MASK_FLAG_NO_UI; //::ShellExecuteEx() shows a non-blocking pop-up dialog on errors -> we want a blocking one
//for the record, SEE_MASK_UNICODE does nothing: https://blogs.msdn.microsoft.com/oldnewthing/20140227-00/?p=1643/
fillExecInfo(execInfo);
if (!::ShellExecuteEx(&execInfo)) //__inout LPSHELLEXECUTEINFO lpExecInfo
return false;
if (execInfo.hProcess)
{
ZEN_ON_SCOPE_EXIT(::CloseHandle(execInfo.hProcess));
if (type == EXEC_TYPE_SYNC)
::WaitForSingleObject(execInfo.hProcess, INFINITE);
}
return true;
}
void shellExecute(const void* /*PCIDLIST_ABSOLUTE*/ shellItemPidl, const std::wstring& displayPath, ExecutionType type) //throw FileError
{
auto fillExecInfo = [&](SHELLEXECUTEINFO& execInfo)
{
execInfo.fMask |= SEE_MASK_IDLIST;
execInfo.lpIDList = const_cast<void*>(shellItemPidl); //lpIDList is documented as PCIDLIST_ABSOLUTE!
};
if (!shellExecuteImpl(fillExecInfo , type)) //throw FileError
THROW_LAST_FILE_ERROR(_("Incorrect command line:") + L"\n" + fmtPath(displayPath), L"ShellExecuteEx");
}
#endif
void shellExecute(const Zstring& command, ExecutionType type) //throw FileError
{
#ifdef ZEN_WIN
//parse commandline
Zstring commandTmp = command;
trim(commandTmp, true, false); //CommandLineToArgvW() does not like leading spaces
std::vector<Zstring> argv;
{
int argc = 0;
LPWSTR* tmp = ::CommandLineToArgvW(commandTmp.c_str(), &argc);
if (!tmp)
THROW_LAST_FILE_ERROR(_("Incorrect command line:") + L"\n" + commandTmp.c_str(), L"CommandLineToArgvW");
ZEN_ON_SCOPE_EXIT(::LocalFree(tmp));
std::copy(tmp, tmp + argc, std::back_inserter(argv));
}
Zstring filepath;
Zstring arguments;
if (!argv.empty())
{
filepath = argv[0];
for (auto it = argv.begin() + 1; it != argv.end(); ++it)
arguments += (it != argv.begin() ? L" " : L"") +
(it->empty() || std::any_of(it->begin(), it->end(), &isWhiteSpace<wchar_t>) ? L"\"" + *it + L"\"" : *it);
}
auto fillExecInfo = [&](SHELLEXECUTEINFO& execInfo)
{
execInfo.lpFile = filepath.c_str();
execInfo.lpParameters = arguments.c_str();
};
if (!shellExecuteImpl(fillExecInfo, type))
THROW_LAST_FILE_ERROR(_("Incorrect command line:") + L"\nFile: " + fmtPath(filepath) + L"\nArg: " + arguments.c_str(), L"ShellExecuteEx");
#elif defined ZEN_LINUX || defined ZEN_MAC
/*
we cannot use wxExecute due to various issues:
- screws up encoding on OS X for non-ASCII characters
- does not provide any reasonable error information
- uses a zero-sized dummy window as a hack to keep focus which leaves a useless empty icon in ALT-TAB list in Windows
*/
if (type == EXEC_TYPE_SYNC)
{
//Posix::system - execute a shell command
int rv = ::system(command.c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect...
if (rv == -1 || WEXITSTATUS(rv) == 127) //http://linux.die.net/man/3/system "In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127)"
throw FileError(_("Incorrect command line:") + L"\n" + utfCvrtTo<std::wstring>(command));
}
else
runAsync([=] { int rv = ::system(command.c_str()); (void)rv; });
#endif
}
}
}
#endif //SHELL_EXECUTE_H_23482134578134134
|