[Win32utils-devel] Trouble getting a callback to work with FFI

Daniel Berger djberg96 at gmail.com
Tue Jul 3 13:26:35 UTC 2012


On Mon, Jul 2, 2012 at 6:43 PM, Heesob Park <phasis at gmail.com> wrote:
> Hi,
>
> 2012/7/3 Daniel Berger <djberg96 at gmail.com>
>>
>> Hi,
>>
>> I'm getting a segfault when I try to pass a callback proc to
>> ReadFileEx. The proc fires, then the interpreter segfaults.
>>
>> Any ideas? I tried a blocking callback, too, but that didn't help.
>>
>> require 'ffi'
>>
>> class Windows
>>  extend FFI::Library
>>  ffi_lib :kernel32
>>
>>  class Overlapped < FFI::Struct
>>    layout(
>>      :Internal, :ulong,
>>      :InternalHigh, :ulong,
>>      :Offset, :ulong,
>>      :OffsetHigh, :ulong,
>>      :hEvent, :ulong
>>    )
>>  end
>>
>>  callback :completion_proc, [:ulong, :ulong, Overlapped], :void
>>
>>  attach_function :CloseHandle, [:ulong], :bool
>>  attach_function :CreateFileA, [:string, :ulong, :ulong, :pointer,
>> :ulong, :ulong, :ulong], :ulong
>>  attach_function :ReadFileEx, [:ulong, :buffer_out, :ulong,
>> Overlapped, :completion_proc], :bool
>>  attach_function :GetOverlappedResult, [:ulong, :pointer, :pointer,
>> :bool], :bool
>>  attach_function :SleepEx, [:ulong, :bool], :ulong
>>
>>  OPEN_EXISTING             = 3
>>  GENERIC_READ              = 0x80000000
>>  FILE_SHARE_READ           = 0x00000001
>>  FILE_FLAG_OVERLAPPED      = 0x40000000
>>  FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000
>>  INVALID_HANDLE_VALUE      = 0xFFFFFFFF
>>  ERROR_IO_PENDING          = 997
>>
>>  def self.read(file)
>>    flags = FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN
>>
>>    begin
>>      handle = CreateFileA(
>>        file,
>>        GENERIC_READ,
>>        FILE_SHARE_READ,
>>        nil,
>>        OPEN_EXISTING,
>>        flags,
>>        0
>>      )
>>
>>      if handle == INVALID_HANDLE_VALUE
>>        raise SystemCallError, FFI.errno, "CreateFile"
>>      end
>>
>>      buf   = 0.chr * File.size(file)
>>      olap  = Overlapped.new
>>
>>      cproc = Proc.new do |error, bytes, overlapped|
>>        puts "Error: #{error}"
>>        puts "Bytes: #{bytes}"
>>        p overlapped
>>      end
>>
>>      ### FAIL: cproc fires, but then I get a segfault
>>      bool  = ReadFileEx(handle, buf, buf.size, olap, cproc)
>>      errno = FFI.errno
>>
>>      SleepEx(1, true)
>>
>>      unless bool
>>        if errno == ERROR_IO_PENDING
>>          bytes = FFI::MemoryPointer.new(:ulong)
>>          unless GetOverlappedResult(handle, olap, bytes, true)
>>            raise SystemCallError, FFI.errno, "GetOverlappedResult"
>>          end
>>        else
>>          raise SystemCallError, errno, "ReadFileEx"
>>        end
>>      end
>>
>>      buf.delete(0.chr)
>>    ensure
>>      CloseHandle(handle) if handle
>>    end
>>  end
>> end
>>
>> File.open('small.txt', 'w'){ |fh| fh.puts "Hello World" }
>> p Windows.read('small.txt')
>>
>
> I guess this is due to the calling convention mismatch.
>
> If you add ffi_convention line, it will work.
>
> ...
> class Windows
>  extend FFI::Library
>  ffi_lib :kernel32
>  ffi_convention :stdcall  # add this line

Thanks, I was thinking this was the default. However, I still noticed issues.

First, it only seems to work with mingw. If I used the MSVC++ version
it still bombs.

Second, it only seems to work if I declare a callback in the function
prototype and use a Proc. If I try to use a :pointer + FFI::Function,
it still segfaults. In other words this works:

callback :completion_proc, [:ulong, :ulong, Overlapped], :void
attach_function :ReadFileEx, [:ulong, :buffer_out, :ulong, Overlapped,
:completion_proc], :bool
cproc = Proc.new{ |x,y,z| }

But this does not:

attach_function :ReadFileEx, [:ulong, :buffer_out, :ulong, Overlapped,
:pointer], :bool
cproc = FFI::Function.new(:void, [:ulong, :ulong, :pointer]){ |x,y,z| }

Do you see the same thing?

Regards,

Dan


More information about the win32utils-devel mailing list