#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2013 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library 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 IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_STORAGE_SOURCE 1 #include "WinRTFileApi.h" #if defined(NUCLEX_STORAGE_WINRT) #include "Nuclex/Storage/Helpers/StringHelper.h" #include #if !defined(_MSC_VER) namespace { #endif // ------------------------------------------------------------------------------------------- // /// Automatically closes a search handle upon destruction class SearchHandleScope { /// Initializes a new search handle closer /// Search handle that will be closed on destruction public: SearchHandleScope(HANDLE searchHandle) : searchHandle(searchHandle) {} /// Closes the search handle public: ~SearchHandleScope() { ::FindClose(this->searchHandle); } /// Search handle that will be closed when the instance is destroyed private: HANDLE searchHandle; }; // ------------------------------------------------------------------------------------------- // #if !defined(_MSC_VER) } // anonymous namespace #endif namespace Nuclex { namespace Storage { namespace FileSystem { // ------------------------------------------------------------------------------------------- // void WinRTFileApi::CreateDirectory(const std::string &path) { std::wstring utf16Path = Helpers::StringHelper::WideCharFromUtf8(path); BOOL result = ::CreateDirectoryW(utf16Path.c_str(), nullptr); if(result == FALSE) { throw std::runtime_error("Could not create directory"); } } // ------------------------------------------------------------------------------------------- // void WinRTFileApi::CreateDirectoryRecursively(const std::string &path) { std::wstring utf16Path = Helpers::StringHelper::WideCharFromUtf8(path); createDirectoryRecursively(utf16Path); } // ------------------------------------------------------------------------------------------- // void WinRTFileApi::DeleteDirectory(const std::string &path) { std::wstring utf16Path = Helpers::StringHelper::WideCharFromUtf8(path); recursiveDeleteDirectory(utf16Path.c_str()); } // ------------------------------------------------------------------------------------------- // void WinRTFileApi::DeleteFile(const std::string &path) { std::wstring utf16Path = Helpers::StringHelper::WideCharFromUtf8(path); BOOL result = ::DeleteFileW(utf16Path.c_str()); if(result == FALSE) { throw std::runtime_error("Could not delete file"); } } // ------------------------------------------------------------------------------------------- // DWORD WinRTFileApi::GetFileAttributes(const std::string &path) { WIN32_FILE_ATTRIBUTE_DATA fileAttributeData; if(GetFileAttributesEx(path, fileAttributeData)) { return fileAttributeData.dwFileAttributes; } else { return INVALID_FILE_ATTRIBUTES; } } // ------------------------------------------------------------------------------------------- // bool WinRTFileApi::GetFileAttributesEx( const std::string &path, WIN32_FILE_ATTRIBUTE_DATA &fileAttributeData ) { std::wstring utf16Path = Helpers::StringHelper::WideCharFromUtf8(path); return getFileAttributesEx(utf16Path, fileAttributeData); } // ------------------------------------------------------------------------------------------- // HANDLE WinRTFileApi::OpenFileForReading(const std::string &path) { std::wstring utf16Path = Helpers::StringHelper::WideCharFromUtf8(path); HANDLE fileHandle = ::CreateFile2( utf16Path.c_str(), GENERIC_READ, // desired access FILE_SHARE_READ, // share mode, OPEN_EXISTING, // creation disposition nullptr // extended parameters ); if(fileHandle == INVALID_HANDLE_VALUE) { throw std::runtime_error("Could not open file for reading"); } return fileHandle; } // ------------------------------------------------------------------------------------------- // HANDLE WinRTFileApi::OpenFileForWriting(const std::string &path) { std::wstring utf16Path = Helpers::StringHelper::WideCharFromUtf8(path); CREATEFILE2_EXTENDED_PARAMETERS extendedParameters = {0}; extendedParameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; HANDLE fileHandle = ::CreateFile2( utf16Path.c_str(), GENERIC_READ | GENERIC_WRITE, // desired access 0, // share mode OPEN_ALWAYS, // creation disposition &extendedParameters ); if(fileHandle == INVALID_HANDLE_VALUE) { throw std::runtime_error("Could not open file for writing"); } return fileHandle; } // ------------------------------------------------------------------------------------------- // std::uint64_t WinRTFileApi::SetFilePointer(HANDLE fileHandle, std::uint64_t location) { LARGE_INTEGER desiredLocation; desiredLocation.QuadPart = location; LARGE_INTEGER actualLocation = {0}; BOOL result = ::SetFilePointerEx(fileHandle, desiredLocation, &actualLocation, FILE_BEGIN); if(result == FALSE) { throw std::runtime_error("Could not move the file pointer"); } return actualLocation.QuadPart; } // ------------------------------------------------------------------------------------------- // std::uint64_t WinRTFileApi::GetFileSize(HANDLE fileHandle) { FILE_STANDARD_INFO standardInfo; BOOL result = ::GetFileInformationByHandleEx( fileHandle, FileStandardInfo, &standardInfo, sizeof(standardInfo) ); if(result == FALSE) { throw std::runtime_error("Could not query file attributes"); } return static_cast(standardInfo.EndOfFile.QuadPart); } // ------------------------------------------------------------------------------------------- // std::size_t WinRTFileApi::Read(HANDLE fileHandle, void *buffer, std::size_t count) { DWORD desiredCount = static_cast(count); DWORD actualCount = 0; // Perform the actual read through the Windows API BOOL result = ::ReadFile(fileHandle, buffer, desiredCount, &actualCount, nullptr); if(result == FALSE) { throw std::runtime_error("Could not read data from file"); } return actualCount; } // ------------------------------------------------------------------------------------------- // std::size_t WinRTFileApi::Write(HANDLE fileHandle, const void *buffer, std::size_t count) { DWORD desiredCount = static_cast(count); DWORD actualCount = 0; // Perform the actual write through the Windows API BOOL result = ::WriteFile(fileHandle, buffer, desiredCount, &actualCount, nullptr); if(result == FALSE) { throw std::runtime_error("Could not write data to file"); } return actualCount; } // ------------------------------------------------------------------------------------------- // void WinRTFileApi::FlushFileBuffers(HANDLE fileHandle) { BOOL result = ::FlushFileBuffers(fileHandle); if(result == FALSE) { throw std::runtime_error("Could not flush file buffers"); } } // ------------------------------------------------------------------------------------------- // void WinRTFileApi::CloseFile(HANDLE fileHandle, bool throwOnError /* = true */) { BOOL result = ::CloseHandle(fileHandle); if(throwOnError && (result == FALSE)) { throw std::runtime_error("Could not close file handle"); } } // ------------------------------------------------------------------------------------------- // HANDLE WinRTFileApi::FindFirstFile( const std::string &searchMask, WIN32_FIND_DATAW &findData ) { std::wstring utf16SearchMask = Helpers::StringHelper::WideCharFromUtf8(searchMask); HANDLE searchHandle = ::FindFirstFileExW( utf16SearchMask.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, nullptr, 0 ); if(searchHandle == INVALID_HANDLE_VALUE) { DWORD lastError = ::GetLastError(); if(lastError != ERROR_FILE_NOT_FOUND) { // or ERROR_NO_MORE_FILES, ERROR_NOT_FOUND? throw std::runtime_error("Could not start directory enumeration"); } } return searchHandle; } // ------------------------------------------------------------------------------------------- // bool WinRTFileApi::FindNextFile(HANDLE searchHandle, WIN32_FIND_DATAW &findData) { BOOL result = ::FindNextFileW(searchHandle, &findData); if(result == FALSE) { DWORD lastError = ::GetLastError(); if(lastError != ERROR_NO_MORE_FILES) { throw std::runtime_error("Error enumerating directory"); } return false; } else { return true; } } // ------------------------------------------------------------------------------------------- // void WinRTFileApi::CloseSearch(HANDLE searchHandle, bool throwOnError /* = true */) { BOOL result = ::FindClose(searchHandle); if(throwOnError && (result == FALSE)) { throw std::runtime_error("Could not close search handle"); } } // ------------------------------------------------------------------------------------------- // void WinRTFileApi::recursiveDeleteDirectory(const std::wstring &path) { static const std::wstring allFilesMask(L"\\*"); WIN32_FIND_DATAW findData; // First, delete the contents of the directory, recursively for subdirectories std::wstring searchMask = path + allFilesMask; HANDLE searchHandle = ::FindFirstFileExW( searchMask.c_str(), FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, 0 ); if(searchHandle == INVALID_HANDLE_VALUE) { DWORD lastError = ::GetLastError(); if(lastError != ERROR_FILE_NOT_FOUND) { // or ERROR_NO_MORE_FILES, ERROR_NOT_FOUND? throw std::runtime_error("Could not start directory enumeration"); } } // Did this directory have any contents? If so, delete them first if(searchHandle != INVALID_HANDLE_VALUE) { SearchHandleScope scope(searchHandle); for(;;) { // Do not process the obligatory '.' and '..' directories if(findData.cFileName[0] != '.') { bool isDirectory = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) || ((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0); // Subdirectories need to be handled by deleting their contents first std::wstring filePath = path + L'\\' + findData.cFileName; if(isDirectory) { recursiveDeleteDirectory(filePath); } else { BOOL result = ::DeleteFileW(filePath.c_str()); if(result == FALSE) { throw std::runtime_error("Could not delete file"); } } } // Advance to the next file in the directory BOOL result = ::FindNextFileW(searchHandle, &findData); if(result == FALSE) { DWORD lastError = ::GetLastError(); if(lastError != ERROR_NO_MORE_FILES) { throw std::runtime_error("Error enumerating directory"); } break; // All directory contents enumerated and deleted } } // for } // The directory is empty, we can now safely remove it BOOL result = ::RemoveDirectory(path.c_str()); if(result == FALSE) { throw std::runtime_error("Could not remove directory"); } } // ------------------------------------------------------------------------------------------- // void WinRTFileApi::createDirectoryRecursively(const std::wstring &directory) { static const std::wstring separators(L"\\/"); // If the specified directory name doesn't exist, do our thing WIN32_FILE_ATTRIBUTE_DATA fileAttributeData; if(getFileAttributesEx(directory, fileAttributeData)) { bool isDirectoryOrJunction = ((fileAttributeData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) || ((fileAttributeData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0); if(!isDirectoryOrJunction) { throw std::runtime_error( "Could not create directory because a file with the same name exists" ); } } else { // Specified directory name already exists as a file or directory // Recursively do it all again for the parent directory, if any std::size_t slashIndex = directory.find_last_of(separators); if(slashIndex != std::wstring::npos) { createDirectoryRecursively(directory.substr(0, slashIndex)); } // Create the last directory on the path (the recursive calls will have taken // care of the parent directories by now) BOOL result = ::CreateDirectoryW(directory.c_str(), nullptr); if(result == FALSE) { throw std::runtime_error("Could not create directory"); } } } // ------------------------------------------------------------------------------------------- // bool WinRTFileApi::getFileAttributesEx( const std::wstring &path, WIN32_FILE_ATTRIBUTE_DATA &fileAttributeData ) { BOOL result = ::GetFileAttributesExW( path.c_str(), GetFileExInfoStandard, &fileAttributeData ); if(result == FALSE) { DWORD lastError = ::GetLastError(); if(lastError == ERROR_FILE_NOT_FOUND) { return false; } else { throw std::runtime_error("Could not query file attributes"); } } return true; } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Storage::FileSystem #endif // defined(NUCLEX_STORAGE_WINRT)