[Win32utils-devel] win32-dir, unicode

Daniel Berger djberg96 at gmail.com
Sun May 28 01:24:32 EDT 2006


Heesob Park wrote:
> Hi,
> 2006/5/27, Daniel Berger <djberg96 at gmail.com>:<snip>> Hi Heesob,>> That came through as a single line for some reason...>> Anyway, I modified the approach somewhat.  Instead of adding a 'unicode'> flag, I altered the windows-pr package to wrap methods like this:>> def CreateDirectory(path, attributes)>    if $KCODE != 'NONE'>       CreateDirectoryW.call(path, attributes) != 0>    else>       CreateDirectory.call(path, attributes) != 0>    end> end>> Within dir.rb, I did this:>> if $KCODE != 'NONE'>    to_path = to_path.split("\0\0").first> else>    to_path = to_path.split(0.chr).first> end>> However, after making those changes I got this:>> dir.rb:123:in `create_junction': DeviceIoControl() failed: The data> present in the reparse point buffer is invalid. (RuntimeError).>> Does rdb need to be packed differently?>> BTW, I also tinkered with the idea of using IsTextUnicode():>> def CreateDirectory(path, attributes)>    if IsTextUnicode(path, path.size, 0)>       CreateDirect
oryW.call(path, attributes) != 0>    else>       CreateDirectory.call(path, attributes) != 0>    end> end>> But this didn't seem to handle UCS-2, i.e it worked for> "\x95\x03\xBB\x03\xBB\x03\xAC\x03\xC3\x03\0\0" but not "Ελλάσ".  I> imagine it's also slower.>> Regards,>> Dan> _______________________________________________> win32utils-devel mailing list> win32utils-devel at rubyforge.org> http://rubyforge.org/mailman/listinfo/win32utils-develI have managed to succeed in creating junction with create_junction method.
> First modify multi_to_wide and wide_to_multi to support UTF8 and Ansi string.For english users, UTF8 encoding is sufficient but other languageusers like Korean, both UTF8 and Ansi are required.
>      def multi_to_wide(str)         cp = ($KCODE == 'UTF8') ? CP_UTF8 : CP_ACP         buf = 0.chr * 260         int = MultiByteToWideChar(cp, 0, str, -1, buf, buf.size)         if int > 0            buf[0, int*2]         else            str         end      end
>     def wide_to_multi(str)         cp = ($KCODE == 'UTF8') ? CP_UTF8 : CP_ACP         buf = 0.chr * 260         int = WideCharToMultiByte(cp, 0, str, -1, buf, buf.size, 0, 0)
>          if int > 0            buf[0, int]         else            str         end      end
> Second, modify create_junction method like this:
> 
>   def self.create_junction(to, from)      # Normalize the paths      to.tr!('/', "\\")      from.tr!('/', "\\")
>       to_path    = 0.chr * 260      from_path  = 0.chr * 260      buf_target = 0.chr * 260
>       from = multi_to_wide(from)      to = multi_to_wide(to)      if GetFullPathNameW.call(from, from_path.size, from_path, 0) == 0         raise StandardError, 'GetFullPathName() failed: ' + get_last_error      end
>       if GetFullPathNameW.call(to, to_path.size, to_path, 0) == 0         raise StandardError, 'GetFullPathName() failed: ' + get_last_error      end
> 	to_path = to_path.strip+"\0\0\0"	from_path = from_path.strip+"\0\0\0"
>       # You can create a junction to a directory that already exists, so      # long as it's empty.
>       rv = CreateDirectoryW.call(to_path, 0)      if rv == 0 && GetLastError.call != ERROR_ALREADY_EXISTS         raise StandardError, 'CreateDirectory() failed: ' + get_last_error      end
>       handle = CreateFileW.call(         to_path,         GENERIC_READ | GENERIC_WRITE,         0,         0,         OPEN_EXISTING,         FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,         0      )
>       if handle == INVALID_HANDLE_VALUE         raise StandardError, 'CreateFile() failed: ' + get_last_error      end      buf_target  = multi_to_wide("\\??\\")      length      = buf_target.size-2      buf_target  = buf_target[0,length] + from_path      length      = buf_target.size-2      wide_string = buf_target
>       # REPARSE_JDATA_BUFFER      rdb = [         "0xA0000003L".hex,      # ReparseTag (IO_REPARSE_TAG_MOUNT_POINT)         length + 12,  # ReparseDataLength         0,                      # Reserved         0,                      # SubstituteNameOffset         length,       # SubstituteNameLength         length + 2,   # PrintNameOffset         0,                      # PrintNameLength         wide_string             # PathBuffer      ].pack('LSSSSSSa' + (length + 4).to_s)
>       bytes = [length].pack('L')
> 
>       bool = DeviceIoControl(         handle,         CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED,FILE_ANY_ACCESS),         rdb,         rdb.size,         0,         0,         bytes,         0      )
>       unless bool         error = 'DeviceIoControl() failed: ' + get_last_error
>          RemoveDirectoryW.call(to_path)         CloseHandle(handle)         raise error      end
>       CloseHandle(handle)
>       self   end
> Make test.rb like this:
> from = "C:\\test"to = "Ελλάσ"Dir.mkdir(from) unless File.exists?(from)Dir.create_junction(to, from)
> The file format must be UTF8 DOS format.
> 
> Run with KCODE option like this:
> ruby -Ku test.rb

Thanks Heesob, but I'm getting some weird segfaults with wide character 
functions and buffers over 245 characters.  I posted about this to 
ruby-talk as well.  Here's some sample code that demonstrates the problem:

require 'Win32API'

GetFullPathNameW = Win32API.new('kernel32', 'GetFullPathNameW', 'PLPP', 'L')

path = "C:\\test"
buf  = 0.chr * 260 # 245 or less works ok

if GetFullPathNameW.call(path, buf.size, buf, 0) == 0
    puts "Failed"
    exit
end

p buf.split("\0\0").first # BOOM!

I'm not sure what the significance of 245 or less is.  I can inspect 
'buf', copy and paste it to a separate editor as a string and run ops on 
it with no problem, so I'm very curious as to what's making Ruby segfault.

Anyway, I think I'm not going to worry about Unicode support for the 
initial release of the pure Ruby version for now.

Thanks,

Dan


More information about the win32utils-devel mailing list