diff --git a/spec/std/dir_spec.cr b/spec/std/dir_spec.cr index 4fc2cf95d135..60e20cea581f 100644 --- a/spec/std/dir_spec.cr +++ b/spec/std/dir_spec.cr @@ -68,15 +68,14 @@ describe "Dir" do end end - # TODO: do we even want this? - pending_win32 "tests empty? on a directory path to a file" do + it "tests empty? on a directory path to a file" do expect_raises(File::Error, "Error opening directory: '#{datapath("dir", "f1.txt", "/").inspect_unquoted}'") do Dir.empty?(datapath("dir", "f1.txt", "/")) end end end - pending_win32 "tests info on existing directory" do + it "tests info on existing directory" do Dir.open(datapath) do |dir| info = dir.info info.directory?.should be_true diff --git a/src/crystal/system/win32/dir.cr b/src/crystal/system/win32/dir.cr index 661fc39ae08c..7e426be33207 100644 --- a/src/crystal/system/win32/dir.cr +++ b/src/crystal/system/win32/dir.cr @@ -6,10 +6,11 @@ require "c/processenv" module Crystal::System::Dir private class DirHandle - property handle : LibC::HANDLE + property iter_handle : LibC::HANDLE + property file_handle : LibC::HANDLE = LibC::INVALID_HANDLE_VALUE getter query : LibC::LPWSTR - def initialize(@handle, @query) + def initialize(@iter_handle, @query) end end @@ -22,11 +23,11 @@ module Crystal::System::Dir end def self.next_entry(dir : DirHandle, path : String) : Entry? - if dir.handle == LibC::INVALID_HANDLE_VALUE + if dir.iter_handle == LibC::INVALID_HANDLE_VALUE # Directory is at start, use FindFirstFile handle = LibC.FindFirstFileW(dir.query, out data) if handle != LibC::INVALID_HANDLE_VALUE - dir.handle = handle + dir.iter_handle = handle data_to_entry(data) else error = WinError.value @@ -38,7 +39,7 @@ module Crystal::System::Dir end else # Use FindNextFile - if LibC.FindNextFileW(dir.handle, out data_) != 0 + if LibC.FindNextFileW(dir.iter_handle, out data_) != 0 data_to_entry(data_) else error = WinError.value @@ -64,24 +65,34 @@ module Crystal::System::Dir end def self.info(dir : DirHandle, path) : ::File::Info - if dir.handle == LibC::INVALID_HANDLE_VALUE - handle = LibC.FindFirstFileW(dir.query, out data) - begin - Crystal::System::FileDescriptor.system_info handle, LibC::FILE_TYPE_DISK - ensure - close(handle, path) rescue nil + if dir.file_handle == LibC::INVALID_HANDLE_VALUE + handle = LibC.CreateFileW( + System.to_wstr(path), + LibC::FILE_READ_ATTRIBUTES, + LibC::FILE_SHARE_READ | LibC::FILE_SHARE_WRITE | LibC::FILE_SHARE_DELETE, + nil, + LibC::OPEN_EXISTING, + LibC::FILE_FLAG_BACKUP_SEMANTICS, + LibC::HANDLE.null, + ) + + if handle == LibC::INVALID_HANDLE_VALUE + raise ::File::Error.from_winerror("Unable to get directory info", file: path) end - else - Crystal::System::FileDescriptor.system_info dir.handle, LibC::FILE_TYPE_DISK + + dir.file_handle = handle end + + Crystal::System::FileDescriptor.system_info dir.file_handle, LibC::FILE_TYPE_DISK end def self.close(dir : DirHandle, path : String) : Nil - close(dir.handle, path) - dir.handle = LibC::INVALID_HANDLE_VALUE + close_iter(dir.iter_handle, path) + close_file(dir.file_handle, path) + dir.iter_handle = dir.file_handle = LibC::INVALID_HANDLE_VALUE end - def self.close(handle : LibC::HANDLE, path : String) : Nil + private def self.close_iter(handle : LibC::HANDLE, path : String) : Nil return if handle == LibC::INVALID_HANDLE_VALUE if LibC.FindClose(handle) == 0 @@ -89,6 +100,14 @@ module Crystal::System::Dir end end + private def self.close_file(handle : LibC::HANDLE, path : String) : Nil + return if handle == LibC::INVALID_HANDLE_VALUE + + if LibC.CloseHandle(handle) == 0 + raise ::File::Error.from_winerror("CloseHandle", file: path) + end + end + def self.current : String System.retry_wstr_buffer do |buffer, small_buf| len = LibC.GetCurrentDirectoryW(buffer.size, buffer)