Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/angle
Path: blob/main_old/util/windows/test_utils_win.cpp
1693 views
1
//
2
// Copyright 2014 The ANGLE Project Authors. All rights reserved.
3
// Use of this source code is governed by a BSD-style license that can be
4
// found in the LICENSE file.
5
//
6
7
// test_utils_win.cpp: Implementation of OS-specific functions for Windows
8
9
#include "util/test_utils.h"
10
11
#include <aclapi.h>
12
#include <stdarg.h>
13
#include <versionhelpers.h>
14
#include <windows.h>
15
#include <array>
16
#include <iostream>
17
#include <vector>
18
19
#include "anglebase/no_destructor.h"
20
#include "common/angleutils.h"
21
22
namespace angle
23
{
24
namespace
25
{
26
struct ScopedPipe
27
{
28
~ScopedPipe()
29
{
30
closeReadHandle();
31
closeWriteHandle();
32
}
33
bool closeReadHandle()
34
{
35
if (readHandle)
36
{
37
if (::CloseHandle(readHandle) == FALSE)
38
{
39
std::cerr << "Error closing write handle: " << GetLastError();
40
return false;
41
}
42
readHandle = nullptr;
43
}
44
45
return true;
46
}
47
bool closeWriteHandle()
48
{
49
if (writeHandle)
50
{
51
if (::CloseHandle(writeHandle) == FALSE)
52
{
53
std::cerr << "Error closing write handle: " << GetLastError();
54
return false;
55
}
56
writeHandle = nullptr;
57
}
58
59
return true;
60
}
61
62
bool valid() const { return readHandle != nullptr || writeHandle != nullptr; }
63
64
bool initPipe(SECURITY_ATTRIBUTES *securityAttribs)
65
{
66
if (::CreatePipe(&readHandle, &writeHandle, securityAttribs, 0) == FALSE)
67
{
68
std::cerr << "Error creating pipe: " << GetLastError() << "\n";
69
return false;
70
}
71
72
#if !defined(ANGLE_ENABLE_WINDOWS_UWP)
73
// Ensure the read handles to the pipes are not inherited.
74
if (::SetHandleInformation(readHandle, HANDLE_FLAG_INHERIT, 0) == FALSE)
75
{
76
std::cerr << "Error setting handle info on pipe: " << GetLastError() << "\n";
77
return false;
78
}
79
#endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
80
81
return true;
82
}
83
84
HANDLE readHandle = nullptr;
85
HANDLE writeHandle = nullptr;
86
};
87
88
// Returns false on EOF or error.
89
void ReadFromFile(bool blocking, HANDLE handle, std::string *out)
90
{
91
char buffer[8192];
92
DWORD bytesRead = 0;
93
94
while (true)
95
{
96
if (!blocking)
97
{
98
BOOL success = ::PeekNamedPipe(handle, nullptr, 0, nullptr, &bytesRead, nullptr);
99
if (success == FALSE || bytesRead == 0)
100
return;
101
}
102
103
BOOL success = ::ReadFile(handle, buffer, sizeof(buffer), &bytesRead, nullptr);
104
if (success == FALSE || bytesRead == 0)
105
return;
106
107
out->append(buffer, bytesRead);
108
}
109
110
// unreachable.
111
}
112
113
// Returns the Win32 last error code or ERROR_SUCCESS if the last error code is
114
// ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND. This is useful in cases where
115
// the absence of a file or path is a success condition (e.g., when attempting
116
// to delete an item in the filesystem).
117
bool ReturnSuccessOnNotFound()
118
{
119
const DWORD error_code = ::GetLastError();
120
return (error_code == ERROR_FILE_NOT_FOUND || error_code == ERROR_PATH_NOT_FOUND);
121
}
122
123
// Job objects seems to have problems on the Chromium CI and Windows 7.
124
bool ShouldUseJobObjects()
125
{
126
#if defined(ANGLE_ENABLE_WINDOWS_UWP)
127
return false;
128
#else
129
return (::IsWindows10OrGreater());
130
#endif
131
}
132
133
class WindowsProcess : public Process
134
{
135
public:
136
WindowsProcess(const std::vector<const char *> &commandLineArgs,
137
ProcessOutputCapture captureOutput)
138
{
139
mProcessInfo.hProcess = INVALID_HANDLE_VALUE;
140
mProcessInfo.hThread = INVALID_HANDLE_VALUE;
141
142
std::vector<char> commandLineString;
143
for (const char *arg : commandLineArgs)
144
{
145
if (arg)
146
{
147
if (!commandLineString.empty())
148
{
149
commandLineString.push_back(' ');
150
}
151
commandLineString.insert(commandLineString.end(), arg, arg + strlen(arg));
152
}
153
}
154
commandLineString.push_back('\0');
155
156
// Set the bInheritHandle flag so pipe handles are inherited.
157
SECURITY_ATTRIBUTES securityAttribs;
158
securityAttribs.nLength = sizeof(SECURITY_ATTRIBUTES);
159
securityAttribs.bInheritHandle = TRUE;
160
securityAttribs.lpSecurityDescriptor = nullptr;
161
162
STARTUPINFOA startInfo = {};
163
164
const bool captureStdout = captureOutput != ProcessOutputCapture::Nothing;
165
const bool captureStderr =
166
captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved ||
167
captureOutput == ProcessOutputCapture::StdoutAndStderrSeparately;
168
const bool pipeStderrToStdout =
169
captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved;
170
171
// Create pipes for stdout and stderr.
172
startInfo.cb = sizeof(STARTUPINFOA);
173
startInfo.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE);
174
if (captureStdout)
175
{
176
if (!mStdoutPipe.initPipe(&securityAttribs))
177
{
178
return;
179
}
180
startInfo.hStdOutput = mStdoutPipe.writeHandle;
181
}
182
else
183
{
184
startInfo.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
185
}
186
187
if (pipeStderrToStdout)
188
{
189
startInfo.hStdError = startInfo.hStdOutput;
190
}
191
else if (captureStderr)
192
{
193
if (!mStderrPipe.initPipe(&securityAttribs))
194
{
195
return;
196
}
197
startInfo.hStdError = mStderrPipe.writeHandle;
198
}
199
else
200
{
201
startInfo.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
202
}
203
204
#if !defined(ANGLE_ENABLE_WINDOWS_UWP)
205
if (captureStdout || captureStderr)
206
{
207
startInfo.dwFlags |= STARTF_USESTDHANDLES;
208
}
209
210
if (ShouldUseJobObjects())
211
{
212
// Create job object. Job objects allow us to automatically force child processes to
213
// exit if the parent process is unexpectedly killed. This should prevent ghost
214
// processes from hanging around.
215
mJobHandle = ::CreateJobObjectA(nullptr, nullptr);
216
if (mJobHandle == NULL)
217
{
218
std::cerr << "Error creating job object: " << GetLastError() << "\n";
219
return;
220
}
221
222
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limitInfo = {};
223
limitInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
224
if (::SetInformationJobObject(mJobHandle, JobObjectExtendedLimitInformation, &limitInfo,
225
sizeof(limitInfo)) == FALSE)
226
{
227
std::cerr << "Error setting job information: " << GetLastError() << "\n";
228
return;
229
}
230
}
231
#endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
232
233
// Create the child process.
234
if (::CreateProcessA(nullptr, commandLineString.data(), nullptr, nullptr,
235
TRUE, // Handles are inherited.
236
0, nullptr, nullptr, &startInfo, &mProcessInfo) == FALSE)
237
{
238
std::cerr << "CreateProcessA Error code: " << GetLastError() << "\n";
239
return;
240
}
241
242
#if !defined(ANGLE_ENABLE_WINDOWS_UWP)
243
if (mJobHandle != nullptr)
244
{
245
if (::AssignProcessToJobObject(mJobHandle, mProcessInfo.hProcess) == FALSE)
246
{
247
std::cerr << "AssignProcessToJobObject failed: " << GetLastError() << "\n";
248
return;
249
}
250
}
251
#endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
252
253
// Close the write end of the pipes, so EOF can be generated when child exits.
254
if (!mStdoutPipe.closeWriteHandle() || !mStderrPipe.closeWriteHandle())
255
return;
256
257
mStarted = true;
258
mTimer.start();
259
}
260
261
~WindowsProcess() override
262
{
263
if (mProcessInfo.hProcess != INVALID_HANDLE_VALUE)
264
{
265
::CloseHandle(mProcessInfo.hProcess);
266
}
267
if (mProcessInfo.hThread != INVALID_HANDLE_VALUE)
268
{
269
::CloseHandle(mProcessInfo.hThread);
270
}
271
if (mJobHandle != nullptr)
272
{
273
::CloseHandle(mJobHandle);
274
}
275
}
276
277
bool started() override { return mStarted; }
278
279
bool finish() override
280
{
281
if (mStdoutPipe.valid())
282
{
283
ReadFromFile(true, mStdoutPipe.readHandle, &mStdout);
284
}
285
286
if (mStderrPipe.valid())
287
{
288
ReadFromFile(true, mStderrPipe.readHandle, &mStderr);
289
}
290
291
DWORD result = ::WaitForSingleObject(mProcessInfo.hProcess, INFINITE);
292
mTimer.stop();
293
return result == WAIT_OBJECT_0;
294
}
295
296
bool finished() override
297
{
298
if (!mStarted)
299
return false;
300
301
// Pipe stdin and stdout.
302
if (mStdoutPipe.valid())
303
{
304
ReadFromFile(false, mStdoutPipe.readHandle, &mStdout);
305
}
306
307
if (mStderrPipe.valid())
308
{
309
ReadFromFile(false, mStderrPipe.readHandle, &mStderr);
310
}
311
312
DWORD result = ::WaitForSingleObject(mProcessInfo.hProcess, 0);
313
if (result == WAIT_OBJECT_0)
314
{
315
mTimer.stop();
316
return true;
317
}
318
if (result == WAIT_TIMEOUT)
319
return false;
320
321
mTimer.stop();
322
std::cerr << "Unexpected result from WaitForSingleObject: " << result
323
<< ". Last error: " << ::GetLastError() << "\n";
324
return false;
325
}
326
327
int getExitCode() override
328
{
329
if (!mStarted)
330
return -1;
331
332
if (mProcessInfo.hProcess == INVALID_HANDLE_VALUE)
333
return -1;
334
335
DWORD exitCode = 0;
336
if (::GetExitCodeProcess(mProcessInfo.hProcess, &exitCode) == FALSE)
337
return -1;
338
339
return static_cast<int>(exitCode);
340
}
341
342
bool kill() override
343
{
344
if (!mStarted)
345
return true;
346
347
HANDLE newHandle;
348
if (::DuplicateHandle(::GetCurrentProcess(), mProcessInfo.hProcess, ::GetCurrentProcess(),
349
&newHandle, PROCESS_ALL_ACCESS, false,
350
DUPLICATE_CLOSE_SOURCE) == FALSE)
351
{
352
std::cerr << "Error getting permission to terminate process: " << ::GetLastError()
353
<< "\n";
354
return false;
355
}
356
mProcessInfo.hProcess = newHandle;
357
358
#if !defined(ANGLE_ENABLE_WINDOWS_UWP)
359
if (::TerminateThread(mProcessInfo.hThread, 1) == FALSE)
360
{
361
std::cerr << "TerminateThread failed: " << GetLastError() << "\n";
362
return false;
363
}
364
#endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
365
366
if (::TerminateProcess(mProcessInfo.hProcess, 1) == FALSE)
367
{
368
std::cerr << "TerminateProcess failed: " << GetLastError() << "\n";
369
return false;
370
}
371
372
mStarted = false;
373
mTimer.stop();
374
return true;
375
}
376
377
private:
378
bool mStarted = false;
379
ScopedPipe mStdoutPipe;
380
ScopedPipe mStderrPipe;
381
PROCESS_INFORMATION mProcessInfo = {};
382
HANDLE mJobHandle = nullptr;
383
};
384
} // namespace
385
386
void Sleep(unsigned int milliseconds)
387
{
388
::Sleep(static_cast<DWORD>(milliseconds));
389
}
390
391
void WriteDebugMessage(const char *format, ...)
392
{
393
va_list args;
394
va_start(args, format);
395
int size = vsnprintf(nullptr, 0, format, args);
396
va_end(args);
397
398
std::vector<char> buffer(size + 2);
399
va_start(args, format);
400
vsnprintf(buffer.data(), size + 1, format, args);
401
va_end(args);
402
403
OutputDebugStringA(buffer.data());
404
}
405
406
Process *LaunchProcess(const std::vector<const char *> &args, ProcessOutputCapture captureOutput)
407
{
408
return new WindowsProcess(args, captureOutput);
409
}
410
411
bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen)
412
{
413
DWORD pathLen = ::GetTempPathA(maxDirNameLen, tempDirOut);
414
// Strip last path character if present.
415
if (pathLen > 0)
416
{
417
size_t lastChar = strlen(tempDirOut) - 1;
418
if (tempDirOut[lastChar] == '\\')
419
{
420
tempDirOut[lastChar] = 0;
421
}
422
}
423
return (pathLen < MAX_PATH && pathLen > 0);
424
}
425
426
bool CreateTemporaryFileInDir(const char *dir, char *tempFileNameOut, uint32_t maxFileNameLen)
427
{
428
char fileName[MAX_PATH + 1];
429
if (::GetTempFileNameA(dir, "ANGLE", 0, fileName) == 0)
430
return false;
431
432
strncpy(tempFileNameOut, fileName, maxFileNameLen);
433
return true;
434
}
435
436
bool DeleteFile(const char *path)
437
{
438
if (strlen(path) >= MAX_PATH)
439
return false;
440
441
const DWORD attr = ::GetFileAttributesA(path);
442
// Report success if the file or path does not exist.
443
if (attr == INVALID_FILE_ATTRIBUTES)
444
{
445
return ReturnSuccessOnNotFound();
446
}
447
448
// Clear the read-only bit if it is set.
449
if ((attr & FILE_ATTRIBUTE_READONLY) &&
450
!::SetFileAttributesA(path, attr & ~FILE_ATTRIBUTE_READONLY))
451
{
452
// It's possible for |path| to be gone now under a race with other deleters.
453
return ReturnSuccessOnNotFound();
454
}
455
456
// We don't handle directories right now.
457
if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
458
{
459
return false;
460
}
461
462
return !!::DeleteFileA(path) ? true : ReturnSuccessOnNotFound();
463
}
464
465
const char *GetNativeEGLLibraryNameWithExtension()
466
{
467
return "libEGL.dll";
468
}
469
} // namespace angle
470
471