mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Add Windows Implementation Libraries
This commit is contained in:
908
Externals/WIL/include/wil/filesystem.h
vendored
Normal file
908
Externals/WIL/include/wil/filesystem.h
vendored
Normal file
@ -0,0 +1,908 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_FILESYSTEM_INCLUDED
|
||||
#define __WIL_FILESYSTEM_INCLUDED
|
||||
|
||||
#ifdef _KERNEL_MODE
|
||||
#error This header is not supported in kernel-mode.
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
#include <combaseapi.h> // Needed for CoTaskMemFree() used in output of some helpers.
|
||||
#include <winbase.h> // LocalAlloc
|
||||
#include <PathCch.h>
|
||||
#include "result.h"
|
||||
#include "win32_helpers.h"
|
||||
#include "resource.h"
|
||||
|
||||
namespace wil
|
||||
{
|
||||
//! Determines if a path is an extended length path that can be used to access paths longer than MAX_PATH.
|
||||
inline bool is_extended_length_path(_In_ PCWSTR path)
|
||||
{
|
||||
return wcsncmp(path, L"\\\\?\\", 4) == 0;
|
||||
}
|
||||
|
||||
//! Find the last segment of a path. Matches the behavior of shlwapi!PathFindFileNameW()
|
||||
//! note, does not support streams being specified like PathFindFileNameW(), is that a bug or a feature?
|
||||
inline PCWSTR find_last_path_segment(_In_ PCWSTR path)
|
||||
{
|
||||
auto const pathLength = wcslen(path);
|
||||
// If there is a trailing slash ignore that in the search.
|
||||
auto const limitedLength = ((pathLength > 0) && (path[pathLength - 1] == L'\\')) ? (pathLength - 1) : pathLength;
|
||||
|
||||
PCWSTR result;
|
||||
auto const offset = FindStringOrdinal(FIND_FROMEND, path, static_cast<int>(limitedLength), L"\\", 1, TRUE);
|
||||
if (offset == -1)
|
||||
{
|
||||
result = path + pathLength; // null terminator
|
||||
}
|
||||
else
|
||||
{
|
||||
result = path + offset + 1; // just past the slash
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Determine if the file name is one of the special "." or ".." names.
|
||||
inline bool path_is_dot_or_dotdot(_In_ PCWSTR fileName)
|
||||
{
|
||||
return ((fileName[0] == L'.') &&
|
||||
((fileName[1] == L'\0') || ((fileName[1] == L'.') && (fileName[2] == L'\0'))));
|
||||
}
|
||||
|
||||
//! Returns the drive number, if it has one. Returns true if there is a drive number, false otherwise. Supports regular and extended length paths.
|
||||
inline bool try_get_drive_letter_number(_In_ PCWSTR path, _Out_ int* driveNumber)
|
||||
{
|
||||
if (path[0] == L'\\' && path[1] == L'\\' && path[2] == L'?' && path[3] == L'\\')
|
||||
{
|
||||
path += 4;
|
||||
}
|
||||
if (path[0] && (path[1] == L':'))
|
||||
{
|
||||
if ((path[0] >= L'a') && (path[0] <= L'z'))
|
||||
{
|
||||
*driveNumber = path[0] - L'a';
|
||||
return true;
|
||||
}
|
||||
else if ((path[0] >= L'A') && (path[0] <= L'Z'))
|
||||
{
|
||||
*driveNumber = path[0] - L'A';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*driveNumber = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
|
||||
// PathCch.h APIs are only in desktop API for now.
|
||||
|
||||
// Compute the substring in the input value that is the parent folder path.
|
||||
// returns:
|
||||
// true + parentPathLength - path has a parent starting at the beginning path and of parentPathLength length.
|
||||
// false, no parent path, the input is a root path.
|
||||
inline bool try_get_parent_path_range(_In_ PCWSTR path, _Out_ size_t* parentPathLength)
|
||||
{
|
||||
*parentPathLength = 0;
|
||||
bool hasParent = false;
|
||||
PCWSTR rootEnd;
|
||||
if (SUCCEEDED(PathCchSkipRoot(path, &rootEnd)) && (*rootEnd != L'\0'))
|
||||
{
|
||||
auto const lastSegment = find_last_path_segment(path);
|
||||
*parentPathLength = lastSegment - path;
|
||||
hasParent = (*parentPathLength != 0);
|
||||
}
|
||||
return hasParent;
|
||||
}
|
||||
|
||||
// Creates directories for the specified path, creating parent paths
|
||||
// as needed.
|
||||
inline HRESULT CreateDirectoryDeepNoThrow(PCWSTR path) WI_NOEXCEPT
|
||||
{
|
||||
if (::CreateDirectoryW(path, nullptr) == FALSE)
|
||||
{
|
||||
DWORD const lastError = ::GetLastError();
|
||||
if (lastError == ERROR_PATH_NOT_FOUND)
|
||||
{
|
||||
size_t parentLength;
|
||||
if (try_get_parent_path_range(path, &parentLength))
|
||||
{
|
||||
wistd::unique_ptr<wchar_t[]> parent(new (std::nothrow) wchar_t[parentLength + 1]);
|
||||
RETURN_IF_NULL_ALLOC(parent.get());
|
||||
RETURN_IF_FAILED(StringCchCopyNW(parent.get(), parentLength + 1, path, parentLength));
|
||||
CreateDirectoryDeepNoThrow(parent.get()); // recurs
|
||||
}
|
||||
RETURN_IF_WIN32_BOOL_FALSE(::CreateDirectoryW(path, nullptr));
|
||||
}
|
||||
else if (lastError != ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
RETURN_WIN32(lastError);
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
inline void CreateDirectoryDeep(PCWSTR path)
|
||||
{
|
||||
THROW_IF_FAILED(CreateDirectoryDeepNoThrow(path));
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
//! A strongly typed version of the Win32 API GetFullPathNameW.
|
||||
//! Return a path in an allocated buffer for handling long paths.
|
||||
//! Optionally return the pointer to the file name part.
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT GetFullPathNameW(PCWSTR file, string_type& path, _Outptr_opt_ PCWSTR* filePart = nullptr)
|
||||
{
|
||||
wil::assign_null_to_opt_param(filePart);
|
||||
const auto hr = AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
|
||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
|
||||
{
|
||||
// Note that GetFullPathNameW() is not limited to MAX_PATH
|
||||
// but it does take a fixed size buffer.
|
||||
*valueLengthNeededWithNull = ::GetFullPathNameW(file, static_cast<DWORD>(valueLength), value, nullptr);
|
||||
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
|
||||
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
|
||||
if (*valueLengthNeededWithNull < valueLength)
|
||||
{
|
||||
(*valueLengthNeededWithNull)++; // it fit, account for the null
|
||||
}
|
||||
return S_OK;
|
||||
});
|
||||
if (SUCCEEDED(hr) && filePart)
|
||||
{
|
||||
*filePart = wil::find_last_path_segment(details::string_maker<string_type>::get(path));
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
//! A strongly typed version of the Win32 API of GetFullPathNameW.
|
||||
//! Return a path in an allocated buffer for handling long paths.
|
||||
//! Optionally return the pointer to the file name part.
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type GetFullPathNameW(PCWSTR file, _Outptr_opt_ PCWSTR* filePart = nullptr)
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED((GetFullPathNameW<string_type, stackBufferLength>(file, result, filePart)));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
enum class RemoveDirectoryOptions
|
||||
{
|
||||
None = 0,
|
||||
KeepRootDirectory = 0x1
|
||||
};
|
||||
DEFINE_ENUM_FLAG_OPERATORS(RemoveDirectoryOptions);
|
||||
|
||||
// If inputPath is a non-normalized name be sure to pass an extended length form to ensure
|
||||
// it can be addressed and deleted.
|
||||
inline HRESULT RemoveDirectoryRecursiveNoThrow(PCWSTR inputPath, RemoveDirectoryOptions options = RemoveDirectoryOptions::None) WI_NOEXCEPT
|
||||
{
|
||||
wil::unique_hlocal_string path;
|
||||
PATHCCH_OPTIONS combineOptions = PATHCCH_NONE;
|
||||
|
||||
if (is_extended_length_path(inputPath))
|
||||
{
|
||||
path = wil::make_hlocal_string_nothrow(inputPath);
|
||||
RETURN_IF_NULL_ALLOC(path);
|
||||
// PathAllocCombine will convert extended length paths to regular paths if shorter than
|
||||
// MAX_PATH, avoid that behavior to provide access inputPath with non-normalized names.
|
||||
combineOptions = PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For regular paths normalize here to get consistent results when searching and deleting.
|
||||
RETURN_IF_FAILED(wil::GetFullPathNameW(inputPath, path));
|
||||
combineOptions = PATHCCH_ALLOW_LONG_PATHS;
|
||||
}
|
||||
|
||||
wil::unique_hlocal_string searchPath;
|
||||
RETURN_IF_FAILED(::PathAllocCombine(path.get(), L"*", combineOptions, &searchPath));
|
||||
|
||||
WIN32_FIND_DATAW fd;
|
||||
wil::unique_hfind findHandle(::FindFirstFileW(searchPath.get(), &fd));
|
||||
RETURN_LAST_ERROR_IF(!findHandle);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// skip "." and ".."
|
||||
if (!(WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) && path_is_dot_or_dotdot(fd.cFileName)))
|
||||
{
|
||||
// Need to form an extended length path to provide the ability to delete paths > MAX_PATH
|
||||
// and files with non-normalized names (dots or spaces at the end).
|
||||
wil::unique_hlocal_string pathToDelete;
|
||||
RETURN_IF_FAILED(::PathAllocCombine(path.get(), fd.cFileName,
|
||||
PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &pathToDelete));
|
||||
if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
RemoveDirectoryOptions localOptions = options;
|
||||
RETURN_IF_FAILED(RemoveDirectoryRecursiveNoThrow(pathToDelete.get(), WI_ClearFlag(localOptions, RemoveDirectoryOptions::KeepRootDirectory)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// note: if pathToDelete is read-only this will fail, consider adding
|
||||
// RemoveDirectoryOptions::RemoveReadOnly to enable this behavior.
|
||||
RETURN_IF_WIN32_BOOL_FALSE(::DeleteFileW(pathToDelete.get()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!::FindNextFileW(findHandle.get(), &fd))
|
||||
{
|
||||
auto const err = ::GetLastError();
|
||||
if (err == ERROR_NO_MORE_FILES)
|
||||
{
|
||||
break;
|
||||
}
|
||||
RETURN_WIN32(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (WI_IsFlagClear(options, RemoveDirectoryOptions::KeepRootDirectory))
|
||||
{
|
||||
RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(path.get()));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
inline void RemoveDirectoryRecursive(PCWSTR path, RemoveDirectoryOptions options = RemoveDirectoryOptions::None)
|
||||
{
|
||||
THROW_IF_FAILED(RemoveDirectoryRecursiveNoThrow(path, options));
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
// Range based for that supports Win32 structures that use NextEntryOffset as the basis of traversing
|
||||
// a result buffer that contains data. This is used in the following FileIO calls:
|
||||
// FileStreamInfo, FILE_STREAM_INFO
|
||||
// FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO
|
||||
// FileFullDirectoryInfo, FILE_FULL_DIR_INFO
|
||||
// FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO
|
||||
// ReadDirectoryChangesW, FILE_NOTIFY_INFORMATION
|
||||
|
||||
template <typename T>
|
||||
struct next_entry_offset_iterator
|
||||
{
|
||||
// Fulfill std::iterator_traits requirements
|
||||
using difference_type = ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = const T*;
|
||||
using reference = const T&;
|
||||
#ifdef _XUTILITY_
|
||||
using iterator_category = ::std::forward_iterator_tag;
|
||||
#endif
|
||||
|
||||
next_entry_offset_iterator(T *iterable = __nullptr) : current_(iterable) {}
|
||||
|
||||
// range based for requires operator!=, operator++ and operator* to do its work
|
||||
// on the type returned from begin() and end(), provide those here.
|
||||
bool operator!=(const next_entry_offset_iterator& other) const { return current_ != other.current_; }
|
||||
|
||||
next_entry_offset_iterator& operator++()
|
||||
{
|
||||
current_ = (current_->NextEntryOffset != 0) ?
|
||||
reinterpret_cast<T *>(reinterpret_cast<unsigned char*>(current_) + current_->NextEntryOffset) :
|
||||
__nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
next_entry_offset_iterator operator++(int)
|
||||
{
|
||||
auto copy = *this;
|
||||
++(*this);
|
||||
return copy;
|
||||
}
|
||||
|
||||
reference operator*() const WI_NOEXCEPT { return *current_; }
|
||||
pointer operator->() const WI_NOEXCEPT { return current_; }
|
||||
|
||||
next_entry_offset_iterator<T> begin() { return *this; }
|
||||
next_entry_offset_iterator<T> end() { return next_entry_offset_iterator<T>(); }
|
||||
|
||||
T* current_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
next_entry_offset_iterator<T> create_next_entry_offset_iterator(T* p)
|
||||
{
|
||||
return next_entry_offset_iterator<T>(p);
|
||||
}
|
||||
|
||||
#pragma region Folder Watcher
|
||||
// Example use in exception based code:
|
||||
// auto watcher = wil::make_folder_watcher(folder.Path().c_str(), true, wil::allChangeEvents, []()
|
||||
// {
|
||||
// // respond
|
||||
// });
|
||||
//
|
||||
// Example use in result code based code:
|
||||
// wil::unique_folder_watcher watcher;
|
||||
// THROW_IF_FAILED(watcher.create(folder, true, wil::allChangeEvents, []()
|
||||
// {
|
||||
// // respond
|
||||
// }));
|
||||
|
||||
enum class FolderChangeEvent : DWORD
|
||||
{
|
||||
ChangesLost = 0, // requies special handling, reset state as events were lost
|
||||
Added = FILE_ACTION_ADDED,
|
||||
Removed = FILE_ACTION_REMOVED,
|
||||
Modified = FILE_ACTION_MODIFIED,
|
||||
RenameOldName = FILE_ACTION_RENAMED_OLD_NAME,
|
||||
RenameNewName = FILE_ACTION_RENAMED_NEW_NAME,
|
||||
};
|
||||
|
||||
enum class FolderChangeEvents : DWORD
|
||||
{
|
||||
None = 0,
|
||||
FileName = FILE_NOTIFY_CHANGE_FILE_NAME,
|
||||
DirectoryName = FILE_NOTIFY_CHANGE_DIR_NAME,
|
||||
Attributes = FILE_NOTIFY_CHANGE_ATTRIBUTES,
|
||||
FileSize = FILE_NOTIFY_CHANGE_SIZE,
|
||||
LastWriteTime = FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||
Security = FILE_NOTIFY_CHANGE_SECURITY,
|
||||
All = FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME |
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
FILE_NOTIFY_CHANGE_SIZE |
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE |
|
||||
FILE_NOTIFY_CHANGE_SECURITY
|
||||
};
|
||||
DEFINE_ENUM_FLAG_OPERATORS(FolderChangeEvents);
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
struct folder_watcher_state
|
||||
{
|
||||
folder_watcher_state(wistd::function<void()> &&callback) : m_callback(wistd::move(callback))
|
||||
{
|
||||
}
|
||||
wistd::function<void()> m_callback;
|
||||
// Order is important, need to close the thread pool wait before the change handle.
|
||||
unique_hfind_change m_findChangeHandle;
|
||||
unique_threadpool_wait m_threadPoolWait;
|
||||
};
|
||||
|
||||
inline void delete_folder_watcher_state(_In_opt_ folder_watcher_state *storage) { delete storage; }
|
||||
|
||||
typedef resource_policy<folder_watcher_state *, decltype(&details::delete_folder_watcher_state),
|
||||
details::delete_folder_watcher_state, details::pointer_access_none> folder_watcher_state_resource_policy;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
template <typename storage_t, typename err_policy = err_exception_policy>
|
||||
class folder_watcher_t : public storage_t
|
||||
{
|
||||
public:
|
||||
// forward all base class constructors...
|
||||
template <typename... args_t>
|
||||
explicit folder_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
|
||||
|
||||
// HRESULT or void error handling...
|
||||
typedef typename err_policy::result result;
|
||||
|
||||
// Exception-based constructors
|
||||
folder_watcher_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
|
||||
{
|
||||
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
|
||||
create(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
}
|
||||
|
||||
result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
|
||||
{
|
||||
return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback)));
|
||||
}
|
||||
private:
|
||||
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
|
||||
// to __stdcall
|
||||
static void __stdcall callback(PTP_CALLBACK_INSTANCE /*Instance*/, void *context, TP_WAIT *pThreadPoolWait, TP_WAIT_RESULT /*result*/)
|
||||
{
|
||||
auto watcherState = static_cast<details::folder_watcher_state *>(context);
|
||||
watcherState->m_callback();
|
||||
|
||||
// Rearm the wait. Should not fail with valid parameters.
|
||||
FindNextChangeNotification(watcherState->m_findChangeHandle.get());
|
||||
SetThreadpoolWait(pThreadPoolWait, watcherState->m_findChangeHandle.get(), __nullptr);
|
||||
}
|
||||
|
||||
// This function exists to avoid template expansion of this code based on err_policy.
|
||||
HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
|
||||
{
|
||||
wistd::unique_ptr<details::folder_watcher_state> watcherState(new(std::nothrow) details::folder_watcher_state(wistd::move(callback)));
|
||||
RETURN_IF_NULL_ALLOC(watcherState);
|
||||
|
||||
watcherState->m_findChangeHandle.reset(FindFirstChangeNotificationW(folderToWatch, isRecursive, static_cast<DWORD>(filter)));
|
||||
RETURN_LAST_ERROR_IF(!watcherState->m_findChangeHandle);
|
||||
|
||||
watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(&folder_watcher_t::callback, watcherState.get(), __nullptr));
|
||||
RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait);
|
||||
this->reset(watcherState.release()); // no more failures after this, pass ownership
|
||||
SetThreadpoolWait(this->get()->m_threadPoolWait.get(), this->get()->m_findChangeHandle.get(), __nullptr);
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
typedef unique_any_t<folder_watcher_t<details::unique_storage<details::folder_watcher_state_resource_policy>, err_returncode_policy>> unique_folder_watcher_nothrow;
|
||||
|
||||
inline unique_folder_watcher_nothrow make_folder_watcher_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback) WI_NOEXCEPT
|
||||
{
|
||||
unique_folder_watcher_nothrow watcher;
|
||||
watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
return watcher; // caller must test for success using if (watcher)
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
typedef unique_any_t<folder_watcher_t<details::unique_storage<details::folder_watcher_state_resource_policy>, err_exception_policy>> unique_folder_watcher;
|
||||
|
||||
inline unique_folder_watcher make_folder_watcher(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
|
||||
{
|
||||
return unique_folder_watcher(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Folder Reader
|
||||
|
||||
// Example use for throwing:
|
||||
// auto reader = wil::make_folder_change_reader(folder.Path().c_str(), true, wil::FolderChangeEvents::All,
|
||||
// [](wil::FolderChangeEvent event, PCWSTR fileName)
|
||||
// {
|
||||
// switch (event)
|
||||
// {
|
||||
// case wil::FolderChangeEvent::ChangesLost: break;
|
||||
// case wil::FolderChangeEvent::Added: break;
|
||||
// case wil::FolderChangeEvent::Removed: break;
|
||||
// case wil::FolderChangeEvent::Modified: break;
|
||||
// case wil::FolderChangeEvent::RenamedOldName: break;
|
||||
// case wil::FolderChangeEvent::RenamedNewName: break;
|
||||
// });
|
||||
//
|
||||
// Example use for non throwing:
|
||||
// wil::unique_folder_change_reader_nothrow reader;
|
||||
// THROW_IF_FAILED(reader.create(folder, true, wil::FolderChangeEvents::All,
|
||||
// [](wil::FolderChangeEvent event, PCWSTR fileName)
|
||||
// {
|
||||
// // handle changes
|
||||
// }));
|
||||
//
|
||||
|
||||
// @cond
|
||||
namespace details
|
||||
{
|
||||
struct folder_change_reader_state
|
||||
{
|
||||
folder_change_reader_state(bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||
: m_callback(wistd::move(callback)), m_isRecursive(isRecursive), m_filter(filter)
|
||||
{
|
||||
}
|
||||
|
||||
~folder_change_reader_state()
|
||||
{
|
||||
if (m_tpIo != __nullptr)
|
||||
{
|
||||
TP_IO *tpIo = m_tpIo;
|
||||
|
||||
// Indicate to the callback function that this object is being torn
|
||||
// down.
|
||||
|
||||
{
|
||||
auto autoLock = m_cancelLock.lock_exclusive();
|
||||
m_tpIo = __nullptr;
|
||||
}
|
||||
|
||||
// Cancel IO to terminate the file system monitoring operation.
|
||||
|
||||
if (m_folderHandle)
|
||||
{
|
||||
CancelIoEx(m_folderHandle.get(), &m_overlapped);
|
||||
}
|
||||
|
||||
// Wait for callbacks to complete.
|
||||
//
|
||||
// N.B. This is a blocking call and must not be made within a
|
||||
// callback or within a lock which is taken inside the
|
||||
// callback.
|
||||
|
||||
WaitForThreadpoolIoCallbacks(tpIo, TRUE);
|
||||
CloseThreadpoolIo(tpIo);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT StartIo()
|
||||
{
|
||||
// Unfortunately we have to handle ref-counting of IOs on behalf of the
|
||||
// thread pool.
|
||||
StartThreadpoolIo(m_tpIo);
|
||||
HRESULT hr = ReadDirectoryChangesW(m_folderHandle.get(), m_readBuffer, sizeof(m_readBuffer),
|
||||
m_isRecursive, static_cast<DWORD>(m_filter), __nullptr, &m_overlapped, __nullptr) ?
|
||||
S_OK : HRESULT_FROM_WIN32(::GetLastError());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// This operation does not have the usual semantic of returning
|
||||
// ERROR_IO_PENDING.
|
||||
// WI_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING));
|
||||
|
||||
// If the operation failed for whatever reason, ensure the TP
|
||||
// ref counts are accurate.
|
||||
|
||||
CancelThreadpoolIo(m_tpIo);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
// void (wil::FolderChangeEvent event, PCWSTR fileName)
|
||||
wistd::function<void(FolderChangeEvent, PCWSTR)> m_callback;
|
||||
unique_handle m_folderHandle;
|
||||
BOOL m_isRecursive = FALSE;
|
||||
FolderChangeEvents m_filter = FolderChangeEvents::None;
|
||||
OVERLAPPED m_overlapped{};
|
||||
TP_IO *m_tpIo = __nullptr;
|
||||
srwlock m_cancelLock;
|
||||
char m_readBuffer[4096]; // Consider alternative buffer sizes. With 512 byte buffer i was not able to observe overflow.
|
||||
};
|
||||
|
||||
inline void delete_folder_change_reader_state(_In_opt_ folder_change_reader_state *storage) { delete storage; }
|
||||
|
||||
typedef resource_policy<folder_change_reader_state *, decltype(&details::delete_folder_change_reader_state),
|
||||
details::delete_folder_change_reader_state, details::pointer_access_none> folder_change_reader_state_resource_policy;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
template <typename storage_t, typename err_policy = err_exception_policy>
|
||||
class folder_change_reader_t : public storage_t
|
||||
{
|
||||
public:
|
||||
// forward all base class constructors...
|
||||
template <typename... args_t>
|
||||
explicit folder_change_reader_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
|
||||
|
||||
// HRESULT or void error handling...
|
||||
typedef typename err_policy::result result;
|
||||
|
||||
// Exception-based constructors
|
||||
folder_change_reader_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||
{
|
||||
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
|
||||
create(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
}
|
||||
|
||||
result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||
{
|
||||
return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback)));
|
||||
}
|
||||
|
||||
wil::unique_hfile& folder_handle() { return this->get()->m_folderHandle; }
|
||||
|
||||
private:
|
||||
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
|
||||
// to __stdcall
|
||||
static void __stdcall callback(PTP_CALLBACK_INSTANCE /* Instance */, void *context, void * /*overlapped*/,
|
||||
ULONG result, ULONG_PTR /* BytesTransferred */, TP_IO * /* Io */)
|
||||
{
|
||||
auto readerState = static_cast<details::folder_change_reader_state *>(context);
|
||||
// WI_ASSERT(overlapped == &readerState->m_overlapped);
|
||||
|
||||
bool requeue = true;
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
for (auto const& info : create_next_entry_offset_iterator(reinterpret_cast<FILE_NOTIFY_INFORMATION *>(readerState->m_readBuffer)))
|
||||
{
|
||||
wchar_t realtiveFileName[MAX_PATH];
|
||||
StringCchCopyNW(realtiveFileName, ARRAYSIZE(realtiveFileName), info.FileName, info.FileNameLength / sizeof(info.FileName[0]));
|
||||
|
||||
readerState->m_callback(static_cast<FolderChangeEvent>(info.Action), realtiveFileName);
|
||||
}
|
||||
}
|
||||
else if (result == ERROR_NOTIFY_ENUM_DIR)
|
||||
{
|
||||
readerState->m_callback(FolderChangeEvent::ChangesLost, __nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
requeue = false;
|
||||
}
|
||||
|
||||
if (requeue)
|
||||
{
|
||||
// If the lock is held non-shared or the TP IO is nullptr, this
|
||||
// structure is being torn down. Otherwise, monitor for further
|
||||
// changes.
|
||||
auto autoLock = readerState->m_cancelLock.try_lock_shared();
|
||||
if (autoLock && readerState->m_tpIo)
|
||||
{
|
||||
readerState->StartIo(); // ignoring failure here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function exists to avoid template expansion of this code based on err_policy.
|
||||
HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||
{
|
||||
wistd::unique_ptr<details::folder_change_reader_state> readerState(new(std::nothrow) details::folder_change_reader_state(
|
||||
isRecursive, filter, wistd::move(callback)));
|
||||
RETURN_IF_NULL_ALLOC(readerState);
|
||||
|
||||
readerState->m_folderHandle.reset(CreateFileW(folderToWatch,
|
||||
FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
|
||||
__nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, __nullptr));
|
||||
RETURN_LAST_ERROR_IF(!readerState->m_folderHandle);
|
||||
|
||||
readerState->m_tpIo = CreateThreadpoolIo(readerState->m_folderHandle.get(), &folder_change_reader_t::callback, readerState.get(), __nullptr);
|
||||
RETURN_LAST_ERROR_IF_NULL(readerState->m_tpIo);
|
||||
RETURN_IF_FAILED(readerState->StartIo());
|
||||
this->reset(readerState.release());
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
typedef unique_any_t<folder_change_reader_t<details::unique_storage<details::folder_change_reader_state_resource_policy>, err_returncode_policy>> unique_folder_change_reader_nothrow;
|
||||
|
||||
inline unique_folder_change_reader_nothrow make_folder_change_reader_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter,
|
||||
wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback) WI_NOEXCEPT
|
||||
{
|
||||
unique_folder_change_reader_nothrow watcher;
|
||||
watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
return watcher; // caller must test for success using if (watcher)
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
typedef unique_any_t<folder_change_reader_t<details::unique_storage<details::folder_change_reader_state_resource_policy>, err_exception_policy>> unique_folder_change_reader;
|
||||
|
||||
inline unique_folder_change_reader make_folder_change_reader(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter,
|
||||
wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||
{
|
||||
return unique_folder_change_reader(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
#pragma endregion
|
||||
|
||||
//! Dos and VolumeGuid paths are always extended length paths with the \\?\ prefix.
|
||||
enum class VolumePrefix
|
||||
{
|
||||
Dos = VOLUME_NAME_DOS, // Extended Dos Device path form, e.g. \\?\C:\Users\Chris\AppData\Local\Temp\wil8C31.tmp
|
||||
VolumeGuid = VOLUME_NAME_GUID, // \\?\Volume{588fb606-b95b-4eae-b3cb-1e49861aaf18}\Users\Chris\AppData\Local\Temp\wil8C31.tmp
|
||||
// The following are special paths which can't be used with Win32 APIs, but are useful in other scenarios.
|
||||
None = VOLUME_NAME_NONE, // Path without the volume root, e.g. \Users\Chris\AppData\Local\Temp\wil8C31.tmp
|
||||
NtObjectName = VOLUME_NAME_NT, // Unique name used by Object Manager, e.g. \Device\HarddiskVolume4\Users\Chris\AppData\Local\Temp\wil8C31.tmp
|
||||
};
|
||||
enum class PathOptions
|
||||
{
|
||||
Normalized = FILE_NAME_NORMALIZED,
|
||||
Opened = FILE_NAME_OPENED,
|
||||
};
|
||||
DEFINE_ENUM_FLAG_OPERATORS(PathOptions);
|
||||
|
||||
/** A strongly typed version of the Win32 API GetFinalPathNameByHandleW.
|
||||
Get the full path name in different forms
|
||||
Use this instead + VolumePrefix::None instead of GetFileInformationByHandleEx(FileNameInfo) to
|
||||
get that path form. */
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT GetFinalPathNameByHandleW(HANDLE fileHandle, string_type& path,
|
||||
wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized)
|
||||
{
|
||||
return AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
|
||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
|
||||
{
|
||||
*valueLengthNeededWithNull = ::GetFinalPathNameByHandleW(fileHandle, value, static_cast<DWORD>(valueLength),
|
||||
static_cast<DWORD>(volumePrefix) | static_cast<DWORD>(options));
|
||||
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
|
||||
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
|
||||
if (*valueLengthNeededWithNull < valueLength)
|
||||
{
|
||||
(*valueLengthNeededWithNull)++; // it fit, account for the null
|
||||
}
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
/** A strongly typed version of the Win32 API GetFinalPathNameByHandleW.
|
||||
Get the full path name in different forms. Use this + VolumePrefix::None
|
||||
instead of GetFileInformationByHandleEx(FileNameInfo) to get that path form. */
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type GetFinalPathNameByHandleW(HANDLE fileHandle,
|
||||
wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized)
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED((GetFinalPathNameByHandleW<string_type, stackBufferLength>(fileHandle, result, volumePrefix, options)));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! A strongly typed version of the Win32 API of GetCurrentDirectoryW.
|
||||
//! Return a path in an allocated buffer for handling long paths.
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT GetCurrentDirectoryW(string_type& path)
|
||||
{
|
||||
return AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
|
||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
|
||||
{
|
||||
*valueLengthNeededWithNull = ::GetCurrentDirectoryW(static_cast<DWORD>(valueLength), value);
|
||||
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
|
||||
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
|
||||
if (*valueLengthNeededWithNull < valueLength)
|
||||
{
|
||||
(*valueLengthNeededWithNull)++; // it fit, account for the null
|
||||
}
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
//! A strongly typed version of the Win32 API of GetCurrentDirectoryW.
|
||||
//! Return a path in an allocated buffer for handling long paths.
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type GetCurrentDirectoryW()
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED((GetCurrentDirectoryW<string_type, stackBufferLength>(result)));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: add support for these and other similar APIs.
|
||||
// GetShortPathNameW()
|
||||
// GetLongPathNameW()
|
||||
// GetWindowsDirectory()
|
||||
// GetTempDirectory()
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template <FILE_INFO_BY_HANDLE_CLASS infoClass> struct MapInfoClassToInfoStruct; // failure to map is a usage error caught by the compiler
|
||||
#define MAP_INFOCLASS_TO_STRUCT(InfoClass, InfoStruct, IsFixed, Extra) \
|
||||
template <> struct MapInfoClassToInfoStruct<InfoClass> \
|
||||
{ \
|
||||
typedef InfoStruct type; \
|
||||
static bool const isFixed = IsFixed; \
|
||||
static size_t const extraSize = Extra; \
|
||||
};
|
||||
|
||||
MAP_INFOCLASS_TO_STRUCT(FileBasicInfo, FILE_BASIC_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileStandardInfo, FILE_STANDARD_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileNameInfo, FILE_NAME_INFO, false, 32);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileRenameInfo, FILE_RENAME_INFO, false, 32);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileDispositionInfo, FILE_DISPOSITION_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileAllocationInfo, FILE_ALLOCATION_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileEndOfFileInfo, FILE_END_OF_FILE_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileStreamInfo, FILE_STREAM_INFO, false, 32);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileCompressionInfo, FILE_COMPRESSION_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileAttributeTagInfo, FILE_ATTRIBUTE_TAG_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO, false, 4096);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryRestartInfo, FILE_ID_BOTH_DIR_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIoPriorityHintInfo, FILE_IO_PRIORITY_HINT_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileRemoteProtocolInfo, FILE_REMOTE_PROTOCOL_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryInfo, FILE_FULL_DIR_INFO, false, 4096);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryRestartInfo, FILE_FULL_DIR_INFO, true, 0);
|
||||
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||
MAP_INFOCLASS_TO_STRUCT(FileStorageInfo, FILE_STORAGE_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileAlignmentInfo, FILE_ALIGNMENT_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIdInfo, FILE_ID_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO, false, 4096);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryRestartInfo, FILE_ID_EXTD_DIR_INFO, true, 0);
|
||||
#endif
|
||||
|
||||
// Type unsafe version used in the implementation to avoid template bloat.
|
||||
inline HRESULT GetFileInfo(HANDLE fileHandle, FILE_INFO_BY_HANDLE_CLASS infoClass, size_t allocationSize,
|
||||
_Outptr_result_nullonfailure_ void **result)
|
||||
{
|
||||
*result = nullptr;
|
||||
|
||||
wistd::unique_ptr<char[]> resultHolder(new(std::nothrow) char[allocationSize]);
|
||||
RETURN_IF_NULL_ALLOC(resultHolder);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (GetFileInformationByHandleEx(fileHandle, infoClass, resultHolder.get(), static_cast<DWORD>(allocationSize)))
|
||||
{
|
||||
*result = resultHolder.release();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD const lastError = ::GetLastError();
|
||||
if (lastError == ERROR_MORE_DATA)
|
||||
{
|
||||
allocationSize *= 2;
|
||||
resultHolder.reset(new(std::nothrow) char[allocationSize]);
|
||||
RETURN_IF_NULL_ALLOC(resultHolder);
|
||||
}
|
||||
else if (lastError == ERROR_NO_MORE_FILES) // for folder enumeration cases
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (lastError == ERROR_INVALID_PARAMETER) // operation not supported by file system
|
||||
{
|
||||
return HRESULT_FROM_WIN32(lastError);
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_WIN32(lastError);
|
||||
}
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/** Get file information for a variable sized structure, returns an HRESULT.
|
||||
~~~
|
||||
wistd::unique_ptr<FILE_NAME_INFO> fileNameInfo;
|
||||
RETURN_IF_FAILED(GetFileInfoNoThrow<FileNameInfo>(fileHandle, fileNameInfo));
|
||||
~~~
|
||||
*/
|
||||
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<!details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
|
||||
HRESULT GetFileInfoNoThrow(HANDLE fileHandle, wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> &result) WI_NOEXCEPT
|
||||
{
|
||||
void *rawResult;
|
||||
HRESULT hr = details::GetFileInfo(fileHandle, infoClass,
|
||||
sizeof(typename details::MapInfoClassToInfoStruct<infoClass>::type) + details::MapInfoClassToInfoStruct<infoClass>::extraSize,
|
||||
&rawResult);
|
||||
result.reset(static_cast<typename details::MapInfoClassToInfoStruct<infoClass>::type*>(rawResult));
|
||||
RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system
|
||||
RETURN_IF_FAILED(hr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/** Get file information for a fixed sized structure, returns an HRESULT.
|
||||
~~~
|
||||
FILE_BASIC_INFO fileBasicInfo;
|
||||
RETURN_IF_FAILED(GetFileInfoNoThrow<FileBasicInfo>(fileHandle, &fileBasicInfo));
|
||||
~~~
|
||||
*/
|
||||
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
|
||||
HRESULT GetFileInfoNoThrow(HANDLE fileHandle, _Out_ typename details::MapInfoClassToInfoStruct<infoClass>::type *result) WI_NOEXCEPT
|
||||
{
|
||||
const HRESULT hr = GetFileInformationByHandleEx(fileHandle, infoClass, result, sizeof(*result)) ?
|
||||
S_OK : HRESULT_FROM_WIN32(::GetLastError());
|
||||
RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system
|
||||
RETURN_IF_FAILED(hr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#ifdef _CPPUNWIND
|
||||
/** Get file information for a fixed sized structure, throws on failure.
|
||||
~~~
|
||||
auto fileBasicInfo = GetFileInfo<FileBasicInfo>(fileHandle);
|
||||
~~~
|
||||
*/
|
||||
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
|
||||
typename details::MapInfoClassToInfoStruct<infoClass>::type GetFileInfo(HANDLE fileHandle)
|
||||
{
|
||||
typename details::MapInfoClassToInfoStruct<infoClass>::type result;
|
||||
THROW_IF_FAILED(GetFileInfoNoThrow<infoClass>(fileHandle, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Get file information for a variable sized structure, throws on failure.
|
||||
~~~
|
||||
auto fileBasicInfo = GetFileInfo<FileNameInfo>(fileHandle);
|
||||
~~~
|
||||
*/
|
||||
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<!details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
|
||||
wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> GetFileInfo(HANDLE fileHandle)
|
||||
{
|
||||
wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> result;
|
||||
THROW_IF_FAILED(GetFileInfoNoThrow<infoClass>(fileHandle, result));
|
||||
return result;
|
||||
}
|
||||
#endif // _CPPUNWIND
|
||||
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
}
|
||||
|
||||
#endif // __WIL_FILESYSTEM_INCLUDED
|
Reference in New Issue
Block a user