Class: Win32::MMap

Inherits:
Object
  • Object
show all
Includes:
Windows::Constants, Windows::Functions, Windows::Structs
Defined in:
lib/win32/mmap.rb

Overview

The MMap class encapsulates functions for memory mapped files.

Constant Summary collapse

VERSION =

The version of the win32-mmap library.

'0.4.2'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ MMap

:call-seq:

MMap.new(opts = {})
MMap.new(opts = {}){ |address| block }

Creates and returns a new Win32::MMap object. If file is set, then that file is used to create the mapping. If a block is provided the address is yielded and the mapping object is automatically closed at the end of the block.

Accepts any one of the following hash attributes:

  • protection

  • size

  • access

  • inherit

  • name

  • base_address

  • autolock

  • timeout

  • file

Please see the documentation on the individual attributes for further details. Note that, although these are accessors, they must be set in the constructor (if set at all). Setting them after the call to MMap.new will not have any effect.

Example

require 'win32/mmap'
require 'windows/msvcrt/string'
include Windows::MSVCRT::String
include Win32

# Reverse the contents of a file.
mmap = MMap.new(:file => 'test.txt') do |addr|
   strrev(addr)
end


133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/win32/mmap.rb', line 133

def initialize(opts = {})
  valid = %w[
    file name protection access inherit size
    base_address open autolock timeout
  ]

  @open     = nil
  @file     = nil
  @autolock = nil

  # Validate the keys, handle inherit specially.
  opts.each{ |key, value|
    key = key.to_s.downcase

    unless valid.include?(key)
      raise ArgumentError, "Invalid key '#{key}'"
    end

    if key == 'inherit'
      self.inherit = value # To force inherit= method call
    else
      instance_variable_set("@#{key}", value)
    end
  }

  @protection   ||= PAGE_READWRITE
  @access       ||= FILE_MAP_WRITE
  @size         ||= 0
  @inherit      ||= nil
  @base_address ||= 0
  @timeout      ||= 10 # Milliseconds

  self.inherit = false unless @inherit

  @hash = {}

  # Anything except an explicit false means the autolock is on.
  @autolock = true unless @autolock == false

  @lock_flag = 0 # Internal use only

  if @file
    if File.exists?(@file)
      fsize = File.size(@file)
      raise ArgumentError, 'cannot open 0 byte file' if fsize.zero?
      @size = fsize if @size < fsize
    end

    rights = GENERIC_READ|GENERIC_WRITE

    @fh = CreateFile(@file, rights, 0, nil, OPEN_ALWAYS, 0, 0)

    if @fh == INVALID_HANDLE_VALUE
      raise SystemCallError.new('CreateFile', FFI.errno)
    end
  else
    @fh = INVALID_HANDLE_VALUE
  end

  if @open
    @mh = OpenFileMapping(@access, @inherit[:bInheritHandle], @name)
    raise SystemCallError.new('OpenFileMapping', FFI.errno) if @mh == 0
  else
    @mh = CreateFileMapping(@fh, @inherit, @protection, 0, @size, @name)
    raise SystemCallError.new('CreateFileMapping', FFI.errno) if @mh == 0
  end

  if @open
    @address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
    @size    = get_view_size()
  else
    @address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
  end

  if @address == 0
    raise SystemCallError.new('MapviewOfFileEx', FFI.errno)
  end

  if @autolock
    @semaphore = CreateSemaphore(nil, 1, 1, "#{@name}.ruby_lock")
    if @semaphore == 0
      raise Error, get_last_error
    end
  end

  if block_given?
    begin
      yield @address
    ensure
     close
    end
  end

  @address
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_id, *args) ⇒ Object (private)

This is used to allow dynamic getters and setters between memory mapped objects. – This replaces the getvar/setvar API from 0.1.0.



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/win32/mmap.rb', line 323

def method_missing(method_id, *args)
  method = method_id.id2name
  args = args.first if args.length == 1

  if method[-1,1] == '=' # Setter
    method.chop!
    @hash["#{method}"] = args

    lock_pattern do
      instance_variable_set("@#{method}", args)
      marshal = Marshal.dump(@hash)
      ptr = FFI::Pointer.new(:char, @address)
      ptr.write_string(marshal,marshal.length)
    end

  else # Getter

    lock_pattern do
      buf = FFI::MemoryPointer.new(:char, @size)
      ptr = FFI::Pointer.new(:char, @address)
      buf = ptr.read_string(@size)
      hash = Marshal.load(buf)
      val  = hash["#{method}"]
      instance_variable_set("@#{method}", val)
    end

    return instance_variable_get("@#{method}")
  end
end

Instance Attribute Details

#accessObject

Access desired to the file mapping object. This may be any of the following values:

  • FILE_MAP_WRITE

  • FILE_MAP_READ

  • FILE_MAP_COPY

  • FILE_MAP_ALL_ACCESS

The default access is FILE_MAP_WRITE.



67
68
69
# File 'lib/win32/mmap.rb', line 67

def access
  @access
end

#addressObject (readonly)

The address of the file view mapping.



71
72
73
# File 'lib/win32/mmap.rb', line 71

def address
  @address
end

#autolock=(value) ⇒ Object (writeonly)

Sets whether or not a semaphore lock is automatically placed on the mapped view for read and write operations. This is true by default.



89
90
91
# File 'lib/win32/mmap.rb', line 89

