// // Copyright (c) 2014 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // system_utils_win.cpp: Implementation of OS-specific functions for Windows #include "system_utils.h" #include #include #include #include namespace angle { namespace { struct ScopedPipe { ~ScopedPipe() { closeReadHandle(); closeWriteHandle(); } void closeReadHandle() { if (readHandle) { CloseHandle(readHandle); readHandle = nullptr; } } void closeWriteHandle() { if (writeHandle) { CloseHandle(writeHandle); writeHandle = nullptr; } } HANDLE readHandle = nullptr; HANDLE writeHandle = nullptr; }; void ReadEntireFile(HANDLE handle, std::string *out) { out->clear(); while (true) { char buffer[256]; DWORD bytesRead; BOOL success = ReadFile(handle, buffer, sizeof(buffer), &bytesRead, nullptr); if (!success || bytesRead == 0) { break; } out->append(buffer, bytesRead); } } } // anonymous namespace std::string GetExecutablePath() { std::array executableFileBuf; DWORD executablePathLen = GetModuleFileNameA(nullptr, executableFileBuf.data(), static_cast(executableFileBuf.size())); return (executablePathLen > 0 ? std::string(executableFileBuf.data()) : ""); } std::string GetExecutableDirectory() { std::string executablePath = GetExecutablePath(); size_t lastPathSepLoc = executablePath.find_last_of("\\/"); return (lastPathSepLoc != std::string::npos) ? executablePath.substr(0, lastPathSepLoc) : ""; } const char *GetSharedLibraryExtension() { return "dll"; } Optional GetCWD() { std::array pathBuf; DWORD result = GetCurrentDirectoryA(static_cast(pathBuf.size()), pathBuf.data()); if (result == 0) { return Optional::Invalid(); } return std::string(pathBuf.data()); } bool SetCWD(const char *dirName) { return (SetCurrentDirectoryA(dirName) == TRUE); } bool UnsetEnvironmentVar(const char *variableName) { return (SetEnvironmentVariableA(variableName, nullptr) == TRUE); } bool SetEnvironmentVar(const char *variableName, const char *value) { return (SetEnvironmentVariableA(variableName, value) == TRUE); } std::string GetEnvironmentVar(const char *variableName) { std::array oldValue; DWORD result = GetEnvironmentVariableA(variableName, oldValue.data(), static_cast(oldValue.size())); if (result == 0) { return std::string(); } else { return std::string(oldValue.data()); } } const char *GetPathSeparator() { return ";"; } bool RunApp(const std::vector &args, std::string *stdoutOut, std::string *stderrOut, int *exitCodeOut) { ScopedPipe stdoutPipe; ScopedPipe stderrPipe; SECURITY_ATTRIBUTES sa_attr; // Set the bInheritHandle flag so pipe handles are inherited. sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES); sa_attr.bInheritHandle = TRUE; sa_attr.lpSecurityDescriptor = nullptr; // Create pipes for stdout and stderr. Ensure the read handles to the pipes are not inherited. if (stdoutOut && !CreatePipe(&stdoutPipe.readHandle, &stdoutPipe.writeHandle, &sa_attr, 0) && !SetHandleInformation(stdoutPipe.readHandle, HANDLE_FLAG_INHERIT, 0)) { return false; } if (stderrOut && !CreatePipe(&stderrPipe.readHandle, &stderrPipe.writeHandle, &sa_attr, 0) && !SetHandleInformation(stderrPipe.readHandle, HANDLE_FLAG_INHERIT, 0)) { return false; } // Concat the nicely separated arguments into one string so the application has to reparse it. // We don't support quotation and spaces in arguments currently. std::vector commandLineString; for (const char *arg : args) { if (arg) { if (!commandLineString.empty()) { commandLineString.push_back(' '); } commandLineString.insert(commandLineString.end(), arg, arg + strlen(arg)); } } commandLineString.push_back('\0'); STARTUPINFOA startInfo = {}; startInfo.cb = sizeof(STARTUPINFOA); startInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); if (stdoutOut) { startInfo.hStdOutput = stdoutPipe.writeHandle; } else { startInfo.hStdError = GetStdHandle(STD_OUTPUT_HANDLE); } if (stderrOut) { startInfo.hStdError = stderrPipe.writeHandle; } else { startInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); } if (stderrOut || stdoutOut) { startInfo.dwFlags |= STARTF_USESTDHANDLES; } // Create the child process. PROCESS_INFORMATION processInfo = {}; if (!CreateProcessA(nullptr, commandLineString.data(), nullptr, nullptr, TRUE, // Handles are inherited. 0, nullptr, nullptr, &startInfo, &processInfo)) { return false; } // Close the write end of the pipes, so EOF can be generated when child exits. stdoutPipe.closeWriteHandle(); stderrPipe.closeWriteHandle(); // Read back the output of the child. if (stdoutOut) { ReadEntireFile(stdoutPipe.readHandle, stdoutOut); } if (stderrOut) { ReadEntireFile(stderrPipe.readHandle, stderrOut); } // Cleanup the child. bool success = WaitForSingleObject(processInfo.hProcess, INFINITE) == WAIT_OBJECT_0; if (success) { DWORD exitCode = 0; success = GetExitCodeProcess(processInfo.hProcess, &exitCode); if (success) { *exitCodeOut = static_cast(exitCode); } } CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); return success; } class Win32Library : public Library { public: Win32Library(const char *libraryName, SearchType searchType) { char buffer[MAX_PATH]; int ret = snprintf(buffer, MAX_PATH, "%s.%s", libraryName, GetSharedLibraryExtension()); if (ret > 0 && ret < MAX_PATH) { switch (searchType) { case SearchType::ApplicationDir: mModule = LoadLibraryA(buffer); break; case SearchType::SystemDir: mModule = LoadLibraryExA(buffer, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); break; } } } ~Win32Library() override { if (mModule) { FreeLibrary(mModule); } } void *getSymbol(const char *symbolName) override { if (!mModule) { return nullptr; } return reinterpret_cast(GetProcAddress(mModule, symbolName)); } void *getNative() const override { return reinterpret_cast(mModule); } private: HMODULE mModule = nullptr; }; Library *OpenSharedLibrary(const char *libraryName, SearchType searchType) { return new Win32Library(libraryName, searchType); } bool IsDirectory(const char *filename) { WIN32_FILE_ATTRIBUTE_DATA fileInformation; BOOL result = GetFileAttributesExA(filename, GetFileExInfoStandard, &fileInformation); if (result) { DWORD attribs = fileInformation.dwFileAttributes; return (attribs != INVALID_FILE_ATTRIBUTES) && ((attribs & FILE_ATTRIBUTE_DIRECTORY) > 0); } return false; } bool IsDebuggerAttached() { return !!::IsDebuggerPresent(); } void BreakDebugger() { __debugbreak(); } } // namespace angle