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

Daniel Berger djberg96 at gmail.com
Tue Jul 3 16:14:44 UTC 2012


On Tue, Jul 3, 2012 at 9:15 AM, Heesob Park <phasis at gmail.com> wrote:
> Hi,
>
> 2012/7/3 Daniel Berger <djberg96 at gmail.com>
>>
>> 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?
>>
>
> First, I can see segfaults with MSVC++ version.
> Yet, I have no idea what is the cause.
>
> Second, the calling convention is the matter.
>
> cproc = FFI::Function.new(:void, [:ulong, :ulong, :pointer]){ |x,y,z| }
> should be
> cproc = FFI::Function.new(:void, [:ulong, :ulong, :pointer],{:convention
> =>:stdcall}){ |x,y,z| }

Ah, thanks. I thought it was automatic if I called it earlier. It
probably should be, but I digress.

I can make it work if I wrap the SleepEx call in its own thread,
though I think that defeats the purpose of calling SleepEx in the
first place, since it wouldn't put the main thread in an alertable
state, right? I suppose I could wrap the majority of the code in its
own thread.

Regards,

Dan


More information about the win32utils-devel mailing list