Win32, file descriptors and rb_io_check_writable()

D

Daniel Berger

Hi all,

Windows 2000/XP
VC++ 6.0 or 7.0
Ruby 1.8.2 (Installer RC9)

I'm trying to write an extension for the win32-file package.
Specifically, I want to write a wrapper for CreateFile() called
File.nopen (for 'native open'). I thought this would be nice for two
reasons. First, it would give you finer control over HANDLE creation
on the Win32 platform. Second, it would allow you to open
directories, since you cannot currently call File.open on a directory
on Windows.

So, the API would look like this:

File.nopen(file, access, share, creation, flags)

The underlying C code looks like this:

static VALUE file_nopen(int argc, VALUE *argv, VALUE klass){
HANDLE h;
int rv;
VALUE args[1];
VALUE rbFile, rbAccess, rbShare, rbCreation, rbFlags;
DWORD dwAccess = FILE_ALL_ACCESS; // I tried various flags here
DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
DWORD dwCreation = OPEN_EXISTING;
DWORD dwFlags = FILE_ATTRIBUTE_NORMAL;

rb_scan_args(argc,argv,"14",&rbFile,&rbAccess,&rbShare,&rbCreation,&rbFlags);

// Override default values with user supplied values
if(Qnil != rbAccess)
dwAccess = (DWORD)NUM2UINT(rbAccess);

if(Qnil != rbShare)
dwShare = (DWORD)NUM2UINT(rbShare);

if(Qnil != rbCreation)
dwCreation = (DWORD)NUM2UINT(rbCreation);

if(Qnil != rbFlags)
dwFlags = (DWORD)NUM2UINT(rbFlags);

h = CreateFile(
StringValuePtr(rbFile),
dwAccess,
dwShare,
NULL, // Cannot be inherited
dwCreation,
dwFlags,
NULL // No template
);

if(h == INVALID_HANDLE_VALUE)
rb_raise(cFileError,ErrorDescription(GetLastError()));

// Convert HANDLE to file descriptor
args[0] = UINT2NUM(_open_osfhandle((long)h,O_RDWR));

// Return a bonafide File object, based on the file descriptor
return rb_class_new_instance(1,args,rb_cFile);
}

For read operations, this works. I can do this:

fh = File.nopen("C:\\somefile")
p fh.readlines # works fine
fh.close

I can even read from a directory:

fh = File.nopen("C:\\TESTDIR",nil,nil,nil,File::BACKUP_SEMANTICS)
fh.close

But, I cannot write to the filehandle:

fh = File.nopen("C:\\somefile")
fh.puts "Hello"

test.rb:13:in `write': not opened for writing (IOError)
from test.rb:13:in `puts'
from test.rb:13

That error appears to be coming from the rb_io_check_writable()
function, which in turn is checking the mode of the OpenFile struct
defined in rubyio.h.

How do I deal with this? Am I doing something wrong? Or would it
take a modification of io.c for this to work? If so, what should be
done?

Regards,

Dan
 
P

Park Heesob

Hi,
Hi all,

Windows 2000/XP
VC++ 6.0 or 7.0
Ruby 1.8.2 (Installer RC9)

I'm trying to write an extension for the win32-file package.
Specifically, I want to write a wrapper for CreateFile() called
File.nopen (for 'native open'). I thought this would be nice for two
reasons. First, it would give you finer control over HANDLE creation
on the Win32 platform. Second, it would allow you to open
directories, since you cannot currently call File.open on a directory
on Windows.

So, the API would look like this:

File.nopen(file, access, share, creation, flags)

The underlying C code looks like this:
...
For read operations, this works. I can do this:

fh = File.nopen("C:\\somefile")
p fh.readlines # works fine
fh.close


But, I cannot write to the filehandle:

fh = File.nopen("C:\\somefile")
fh.puts "Hello"

test.rb:13:in `write': not opened for writing (IOError)
from test.rb:13:in `puts'
from test.rb:13

That error appears to be coming from the rb_io_check_writable()
function, which in turn is checking the mode of the OpenFile struct
defined in rubyio.h.

How do I deal with this? Am I doing something wrong? Or would it
take a modification of io.c for this to work? If so, what should be
done?
Following code will work:

#define OpenFile WINAPI_OpenFile
#include <windows.h>
...
#undef OpenFile
#include "ruby.h"
#include "rubyio.h"
...
static VALUE file_nopen(int argc, VALUE *argv, VALUE klass){
HANDLE h;
int rv;
VALUE args[1];
VALUE rbFile, rbAccess, rbShare, rbCreation, rbFlags;
DWORD dwAccess = FILE_ALL_ACCESS; // I tried various flags here
DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
DWORD dwCreation = OPEN_EXISTING;
DWORD dwFlags = FILE_ATTRIBUTE_NORMAL;

rb_scan_args(argc,argv,"14",&rbFile,&rbAccess,&rbShare,&rbCreation,&rbFlags);

// Override default values with user supplied values
if(Qnil != rbAccess)
dwAccess = (DWORD)NUM2UINT(rbAccess);

if(Qnil != rbShare)
dwShare = (DWORD)NUM2UINT(rbShare);

if(Qnil != rbCreation)
dwCreation = (DWORD)NUM2UINT(rbCreation);

if(Qnil != rbFlags)
dwFlags = (DWORD)NUM2UINT(rbFlags);

h = CreateFile(
StringValuePtr(rbFile),
dwAccess,
dwShare,
NULL, // Cannot be inherited
dwCreation,
dwFlags,
NULL // No template
);

if(h == INVALID_HANDLE_VALUE)
rb_raise(cFileError,ErrorDescription(GetLastError()));

// Convert HANDLE to file descriptor
args[0] = UINT2NUM(_open_osfhandle((long)h,O_RDWR));

RFILE(self)->fptr->mode = rb_io_mode_flags("w+"); // mode string correspond to dwAccess
RFILE(self)->fptr->f = rb_fdopen(NUM2INT(args[0]),"w+"); // mode string correspond to dwAccess

// Return a bonafide File object, based on the file descriptor
return rb_class_new_instance(1,args,rb_cFile);
}
Regards,

