From 55b43077d754defd52b8d0c56f021a3e9ed136a0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 10 Oct 2018 15:47:33 +0200 Subject: [PATCH] libslic3r utils: Add a Windows-proof file move function --- xs/src/libslic3r/Utils.hpp | 5 +++ xs/src/libslic3r/utils.cpp | 63 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index fd63d412a..9334573e8 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -44,6 +44,11 @@ extern local_encoded_string encode_path(const char *src); extern std::string decode_path(const char *src); extern std::string normalize_utf8_nfc(const char *src); +// Safely rename a file even if the target exists. +// On Windows, the file explorer (or anti-virus or whatever else) often locks the file +// for a short while, so the file may not be movable. Retry while we see recoverable errors. +extern int rename_file(const std::string &from, const std::string &to); + // File path / name / extension splitting utilities, working with UTF-8, // to be published to Perl. namespace PerlUtils { diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 4ca4f69fd..45a39bbad 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -149,6 +150,68 @@ const std::string& data_dir() return g_data_dir; } +// borrowed from LLVM lib/Support/Windows/Path.inc +int rename_file(const std::string &from, const std::string &to) +{ + int ec = 0; + +#ifdef _WIN32 + + // Convert to utf-16. + std::wstring wide_from = boost::nowide::widen(from); + std::wstring wide_to = boost::nowide::widen(to); + + // Retry while we see recoverable errors. + // System scanners (eg. indexer) might open the source file when it is written + // and closed. + bool TryReplace = true; + + // This loop may take more than 2000 x 1ms to finish. + for (int i = 0; i < 2000; ++ i) { + if (i > 0) + // Sleep 1ms + ::Sleep(1); + if (TryReplace) { + // Try ReplaceFile first, as it is able to associate a new data stream + // with the destination even if the destination file is currently open. + if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL)) + return 0; + DWORD ReplaceError = ::GetLastError(); + ec = -1; // ReplaceError + // If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or + // ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW(). + if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT || + ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) { + TryReplace = false; + continue; + } + // If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry + // using ReplaceFileW(). + if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED) + continue; + // We get ERROR_FILE_NOT_FOUND if the destination file is missing. + // MoveFileEx can handle this case. + if (ReplaceError != ERROR_ACCESS_DENIED && ReplaceError != ERROR_FILE_NOT_FOUND && ReplaceError != ERROR_SHARING_VIOLATION) + break; + } + if (::MoveFileExW(wide_from.c_str(), wide_to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) + return 0; + DWORD MoveError = ::GetLastError(); + ec = -1; // MoveError + if (MoveError != ERROR_ACCESS_DENIED && MoveError != ERROR_SHARING_VIOLATION) + break; + } + +#else + + boost::nowide::remove(to.c_str()); + ec = boost::nowide::rename(from.c_str(), to.c_str()); + +#endif + + return ec; +} + } // namespace Slic3r #include