Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
38 changes: 15 additions & 23 deletions include/dir_monitor/windows/basic_dir_monitor_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<wchar_t> 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<char[]> dynbuffer;
if (size > sizeof(buffer))
{
dynbuffer = std::make_unique<char[]>(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());
}
}

Expand All @@ -64,7 +55,7 @@ class basic_dir_monitor_service
public:
struct completion_key
{
completion_key(HANDLE h, const std::string &d, std::shared_ptr<DirMonitorImplementation> &i)
completion_key(HANDLE h, const std::wstring &d, std::shared_ptr<DirMonitorImplementation> &i)
: handle(h),
dirname(d),
impl(i)
Expand All @@ -73,7 +64,7 @@ class basic_dir_monitor_service
}

HANDLE handle;
std::string dirname;
std::wstring dirname;
std::weak_ptr<DirMonitorImplementation> impl;
char buffer[1024];
OVERLAPPED overlapped;
Expand Down Expand Up @@ -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<completion_key>(handle, dirname, impl);
auto ck_holder = std::make_unique<completion_key>(handle, wdirname, impl);
helper::throw_system_error_if(NULL == CreateIoCompletionPort(ck_holder->handle, iocp_, reinterpret_cast<ULONG_PTR>(ck_holder.get()), 0),
"boost::asio::basic_dir_monitor_service::add_directory: CreateIoCompletionPort failed");

Expand Down
7 changes: 6 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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")
Expand Down
17 changes: 14 additions & 3 deletions tests/directory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/thread.hpp>
#include <boost/locale.hpp>
#include <fstream>

#define TEST_DIR1 "A95A7AE9-D5F5-459a-AB8D-28649FB1F3F4"
Expand All @@ -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<wchar_t>(name))
#else
: full_path(boost::filesystem::initial_path() / name)
#endif
{
boost::filesystem::create_directory(full_path);
BOOST_REQUIRE(boost::filesystem::is_directory(full_path));
Expand Down Expand Up @@ -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<wchar_t>(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)
Expand Down
17 changes: 17 additions & 0 deletions tests/test_sync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}