diff --git a/CMakeLists.txt b/CMakeLists.txt index 13dbf66..949715a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,9 +15,9 @@ endif() set (BOOST_COMPONENTS system date_time - program_options thread - filesystem) + filesystem +) if (BUILD_TESTING) list(APPEND BOOST_COMPONENTS unit_test_framework) endif (BUILD_TESTING) diff --git a/include/dir_monitor/windows/basic_dir_monitor_service.hpp b/include/dir_monitor/windows/basic_dir_monitor_service.hpp index 5cabdd6..d573302 100755 --- a/include/dir_monitor/windows/basic_dir_monitor_service.hpp +++ b/include/dir_monitor/windows/basic_dir_monitor_service.hpp @@ -33,27 +33,18 @@ namespace helper { } } - inline std::string to_utf8(WCHAR *filename, DWORD length) + inline std::wstring to_utf16(const std::string& filename) { - int size = WideCharToMultiByte(CP_UTF8, 0, filename, length, NULL, 0, NULL, NULL); + static std::wstring empty; + if (filename.empty()) + return empty; - helper::throw_system_error_if(!size, "boost::asio::basic_dir_monitor_service::to_utf8: WideCharToMultiByte failed"); + int size = ::MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, NULL, 0); + size = size > 0 ? size - 1 : 0; + std::vector buffer(size); + helper::throw_system_error_if(::MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, &buffer[0], size), "boost::asio::basic_dir_monitor_service::to_utf16: MultiByteToWideChar failed"); - char buffer[1024]; - std::unique_ptr dynbuffer; - if (size > sizeof(buffer)) - { - dynbuffer = std::make_unique(size); - size = WideCharToMultiByte(CP_UTF8, 0, filename, length, dynbuffer.get(), size, NULL, NULL); - } - else - { - size = WideCharToMultiByte(CP_UTF8, 0, filename, length, buffer, sizeof(buffer), NULL, NULL); - } - - helper::throw_system_error_if(!size, "boost::asio::basic_dir_monitor_service::to_utf8: WideCharToMultiByte failed"); - - return dynbuffer.get() ? std::string(dynbuffer.get(), size) : std::string(buffer, size); + return std::wstring(buffer.begin(), buffer.end()); } } @@ -64,7 +55,7 @@ class basic_dir_monitor_service public: struct completion_key { - completion_key(HANDLE h, const std::string &d, std::shared_ptr &i) + completion_key(HANDLE h, const std::wstring &d, std::shared_ptr &i) : handle(h), dirname(d), impl(i) @@ -73,7 +64,7 @@ class basic_dir_monitor_service } HANDLE handle; - std::string dirname; + std::wstring dirname; std::weak_ptr impl; char buffer[1024]; OVERLAPPED overlapped; @@ -110,17 +101,18 @@ class basic_dir_monitor_service void add_directory(implementation_type &impl, const std::string &dirname) { - if (!boost::filesystem::is_directory(dirname)) + std::wstring wdirname = helper::to_utf16(dirname); + if (!boost::filesystem::is_directory(wdirname)) throw std::invalid_argument("boost::asio::basic_dir_monitor_service::add_directory: " + dirname + " is not a valid directory entry"); - HANDLE handle = CreateFileA(dirname.c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); + HANDLE handle = CreateFileW(wdirname.c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); helper::throw_system_error_if(INVALID_HANDLE_VALUE == handle, "boost::asio::basic_dir_monitor_service::add_directory: CreateFile failed"); // a smart pointer is used to free allocated memory automatically in case of // exceptions while handing over a completion key to the I/O completion port module, // the ownership has to be *released* at the end of scope so as not to free the memory // the OS kernel is using. - auto ck_holder = std::make_unique(handle, dirname, impl); + auto ck_holder = std::make_unique(handle, wdirname, impl); helper::throw_system_error_if(NULL == CreateIoCompletionPort(ck_holder->handle, iocp_, reinterpret_cast(ck_holder.get()), 0), "boost::asio::basic_dir_monitor_service::add_directory: CreateIoCompletionPort failed"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f2411cf..7e74af4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,7 +4,7 @@ function(create_test NAME) cmake_parse_arguments(CT "NO_CTEST" "" "LIBS" ${ARGN}) add_executable(test_${NAME} test_${NAME}.cpp directory.hpp check_paths.hpp) target_link_libraries(test_${NAME} ${CT_LIBS} ${Boost_LIBRARIES} dir_monitor) - target_compile_definitions(test_${NAME} PRIVATE ${BOOST_TEST_LINK_MODE} BOOST_ASIO_ENABLE_HANDLER_TRACKING) + target_compile_definitions(test_${NAME} PRIVATE ${BOOST_TEST_LINK_MODE} BOOST_ASIO_ENABLE_HANDLER_TRACKING BOOST_ASIO_STANDALONE BOOST_ALL_NO_LIB BOOST_ASIO_HAS_BOOST_BIND) install(TARGETS test_${NAME} RUNTIME DESTINATION tests/unittests) if (NOT CT_NO_CTEST) @@ -24,6 +24,11 @@ if (CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g") endif() +if (MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DUNICODE /D_UNICODE /D_WINDOWS /W4 /D_SCL_SECURE_NO_WARNINGS /MP /Z7") + add_definitions( -D_WIN32_WINNT=0x0600 ) # setting minimum supported target version to Vista +endif() + set(BOOST_TEST_LINK_MODE "BOOST_TEST_DYN_LINK") if (BOOST_TEST_STATIC_LINK) set(BOOST_TEST_LINK_MODE "BOOST_TEST_NO_LIB") diff --git a/tests/directory.hpp b/tests/directory.hpp index 9b2721a..ce7c967 100755 --- a/tests/directory.hpp +++ b/tests/directory.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #define TEST_DIR1 "A95A7AE9-D5F5-459a-AB8D-28649FB1F3F4" @@ -20,7 +21,11 @@ class directory { public: directory(const char *name) +#ifdef _WIN32 + : full_path(boost::filesystem::initial_path() / boost::locale::conv::utf_to_utf(name)) +#else : full_path(boost::filesystem::initial_path() / name) +#endif { boost::filesystem::create_directory(full_path); BOOST_REQUIRE(boost::filesystem::is_directory(full_path)); @@ -48,11 +53,17 @@ class directory { boost::filesystem::current_path(full_path); BOOST_REQUIRE(boost::filesystem::equivalent(full_path, boost::filesystem::current_path())); - std::ofstream ofs(file); - BOOST_REQUIRE(boost::filesystem::exists(file)); + +#ifdef _WIN32 + boost::filesystem::path file_path(boost::locale::conv::utf_to_utf(file)); +#else + boost::filesystem::path file_path(file); +#endif + boost::filesystem::ofstream ofs(file_path); + BOOST_REQUIRE(boost::filesystem::exists(file_path)); boost::filesystem::current_path(boost::filesystem::initial_path()); BOOST_REQUIRE(boost::filesystem::equivalent(boost::filesystem::current_path(), boost::filesystem::initial_path())); - return full_path / file; + return full_path / file_path; } boost::filesystem::path rename_file(const char *from, const char *to) diff --git a/tests/test_sync.cpp b/tests/test_sync.cpp index 85b4362..445ff1e 100755 --- a/tests/test_sync.cpp +++ b/tests/test_sync.cpp @@ -118,3 +118,20 @@ BOOST_AUTO_TEST_CASE(dir_monitor_destruction) dir.create_file(TEST_FILE1); } + +BOOST_AUTO_TEST_CASE(non_ascii_paths) +{ + char utf8DirName[] = "\xe6\x97\xa5\xe6\x9c\xac\xe5\x9b\xbd"; // 日本国 + char utf8FileName[] = "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9"".txt"; // العربية.txt + + directory dir(utf8DirName); + + boost::asio::dir_monitor dm(io_service); + dm.add_directory(utf8DirName); + + auto test_file = dir.create_file(utf8FileName); + + boost::asio::dir_monitor_event ev = dm.monitor(); + BOOST_CHECK_THE_SAME_PATHS_RELATIVE(ev.path, test_file); + BOOST_CHECK_EQUAL(ev.type, boost::asio::dir_monitor_event::added); +}