Windows, CreateFile and IO objects

I’m porting some code from Linux to Windows which relies on direct
access to a CD’s block device. This “just works” on Linux if I
File.open("/dev/cdrom"), but I can’t see how to make it work on Windows.

While manually running a dl/imported CreateFile works and gives me a
valid handle, just running File.open("\\.\D:", “r”) crashes the
interpreter (1.9.3-p0), as does IO.sysopen.

Is there a built-in way to turn an open handle into a File object? I
blindly tried IO.for_fd, but that fails and I got no further.

Here’s the code I’ve tried (and yes, there is a CD in the drive :slight_smile: ):

require 'dl/import'
DriveTypes = [:unknown, :no_root_dir,
  :removable, :fixed, :remote, :cdrom, :ramdisk]

module Kernel32
  def self.stdcall(fn); extern fn, :stdcall; end
  extend DL::Importer
  dlload "kernel32.dll"

  stdcall "uint GetDriveType( void * )"
  stdcall "long CreateFile( void *, long, long, void *, long, long,

long )"
stdcall “int CloseHandle( long )”
end

case DriveTypes[Kernel32.GetDriveType( "D:\\" )]
when :cdrom
  handle = Kernel32.CreateFile( "\\\\.\\D:", 0, 3, 0, 3, 128, 0 )
  if handle == -1
    puts "O NOES"
  else
    puts "Ok"
    Kernel32.CloseHandle( handle )
  end
else
  puts "Not a CD"
end

This prints “Ok”, so I know that the handle I’m getting back is at least
valid from Windows’ point of view.


Alex

On 12/19/2011 08:32 AM, Alex Y. wrote:

I’m porting some code from Linux to Windows which relies on direct
access to a CD’s block device. This “just works” on Linux if I
File.open(“/dev/cdrom”), but I can’t see how to make it work on Windows.

While manually running a dl/imported CreateFile works and gives me a
valid handle, just running File.open(“\\.\D:”, “r”) crashes the
interpreter (1.9.3-p0), as does IO.sysopen.

Is there a built-in way to turn an open handle into a File object? I
blindly tried IO.for_fd, but that fails and I got no further.

I’m not expert on this, but my quick look in MSDN seems to indicate that
you need to use DeviceIoControl to handle reads and writes to the device
handle once you get it from CreateFile:

Regular files wouldn’t be accessed using that function, so that’s
probably why Ruby chokes on file objects you create with those special
device paths.

If you want the object you may eventually create in Ruby to work mostly
like an IO object, take a look at my io-like gem:

https://rubygems.org/gems/io-like

Unfortunately, it doesn’t support the Ruby 1.9 IO API yet, but it should
get you the basics without a ton of work. :slight_smile:

-Jeremy

Jeremy B. wrote in post #1037344:

On 12/19/2011 08:32 AM, Alex Y. wrote:

I’m porting some code from Linux to Windows which relies on direct
access to a CD’s block device. This “just works” on Linux if I
File.open(“/dev/cdrom”), but I can’t see how to make it work on Windows.

While manually running a dl/imported CreateFile works and gives me a
valid handle, just running File.open(“\\.\D:”, “r”) crashes the
interpreter (1.9.3-p0), as does IO.sysopen.

Is there a built-in way to turn an open handle into a File object? I
blindly tried IO.for_fd, but that fails and I got no further.

I’m not expert on this, but my quick look in MSDN seems to indicate that
you need to use DeviceIoControl to handle reads and writes to the device
handle once you get it from CreateFile:

Drat. I was afraid of that.

Regular files wouldn’t be accessed using that function, so that’s
probably why Ruby chokes on file objects you create with those special
device paths.

If you want the object you may eventually create in Ruby to work mostly
like an IO object, take a look at my io-like gem:

io-like | RubyGems.org | your community gem host

Unfortunately, it doesn’t support the Ruby 1.9 IO API yet, but it should
get you the basics without a ton of work. :slight_smile:

That looks interesting, I’ll give it a shot. Do you know if this is
likely to break horribly?

class MyFunkyBlockDevice < IO
include IO::Like
end

For some fairly boring reasons I do actually want an IO subclass, but I
only need reading and seeking to work.

Thanks,

Alex

Jeremy B. wrote in post #1037348:

What are the “boring reasons” to require a subclass of IO? If you
control that code, I suggest you tweak the it to make the most of duck
typing.

Unfortunately I don’t - WEBrick::HTTPResponse is the code in question.
There are a couple of places it type-checks for IO specifically.
I’m getting a patch together for bugs.ruby-lang.org and I’m pondering
whether to deploy a monkey-patch in the interim.


Alex

On 12/19/2011 09:18 AM, Alex Y. wrote:

That looks interesting, I’ll give it a shot. Do you know if this is
likely to break horribly?

class MyFunkyBlockDevice < IO
include IO::Like
end

For some fairly boring reasons I do actually want an IO subclass, but I
only need reading and seeking to work.

I don’t think it should break the definition of the class itself or
simple use cases, but it will likely be a bit confusing to consumers of
your class. Since the class you create will effectively be lying about
its type if you follow the example above, other logic in the dependent
code could break if you try to feed it such an object though.

What are the “boring reasons” to require a subclass of IO? If you
control that code, I suggest you tweak the it to make the most of duck
typing.

-Jeremy