def autolock=(value)
  @autolock = value
end

#base_addressObject

Suggested starting address of mapped view. If this value is not specified, the system will choose the base mapping address. If you do specify a value, it must be a multiple of the system’s allocation granularity.

Note: The MSDN documentation recommends that, in most case cases, you should not set this value. – A system’s allocation granularity can be found via GetSystemInfo() and the dwAllocationGranularity member of the SYSTEM_INFO struct.



84
85
86
# File 'lib/win32/mmap.rb', line 84

def base_address
  @base_address
end

#fileObject

The name of the file from which to create a mapping object. This value may be nil.



21
22
23
# File 'lib/win32/mmap.rb', line 21

def file
  @file
end

#nameObject

A string that determines the name of the mapping object. By default this value is nil, i.e. anonymous. If you specify a name that already exists then an attempt is made to access that object.



49
50
51
# File 'lib/win32/mmap.rb', line 49

def name
  @name
end

#protectionObject

The protection for the file view. This may be any of the following values:

  • PAGE_READONLY

  • PAGE_READWRITE

  • PAGE_WRITECOPY

  • PAGE_WRITECOPY

  • PAGE_EXECUTE_READ

  • PAGE_EXECUTE_READWRITE

You can OR the protection value with any of these section attributes:

  • SEC_COMMIT

  • SEC_IMAGE

  • SEC_LARGE_PAGES

  • SEC_NOCACHE

  • SEC_RESERVE

The default protection is PAGE_READWRITE.



43
44
45
# File 'lib/win32/mmap.rb', line 43

def protection
  @protection
end

#sizeObject

The maximum size for the file mapping object. If a file is specified, this value defaults to the size of file. Otherwise it defaults to zero (though you should set it manually).



55
56
57
# File 'lib/win32/mmap.rb', line 55

def size
  @size
end

#timeoutObject

The value, in milliseconds, used to wait on the semaphore lock. Only used if the autolock option is true. The default is 10 milliseconds.



94
95
96
# File 'lib/win32/mmap.rb', line 94

def timeout
  @timeout
end

Class Method Details

.open(name, opts = {}, &block) ⇒ Object

Opens an existing file mapping using name. You may pass a hash of opts as you would to MMap.new. If you don’t specify a size as part of the opts, it will be dynamically determined for you (in blocks equal to your system’s page size, typically 4k).

This method is otherwise identical to MMap.new. – This forces MMap.new to use OpenFileMapping() behind the scenes.



238
239
240
241
242
# File 'lib/win32/mmap.rb', line 238

def self.open(name, opts={}, &block)
  opts[:name] = name
  opts[:open] = true
  self.new(opts, &block)
end

Instance Method Details

#autolock?Boolean

Returns whether or not a semaphore lock is automatically placed on the mapped view for read and write operations. This is true by default.

Returns:

  • (Boolean)


291
292
293
# File 'lib/win32/mmap.rb', line 291

def autolock?
  @autolock
end

#closeObject

Unmaps the file view and closes all open file handles. You should always call this when you are finished with the object (when using the non-block form).



281
282
283
284
285
286
# File 'lib/win32/mmap.rb', line 281

def close
  UnmapViewOfFile(@address) if @address
  CloseHandle(@fh) if @fh
  CloseHandle(@mh) if @mh
  ReleaseSemaphore(@semaphore, 1, nil) if @semaphore
end

#flush(num_bytes = 0) ⇒ Object

Writes num_bytes to the disk within a mapped view of a file, or to the end of the mapping if num_bytes is not specified.



271
272
273
274
275
# File 'lib/win32/mmap.rb', line 271

def flush(num_bytes = 0)
  unless FlushViewOfFile(@address, num_bytes)
    SystemCallError.new('FlushViewOfFile', FFI.errno)
  end
end

#inherit=(bool) ⇒ Object

Sets whether or not the mapping handle can be inherited by child processes. – If true, we have to create a SECURITY_ATTRIBUTES struct and set its nLength member to 12 and its bInheritHandle member to TRUE.



250
251
252
253
254
255
256
257
258
259
# File 'lib/win32/mmap.rb', line 250

def inherit=(bool)
  @inherit = SECURITY_ATTRIBUTES.new
  @inherit[:nLength] = SECURITY_ATTRIBUTES.size

  if bool
    @inherit[:bInheritHandle] = true
  else
    @inherit[:bInheritHandle] = false
  end
end

#inherit?Boolean

Returns whether or not the mapping handle can be inherited by child processes. The default is false.

Returns:

  • (Boolean)


264
265
266
# File 'lib/win32/mmap.rb', line 264

def inherit?
  @inherit and @inherit[:bInheritHandle]
end

#read_string(length = @size) ⇒ Object

Reads a string of a given length from the beginning of the file if no length is given, reads the file with the @size attribute of this instance



306
307
308
309
310
311
312
# File 'lib/win32/mmap.rb', line 306

def read_string(length = @size)
  lock_pattern do
    FFI::MemoryPointer.new(:char, length)
    ptr = FFI::Pointer.new(:char, @address)
    ptr.read_string(length)
  end
end

#write_string(content) ⇒ Object

Writes a string directly to the underlying file



296
297
298
299
300
301
# File 'lib/win32/mmap.rb', line 296

def write_string(content)
  lock_pattern do
    ptr = FFI::Pointer.new(:char, @address)
    ptr.write_string(content,content.length)
  end
end