[Win32utils-devel] Using callbacks with ReadFileEx

Heesob Park phasis at gmail.com
Wed Sep 26 09:22:13 EDT 2007


Hi,

2007/9/25, Berger, Daniel <Daniel.Berger at qwest.com>:
>
> Hi all,
>
> First, I just realized I'll need to go back and fix some of the
> prototype declarations for windows-pr. The callback parameters will now
> need to be set to 'K'. ReadFileEx is one example of this. For purposes
> of my question, let's assume ReadFileEx has been declared like so:
>
> API.new('ReadFileEx', 'LPLPK', 'B')
>
> Now that we have callback support in win32-api, I thought I'd give
> asynchronous IO a shot. Problem is, I'm not sure how to do it in this
> case. What I would like is for the user to be able to provide a block,
> and have that block called once the read is finished. Here's what I've
> tried so far:
>
> require 'windows/file'
> require 'windows/handle'
> require 'windows/error'
> require 'windows/synchronize'
> require 'win32/event'
>
> module Windows
>   module NIO
>      include Windows::File
>      include Windows::Handle
>      include Windows::Error
>      include Windows::Synchronize
>      extend Windows::File
>      extend Windows::Handle
>      extend Windows::Error
>      extend Windows::Synchronize
>
>      class Error < StandardError; end
>
>      def self.read_async(file, length=nil, offset=0, &block)
>         handle = CreateFile(
>            file,
>            FILE_READ_DATA,
>            FILE_SHARE_READ,
>            0,
>            OPEN_EXISTING,
>            FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
>            0
>         )
>
>         if handle == INVALID_HANDLE_VALUE
>            raise Error, get_last_error
>         end
>
>         func = Win32::API::Callback.new('LLP', 'V'){
>            block.call
>         }
>
>         overlapped = 0.chr * 20
>
>         if offset > 0
>            overlapped[8,4] = [offset].pack('L') # OVERLAPPED.Offset
>         end
>
>         # Ruby's File.size fails for files over 2gb
>         unless length
>            size = [0].pack('Q')
>            GetFileSizeEx(handle, size)
>            length = size.unpack('Q').first
>         end
>
>         buf = 0.chr * length
>
>         bool = ReadFileEx(
>            handle,
>            buf,
>            length,
>            overlapped,
>            func
>         )
>
>         unless bool
>            err = get_last_error()
>            CloseHandle(handle)
>            raise Error, err
>         end
>
>         CloseHandle(handle)
>
>         buf[0,length]
>      end
>   end
> end
>
> if $0 == __FILE__
>   include Win32
>
>   file = 'test.txt'
>   data = NIO.read_async(file){ puts "Hello" }
>   p data
> end
>
> When I run this, however, I get this error:
>
> C:/ruby/lib/ruby/site_ruby/1.8/windows/error.rb:329:in `call': wrong
> number of parameters: expected 1, got 0 (ArgumentEr
> ror)
>        from C:/ruby/lib/ruby/site_ruby/1.8/windows/error.rb:329:in
> `get_last_error'
>        from nio.rb:124:in `read_async'
>        from nio.rb:140
>
> What am I doing wrong here?


There are two problems in your code.

First, "FILE_FLAG_NO_BUFFERING" flag.

MSDN says:

An application must meet certain requirements when working with files opened
with FILE_FLAG_NO_BUFFERING:

File access must begin at byte offsets within the file that are integer
multiples of the volume's sector size. To determine a volume's sector size,
call the GetDiskFreeSpace function.
File access must be for numbers of bytes that are integer multiples of the
volume's sector size. For example, if the sector size is 512 bytes, an
application can request reads and writes of 512, 1024, 1536, or 2048 bytes,
but not of 335, 981, or 7171 bytes.
Buffer addresses for read and write operations must be sector aligned
(aligned on addresses in memory that are integer multiples of the volume's
sector size). One way to sector align buffers is to use the VirtualAlloc
function to allocate the buffers. This function allocates memory that is
aligned on addresses that are integer multiples of the system's page size.
Because both page and volume sector sizes are powers of 2, memory aligned by
multiples of the system's page size is also aligned by multiples of the
volume's sector size.

In short words, the buffer length must be multiple of sector size and buffer
address must be sector aligned. In my test, the length set to multiple of
1024 worked well.
Or just removing FILE_FLAG_NO_BUFFERING flag will work.
Second, lpCompletionRoutine callback function.

The MSDN says:
"lpCompletionRoutine
[in] Pointer to the completion routine to be called when the read operation
is complete and the calling thread is in an alertable wait state."

The important bit being "alertable wait state". You can use SleepEx.
In my test, Inserting SleepEx(1,1) between ReadFileEx and CloseHandle works
fine.


Regards,

Park Heesob
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://rubyforge.org/pipermail/win32utils-devel/attachments/20070926/d2b3110e/attachment.html 


More information about the win32utils-devel mailing list