LibreOffice Module comphelper (master)  1
windows_process.cxx
Go to the documentation of this file.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #define WIN32_LEAN_AND_MEAN
6 #include <windows.h>
7 
8 #include <cstring>
9 #include <wchar.h>
10 
12 
13 // Needed for CreateEnvironmentBlock
14 #include <userenv.h>
15 #pragma comment(lib, "userenv.lib")
16 
22 static int ArgStrLen(const wchar_t *s)
23 {
24  int i = wcslen(s);
25  bool hasDoubleQuote = wcschr(s, L'"') != nullptr;
26  // Only add doublequotes if the string contains a space or a tab
27  bool addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
28 
29  if (addDoubleQuotes)
30  {
31  i += 2; // initial and final doublequote
32  }
33 
34  if (hasDoubleQuote)
35  {
36  int backslashes = 0;
37  while (*s)
38  {
39  if (*s == '\\')
40  {
41  ++backslashes;
42  }
43  else
44  {
45  if (*s == '"')
46  {
47  // Escape the doublequote and all backslashes preceding the doublequote
48  i += backslashes + 1;
49  }
50 
51  backslashes = 0;
52  }
53 
54  ++s;
55  }
56  }
57 
58  return i;
59 }
60 
70 static wchar_t* ArgToString(wchar_t *d, const wchar_t *s)
71 {
72  bool hasDoubleQuote = wcschr(s, L'"') != nullptr;
73  // Only add doublequotes if the string contains a space or a tab
74  bool addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
75 
76  if (addDoubleQuotes)
77  {
78  *d = '"'; // initial doublequote
79  ++d;
80  }
81 
82  if (hasDoubleQuote)
83  {
84  int backslashes = 0;
85  while (*s)
86  {
87  if (*s == '\\')
88  {
89  ++backslashes;
90  }
91  else
92  {
93  if (*s == '"')
94  {
95  // Escape the doublequote and all backslashes preceding the doublequote
96  for (int i = 0; i <= backslashes; ++i)
97  {
98  *d = '\\';
99  ++d;
100  }
101  }
102 
103  backslashes = 0;
104  }
105 
106  *d = *s;
107  ++d;
108  ++s;
109  }
110  }
111  else
112  {
113  wcscpy(d, s);
114  d += wcslen(s);
115  }
116 
117  if (addDoubleQuotes)
118  {
119  *d = '"'; // final doublequote
120  ++d;
121  }
122 
123  return d;
124 }
125 
132 wchar_t*
133 MakeCommandLine(int argc, wchar_t **argv)
134 {
135  int i;
136  int len = 0;
137 
138  // The + 1 of the last argument handles the allocation for null termination
139  for (i = 0; i < argc && argv[i]; ++i)
140  len += ArgStrLen(argv[i]) + 1;
141 
142  // Protect against callers that pass 0 arguments
143  if (len == 0)
144  len = 1;
145 
146  wchar_t *s = static_cast<wchar_t*>(malloc(len * sizeof(wchar_t)));
147  if (!s)
148  return nullptr;
149 
150  wchar_t *c = s;
151  for (i = 0; i < argc && argv[i]; ++i)
152  {
153  c = ArgToString(c, argv[i]);
154  if (i + 1 != argc)
155  {
156  *c = ' ';
157  ++c;
158  }
159  }
160 
161  *c = '\0';
162 
163  return s;
164 }
165 
166 BOOL
167 WinLaunchChild(const wchar_t *exePath,
168  int argc,
169  wchar_t **argv,
170  HANDLE userToken,
171  HANDLE *hProcess)
172 {
173  wchar_t *cl;
174  bool ok;
175 
176  cl = MakeCommandLine(argc, argv);
177  if (!cl)
178  {
179  return FALSE;
180  }
181 
182  STARTUPINFOW si;
183  std::memset(&si, 0, sizeof si);
184  si.cb = sizeof(STARTUPINFOW);
185  si.lpDesktop = const_cast<LPWSTR>(L"winsta0\\Default");
186  PROCESS_INFORMATION pi;
187  std::memset(&pi, 0, sizeof pi);
188 
189  if (userToken == nullptr)
190  {
191  ok = CreateProcessW(exePath,
192  cl,
193  nullptr, // no special security attributes
194  nullptr, // no special thread attributes
195  FALSE, // don't inherit filehandles
196  0, // creation flags
197  nullptr, // inherit my environment
198  nullptr, // use my current directory
199  &si,
200  &pi);
201  }
202  else
203  {
204  // Create an environment block for the process we're about to start using
205  // the user's token.
206  LPVOID environmentBlock = nullptr;
207  if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE))
208  {
209  environmentBlock = nullptr;
210  }
211 
212  ok = CreateProcessAsUserW(userToken,
213  exePath,
214  cl,
215  nullptr, // no special security attributes
216  nullptr, // no special thread attributes
217  FALSE, // don't inherit filehandles
218  0, // creation flags
219  environmentBlock,
220  nullptr, // use my current directory
221  &si,
222  &pi);
223 
224  if (environmentBlock)
225  {
226  DestroyEnvironmentBlock(environmentBlock);
227  }
228  }
229 
230  if (ok)
231  {
232  if (hProcess)
233  {
234  *hProcess = pi.hProcess; // the caller now owns the HANDLE
235  }
236  else
237  {
238  CloseHandle(pi.hProcess);
239  }
240  CloseHandle(pi.hThread);
241  }
242  else
243  {
244  LPVOID lpMsgBuf = nullptr;
245  FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
246  FORMAT_MESSAGE_FROM_SYSTEM |
247  FORMAT_MESSAGE_IGNORE_INSERTS,
248  nullptr,
249  GetLastError(),
250  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
251  reinterpret_cast<LPWSTR>(&lpMsgBuf),
252  0,
253  nullptr);
254  wprintf(L"Error restarting: %s\n", lpMsgBuf ? lpMsgBuf : L"(null)");
255  if (lpMsgBuf)
256  HeapFree(GetProcessHeap(), 0, lpMsgBuf);
257  }
258 
259  free(cl);
260 
261  return ok;
262 }
const wchar_t *typedef BOOL
wchar_t * MakeCommandLine(int argc, wchar_t **argv)
Creates a command line from a list of arguments.
static int ArgStrLen(const wchar_t *s)
Get the length that the string will take and takes into account the additional length if the string n...
static wchar_t * ArgToString(wchar_t *d, const wchar_t *s)
Copy string "s" to string "d", quoting the argument as appropriate and escaping doublequotes along wi...
#define TRUE
int i
BOOL WinLaunchChild(const wchar_t *exePath, int argc, wchar_t **argv, HANDLE userToken, HANDLE *hProcess)
Launch a child process with the specified arguments.
#define FALSE