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
|
// **************************************************************************
// * 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 EXECUTE_HEADER_23482134578134134
#define EXECUTE_HEADER_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: http://blogs.msdn.com/b/oldnewthing/archive/2014/02/27/10503519.aspx
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
{
if (!shellExecuteImpl([&](SHELLEXECUTEINFO& execInfo)
{
execInfo.fMask |= SEE_MASK_IDLIST;
execInfo.lpIDList = const_cast<void*>(shellItemPidl); //lpIDList is documented as PCIDLIST_ABSOLUTE!
}, type)) //throw FileError
throwFileError(_("Incorrect command line:") + L"\n" + fmtPath(displayPath), L"ShellExecuteEx", ::GetLastError());
}
#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;
if (LPWSTR* tmp = ::CommandLineToArgvW(commandTmp.c_str(), &argc))
{
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 iter = argv.begin() + 1; iter != argv.end(); ++iter)
arguments += (iter != argv.begin() ? L" " : L"") +
(iter->empty() || std::any_of(iter->begin(), iter->end(), &isWhiteSpace<wchar_t>) ? L"\"" + *iter + L"\"" : *iter);
}
if (!shellExecuteImpl([&](SHELLEXECUTEINFO& execInfo)
{
execInfo.lpFile = filepath.c_str();
execInfo.lpParameters = arguments.c_str();
}, type))
throwFileError(_("Incorrect command line:") + L"\nFile: " + fmtPath(filepath) + L"\nArg: " + copyStringTo<std::wstring>(arguments), L"ShellExecuteEx", ::GetLastError());
#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 //EXECUTE_HEADER_23482134578134134
|