Dan

Regards,

Park Heesob
 
N

nobu.nokada

Hi,

At Sat, 13 Nov 2004 08:13:26 +0900,
Daniel Berger wrote in [ruby-talk:120101]:
But, I cannot write to the filehandle:

fh = File.nopen("C:\\somefile")
fh.puts "Hello"

test.rb:13:in `write': not opened for writing (IOError)
from test.rb:13:in `puts'
from test.rb:13

That error appears to be coming from the rb_io_check_writable()
function, which in turn is checking the mode of the OpenFile struct
defined in rubyio.h.

How do I deal with this? Am I doing something wrong? Or would it
take a modification of io.c for this to work? If so, what should be
done?

File#initialize accepts the mode in Fixnum.
// Convert HANDLE to file descriptor
args[0] = UINT2NUM(_open_osfhandle((long)h,O_RDWR));
switch (dwAccess & (GENERIC_READ|GENERIC_WRITE)) {
case 0:
args[1] = INT2FIX(0);
break;
case GENERIC_READ:
args[1] = INT2FIX(O_RDONLY);
break;
case GENERIC_WRITE:
args[1] = INT2FIX(O_WRONLY);
break;
default:
args[1] = INT2FIX(O_RDWR);
break;
}
// Return a bonafide File object, based on the file descriptor
return rb_class_new_instance(2,args,klass);
 
D

Daniel Berger

Hi,

At Sat, 13 Nov 2004 08:13:26 +0900,
Daniel Berger wrote in [ruby-talk:120101]:
But, I cannot write to the filehandle:

fh = File.nopen("C:\\somefile")
fh.puts "Hello"

test.rb:13:in `write': not opened for writing (IOError)
from test.rb:13:in `puts'
from test.rb:13

That error appears to be coming from the rb_io_check_writable()
function, which in turn is checking the mode of the OpenFile struct
defined in rubyio.h.

How do I deal with this? Am I doing something wrong? Or would it
take a modification of io.c for this to work? If so, what should be
done?

File#initialize accepts the mode in Fixnum.
// Convert HANDLE to file descriptor
args[0] = UINT2NUM(_open_osfhandle((long)h,O_RDWR));
switch (dwAccess & (GENERIC_READ|GENERIC_WRITE)) {
case 0:
args[1] = INT2FIX(0);
break;
case GENERIC_READ:
args[1] = INT2FIX(O_RDONLY);
break;
case GENERIC_WRITE:
args[1] = INT2FIX(O_WRONLY);
break;
default:
args[1] = INT2FIX(O_RDWR);
break;
}
// Return a bonafide File object, based on the file descriptor
return rb_class_new_instance(2,args,klass);

This code does eliminate that error. However, it seems that the write
operations still don't work properly. I can call fh.puts "hello" and,
although no error is raised, nothing is actually written to the file.

Any thoughts?

Dan
 
D

Daniel Berger

Park Heesob said:
Following code will work:

#define OpenFile WINAPI_OpenFile
#include <windows.h>
..
#undef OpenFile
#include "ruby.h"
#include "rubyio.h"
..
// Convert HANDLE to file descriptor
args[0] = UINT2NUM(_open_osfhandle((long)h,O_RDWR));

RFILE(self)->fptr->mode = rb_io_mode_flags("w+"); // mode string correspond to dwAccess
RFcorrespond to dwAccess

// Return a bonafide File object, based on the file descriptor
return rb_class_new_instance(1,args,rb_cFile);
}
Regards,

Park Heesob

Thank you very much Park. There is one curious difference I noticed
with the write operations. It seems that there is a line ending issue
between a file descriptor opened with File.open vs File.nopen. You
can see this easily enough by doing something like this:

fh1 = File.open("test1.txt","w+")
fh1.print "hello\nworld\n"
fh1.close

Open this up with notepad - looks fine. Now try this:

fh2 = File.nopen("test2.txt",nil,nil,File::OPEN_ALWAYS)
fh2.print "hello\nworld\n"
fh2.close

Opened with notepad, you'll see that the line endings aren't the same
(although wordpad handles them properly). I guess I should find an
octal dump tool for windows to verify. Hopefully, you'll see what I
mean.

Regards,

Dan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,139
Latest member
JamaalCald
Top