aboutsummaryrefslogtreecommitdiff
path: root/libs/usvfs/src/shared/winapi.h
diff options
context:
space:
mode:
Diffstat (limited to 'libs/usvfs/src/shared/winapi.h')
-rw-r--r--libs/usvfs/src/shared/winapi.h610
1 files changed, 610 insertions, 0 deletions
diff --git a/libs/usvfs/src/shared/winapi.h b/libs/usvfs/src/shared/winapi.h
new file mode 100644
index 0000000..f94eff6
--- /dev/null
+++ b/libs/usvfs/src/shared/winapi.h
@@ -0,0 +1,610 @@
+/*
+Userspace Virtual Filesystem
+
+Copyright (C) 2015 Sebastian Herbord. All rights reserved.
+
+This file is part of usvfs.
+
+usvfs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+usvfs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with usvfs. If not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+
+#include "logging.h"
+#include "stringcast.h"
+#include "windows_sane.h"
+#include <ShlObj.h>
+
+#define ALIAS(alias, original) \
+ template <typename... Args> \
+ auto alias(Args&&... args) -> decltype(original(std::forward<Args>(args)...)) \
+ { \
+ return original(std::forward<Args>(args)...); \
+ }
+
+#define ALIAST(alias, original) \
+ template <typename T, typename... Args> \
+ auto alias<T>(Args && ... args) \
+ -> decltype(original<T>(std::forward<Args>(args)...)) \
+ { \
+ return original<T>(std::forward<Args>(args)...); \
+ }
+
+namespace winapi
+{
+
+struct parameter_error : public std::runtime_error
+{
+ parameter_error(const std::string& msg) : runtime_error(msg) {}
+};
+
+} // namespace winapi
+
+namespace winapi::process
+{
+
+/**
+ * @brief result of process creation
+ */
+struct Result
+{
+ Result()
+ {
+ ::ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));
+ ::ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
+ startupInfo.cb = sizeof(STARTUPINFO);
+ }
+
+ Result(Result&& reference)
+ : valid(reference.valid), startupInfo(reference.startupInfo),
+ processInfo(reference.processInfo), errorCode(reference.errorCode)
+ {
+ reference.valid = false;
+ }
+
+ ~Result()
+ {
+ if (valid) {
+ CloseHandle(processInfo.hProcess);
+ CloseHandle(processInfo.hThread);
+ }
+
+ if (stdoutPipe != INVALID_HANDLE_VALUE) {
+ CloseHandle(stdoutPipe);
+ }
+ }
+
+ Result(const Result&) = delete;
+
+ size_t readStdout(std::vector<uint8_t>& buffer, bool& eof)
+ {
+ if (stdoutPipe != INVALID_HANDLE_VALUE) {
+ DWORD read;
+ BOOL res = ReadFile(stdoutPipe, &buffer[0], static_cast<DWORD>(buffer.size()),
+ &read, nullptr);
+ eof = (res == TRUE) && (read == 0);
+ return static_cast<size_t>(read);
+ } else {
+ eof = true;
+ return 0;
+ }
+ }
+
+ bool valid{false};
+ STARTUPINFO startupInfo;
+ PROCESS_INFORMATION processInfo;
+ DWORD errorCode{0UL};
+
+ HANDLE stdoutPipe{INVALID_HANDLE_VALUE};
+};
+
+/**
+ * @brief internal class to handle process creation with named parameters.
+ */
+template <typename CharT>
+class _Create
+{
+public:
+ _Create(const std::basic_string<CharT>& binaryName);
+ _Create(const _Create<CharT>& reference) = delete;
+ _Create<CharT>& operator=(const _Create<CharT>& reference) = delete;
+
+ _Create(_Create<CharT>&& reference)
+ : m_CurrentDirectory(std::move(reference.m_CurrentDirectory)),
+ m_ProcessAttributes(reference.m_ProcessAttributes),
+ m_ThreadAttributes(reference.m_ThreadAttributes),
+ m_InheritHandles(reference.m_InheritHandles),
+ m_CreationFlags(std::move(reference.m_CreationFlags)),
+ m_Executed(reference.m_Executed)
+ {
+ // stringstream should be moveable but it seems it isn't on mingw
+ m_CommandLine << reference.m_CommandLine.rdbuf();
+ }
+
+ /// named parameter "argument". May be called repeatedly. This is
+ /// directly appended to the command line with a separating space. No
+ /// quoting happens
+ template <typename ArgT>
+ _Create& argument(const ArgT& argin)
+ {
+ m_CommandLine << " " << argin;
+ return *this;
+ }
+
+ template <typename ArgT>
+ _Create& arg(const ArgT& argin)
+ {
+ return this->argument(argin);
+ }
+
+ template <typename IterT>
+ _Create& arguments(IterT begin, IterT end)
+ {
+ for (; begin != end; ++begin) {
+ m_CommandLine << " " << *begin;
+ }
+
+ return *this;
+ }
+
+ /// @brief set the working directory for the process
+ _Create& workingDirectory(const std::basic_string<CharT>& path);
+
+ /// @brief set process attributes
+ _Create& processAttributes(SECURITY_ATTRIBUTES* attributes);
+
+ /// @brief set thread attributes
+ _Create& threadAttributes(SECURITY_ATTRIBUTES* attributes);
+
+ /// @brief activate inheriting handles
+ _Create& inheritHandles();
+
+ /// @brief have the process start suspended
+ _Create& suspended();
+
+ /// @brief set the process up to output stout to a pipe which can be
+ /// retrieved through the result object
+ _Create& stdoutPipe();
+
+ /// @brief end the named parameter cascade and create the process
+ Result operator()()
+ {
+ m_CommandLine.seekp(0, std::ios::end);
+ unsigned int length = static_cast<unsigned int>(m_CommandLine.tellp());
+ std::unique_ptr<CharT[]> clBuffer(new CharT[length + 1]);
+ memset(clBuffer.get(), 0, (length + 1) * sizeof(CharT));
+ memcpy(clBuffer.get(), m_CommandLine.str().c_str(), length * sizeof(CharT));
+ Result result;
+
+ if (m_StdoutPipe) {
+ result.stdoutPipe = setupPipe(result.startupInfo.hStdOutput);
+ result.startupInfo.dwFlags |= STARTF_USESTDHANDLES;
+ }
+
+ result.valid =
+ createProcessInt(nullptr, clBuffer.get(), m_ProcessAttributes,
+ m_ThreadAttributes, m_InheritHandles, m_CreationFlags, nullptr,
+ m_CurrentDirectory.length() > 0 ? m_CurrentDirectory.c_str()
+ : nullptr,
+ &result.startupInfo, &result.processInfo) == TRUE;
+
+ if (m_Stdout != INVALID_HANDLE_VALUE) {
+ // got to close the write end of pipes
+ CloseHandle(result.startupInfo.hStdOutput);
+ }
+
+ if (result.valid) {
+ result.errorCode = NOERROR;
+ } else {
+ result.errorCode = GetLastError();
+ }
+ return result;
+ }
+
+private:
+ static BOOL createProcessInt(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
+ SECURITY_ATTRIBUTES* lpProcessAttributes,
+ SECURITY_ATTRIBUTES* lpThreadAttributes,
+ BOOL bInheritHandles, DWORD dwCreationFlags,
+ LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation)
+ {
+ return ::CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes,
+ lpThreadAttributes, bInheritHandles, dwCreationFlags,
+ lpEnvironment, lpCurrentDirectory, lpStartupInfo,
+ lpProcessInformation);
+ }
+
+ static BOOL createProcessInt(LPCSTR lpApplicationName, LPSTR lpCommandLine,
+ SECURITY_ATTRIBUTES* lpProcessAttributes,
+ SECURITY_ATTRIBUTES* lpThreadAttributes,
+ BOOL bInheritHandles, DWORD dwCreationFlags,
+ LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation)
+ {
+ std::wstring executable;
+ if (lpApplicationName != nullptr) {
+ executable = usvfs::shared::string_cast<std::wstring>(lpApplicationName);
+ }
+
+ std::wstring cmdline;
+ if (lpCommandLine != nullptr) {
+ cmdline = usvfs::shared::string_cast<std::wstring>(lpCommandLine);
+ }
+
+ std::wstring cwd;
+ if (lpCurrentDirectory != nullptr) {
+ cwd = usvfs::shared::string_cast<std::wstring>(lpCurrentDirectory);
+ }
+
+ return ::CreateProcessW(lpApplicationName != nullptr ? executable.c_str() : nullptr,
+ lpCommandLine != nullptr ? &cmdline[0] : nullptr,
+ lpProcessAttributes, lpThreadAttributes, bInheritHandles,
+ dwCreationFlags, lpEnvironment,
+ lpCurrentDirectory != nullptr ? cwd.c_str() : nullptr,
+ lpStartupInfo, lpProcessInformation);
+ }
+
+ HANDLE setupPipe(HANDLE& childHandle)
+ {
+ SECURITY_ATTRIBUTES attr;
+ attr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ attr.bInheritHandle = TRUE;
+ attr.lpSecurityDescriptor = nullptr;
+
+ HANDLE pipe[2];
+
+ CreatePipe(&pipe[0], &pipe[1], &attr, 0);
+ SetHandleInformation(pipe[0], HANDLE_FLAG_INHERIT, 0);
+
+ childHandle = pipe[1];
+
+ return pipe[0];
+ }
+
+private:
+ std::basic_stringstream<CharT> m_CommandLine;
+ std::basic_string<CharT> m_CurrentDirectory{};
+ SECURITY_ATTRIBUTES* m_ProcessAttributes{nullptr};
+ SECURITY_ATTRIBUTES* m_ThreadAttributes{nullptr};
+ BOOL m_InheritHandles{false};
+ DWORD m_CreationFlags{0UL};
+ bool m_Executed{false};
+ bool m_StdoutPipe{false};
+
+ HANDLE m_Stdout{INVALID_HANDLE_VALUE};
+};
+
+} // namespace winapi::process
+
+namespace winapi::file
+{
+/**
+ * @brief internal class to handle file creation (opening) with named
+ * parameters.
+ */
+template <typename CharT, DWORD DefaultDisposition>
+class _Create
+{
+public:
+ _Create(const std::basic_string<CharT>& fileName) : m_FileName(fileName) {}
+
+ _Create& access(DWORD desiredAccess)
+ {
+ m_DesiredAccess = desiredAccess;
+ return *this;
+ }
+
+ _Create& share(DWORD shareMode)
+ {
+ m_ShareMode = shareMode;
+ return *this;
+ }
+
+ _Create& createAlways()
+ {
+ m_CreationDisposition = CREATE_ALWAYS;
+ return *this;
+ }
+
+ _Create& openAlways()
+ {
+ m_CreationDisposition = OPEN_ALWAYS;
+ return *this;
+ }
+
+ _Create& security(SECURITY_ATTRIBUTES* attributes)
+ {
+ m_SecurityAttributes = attributes;
+ return *this;
+ }
+
+ _Create& templateFile(HANDLE templateFile)
+ {
+ m_Template = templateFile;
+ return *this;
+ }
+
+ /// @brief end the named parameter cascade and open the file
+ HANDLE operator()()
+ {
+ return callDelegate(
+ std::integral_constant<bool, sizeof(CharT) == sizeof(wchar_t)>());
+ }
+
+private:
+ HANDLE callDelegate(std::true_type)
+ {
+ return ::CreateFileW(m_FileName.c_str(), m_DesiredAccess, m_ShareMode,
+ m_SecurityAttributes, m_CreationDisposition, m_Flags,
+ m_Template);
+ }
+
+ HANDLE callDelegate(std::false_type)
+ {
+ return ::CreateFileA(m_FileName.c_str(), m_DesiredAccess, m_ShareMode,
+ m_SecurityAttributes, m_CreationDisposition, m_Flags,
+ m_Template);
+ }
+
+private:
+ std::basic_string<CharT> m_FileName;
+ DWORD m_DesiredAccess{GENERIC_ALL};
+ DWORD m_ShareMode{0UL};
+ DWORD m_CreationDisposition{DefaultDisposition};
+ DWORD m_Flags{FILE_ATTRIBUTE_NORMAL};
+ HANDLE m_Template{nullptr};
+ SECURITY_ATTRIBUTES* m_SecurityAttributes{nullptr};
+};
+
+} // namespace winapi::file
+
+namespace winapi::ansi
+{
+
+std::string getModuleFileName(HMODULE module, HANDLE process = INVALID_HANDLE_VALUE);
+std::pair<std::string, std::string> getFullPathName(LPCSTR fileName);
+std::string getCurrentDirectory();
+typedef process::_Create<char> createProcess;
+typedef file::_Create<char, CREATE_NEW> createFile;
+typedef file::_Create<char, OPEN_EXISTING> openFile;
+
+} // namespace winapi::ansi
+
+namespace winapi::wide
+{
+
+std::wstring getModuleFileName(HMODULE module, HANDLE process = INVALID_HANDLE_VALUE);
+std::pair<std::wstring, std::wstring> getFullPathName(LPCWSTR fileName);
+std::wstring getCurrentDirectory();
+std::wstring getKnownFolderPath(REFKNOWNFOLDERID folderID);
+
+typedef process::_Create<wchar_t> createProcess;
+typedef file::_Create<wchar_t, CREATE_NEW> createFile;
+typedef file::_Create<wchar_t, OPEN_EXISTING> openFile;
+
+} // namespace winapi::wide
+
+/**
+ * useful convenience functions close to the api
+ */
+namespace winapi::ex
+{
+
+/**
+ * @brief retrieve the address range covering the code section of a module
+ * @param moduleHandle handle to the module
+ * @return start and end address of the code section
+ * @note the code section can only be identified if it has the standardized section name
+ * ".text" Otherwise the whole address range of all sections in the module is returned.
+ * This happens for compressed exectuables for example
+ */
+std::pair<uintptr_t, uintptr_t> getSectionRange(HANDLE moduleHandle);
+
+struct OSVersion
+{
+ DWORD major;
+ DWORD minor;
+ DWORD build;
+ DWORD platformid;
+ DWORD servicpack;
+};
+
+OSVersion getOSVersion();
+
+} // namespace winapi::ex
+
+namespace winapi::ex::ansi
+{
+
+/**
+ * @brief retrieve an error string for a windows error message
+ * @param errorCode the error code to look up. If this is left at the default,
+ * ::GetLastError is used
+ * @return string representation of the error. Currently this is localized
+ */
+std::string errorString(DWORD errorCode = std::numeric_limits<DWORD>::max());
+
+/**
+ * @brief convert filetime to string
+ * @param time time to convert
+ * @return a string representation (currently only supports utc and iso format with
+ * second precision)
+ */
+std::string toString(const FILETIME& time);
+
+/**
+ * @brief find file name in a windows file path
+ * @param path the path to search in
+ * @return the file name of the path or an empty string if the path ends on
+ * a slash
+ * @note this function doesn't access the file system so it doesn't depend
+ * on whether the file actually exists. This also means it can't
+ * determine if a path that doesn't end on a slash refers to a file or
+ * directory
+ * @note the return value is a pointer into the same buffer, no copy is
+ * created
+ */
+LPCSTR GetBaseName(LPCSTR string);
+
+} // namespace winapi::ex::ansi
+
+namespace winapi::ex::wide
+{
+/**
+ * retrieve the name of the binary section containing the specified address
+ * @param address the address to test
+ * @param process the process for which to retrieve the section. If this is
+ * nullptr, the current process is analized.
+ * @return name of the section or "unknown" if no matching section was found
+ */
+std::wstring getSectionName(PVOID address, HANDLE process = nullptr);
+
+/**
+ * @brief test if a file exists
+ * @param path path to check
+ * @param isDirectory (optional) if this isn't null, it will be set to true if the path
+ * specifies a directory, false otherwise
+ * @return true if the file (or directory) exists.
+ */
+bool fileExists(LPCWSTR fileName, bool* isDirectory = nullptr);
+
+/**
+ * @brief retrieve an error string for a windows error message
+ * @param errorCode the error code to look up. If this is left at the default,
+ * ::GetLastError is used
+ * @return string representation of the error. Currently this is localized
+ */
+std::wstring errorString(DWORD errorCode = std::numeric_limits<DWORD>::max());
+
+/**
+ * @brief convert filetime to string
+ * @param time time to convert
+ * @return a string representation (currently only supports utc and iso format with
+ * second precision)
+ */
+std::wstring toString(const FILETIME& time);
+
+/**
+ * @brief find file name in a windows file path
+ * @param path the path to search in
+ * @return the file name of the path or an empty string if the path ends on
+ * a slash
+ * @note this function doesn't access the file system so it doesn't depend
+ * on whether the file actually exists. This also means it can't
+ * determine if a path that doesn't end on a slash refers to a file or
+ * directory
+ * @note the return value is a pointer into the same buffer, no copy is
+ * created
+ */
+LPCWSTR GetBaseName(LPCWSTR path);
+
+/**
+ * @see const-variant of this function
+ */
+LPWSTR GetBaseName(LPWSTR path);
+
+struct FileResult
+{
+ std::wstring fileName;
+ ULONG attributes;
+};
+
+/**
+ * @brief a quick function to find all files in a directory or files following a
+ * pattern. This uses NtQueryDirectoryFile api internally so it should be faster than
+ * the usual FindFirstFile/FindNextFile pattern
+ * @param directoryName name of the directory to search in
+ * @param pattern name pattern that needs to match
+ * @return the list of files found
+ */
+std::vector<FileResult> quickFindFiles(LPCWSTR directoryName, LPCWSTR pattern);
+
+/**
+ * @brief create the specified directory including all intermediate
+ * directories
+ * @param path the path to create
+ * @param securityAttributes the security attributes to use for all created
+ * directories. if this is null (default), the standard attributes
+ * are used
+ * @return true if the directory (and possibly parent directories) were actually created
+ * and false if the directory already existed. Throws exceptions on failure.
+ */
+bool createPath(boost::filesystem::path path,
+ LPSECURITY_ATTRIBUTES securityAttributes = nullptr);
+inline bool createPath(LPCWSTR path, LPSECURITY_ATTRIBUTES securityAttributes = nullptr)
+{
+ return createPath(boost::filesystem::path(path), securityAttributes);
+}
+
+std::wstring getWindowsBuildLab(bool ex = false);
+
+} // namespace winapi::ex::wide
+
+namespace winapi::process
+{
+
+template <typename CharT>
+_Create<CharT>::_Create(const std::basic_string<CharT>& binaryName)
+{
+ if (binaryName.length() > MAX_PATH) {
+ throw parameter_error("executable filename can't be longer than 260 characters");
+ }
+ m_CommandLine << "\"" << binaryName << "\"";
+}
+
+template <typename CharT>
+_Create<CharT>& _Create<CharT>::workingDirectory(const std::basic_string<CharT>& path)
+{
+ m_CurrentDirectory = path;
+ return *this;
+}
+
+template <typename CharT>
+_Create<CharT>& _Create<CharT>::processAttributes(SECURITY_ATTRIBUTES* attributes)
+{
+ m_ProcessAttributes = attributes;
+ return *this;
+}
+
+template <typename CharT>
+_Create<CharT>& _Create<CharT>::threadAttributes(SECURITY_ATTRIBUTES* attributes)
+{
+ m_ThreadAttributes = attributes;
+ return *this;
+}
+
+template <typename CharT>
+_Create<CharT>& _Create<CharT>::inheritHandles()
+{
+ m_InheritHandles = true;
+ return *this;
+}
+
+template <typename CharT>
+_Create<CharT>& _Create<CharT>::suspended()
+{
+ m_CreationFlags |= CREATE_SUSPENDED;
+ return *this;
+}
+
+template <typename CharT>
+_Create<CharT>& _Create<CharT>::stdoutPipe()
+{
+ m_StdoutPipe = true;
+ return *this;
+}
+
+} // namespace winapi::process