[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Win32, file descriptors and rb_io_check_writable

djberg96

11/12/2004 11:09:00 PM

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
4 Answers

Park Heesob

11/13/2004 1:22:00 AM

0


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

nobu.nokada

11/15/2004 1:21:00 AM

0

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);
> }

--
Nobu Nakada


djberg96

11/15/2004 6:22:00 PM

0

nobu.nokada@softhome.net wrote in message news:<200411150121.iAF1LCVW027655@sharui.nakada.niregi.kanuma.tochigi.jp>...
> 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

djberg96

11/15/2004 7:23:00 PM

0

"Park Heesob" <phasis@bcline.com> wrote in message news:<003c01c4c91f$1c2718f0$bcdefea9@2xnm9896kmqn5b9>...
<snip>
> >
> Following code will work:
>
> #define OpenFile WINAPI_OpenFile
> #include <windows.h>
> ..
> #undef OpenFile
> #include "ruby.h"
> #include "rubyio.h"
> ..
<snip>

> // 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