win32-mmap 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGES +78 -70
- data/MANIFEST +13 -10
- data/README +71 -71
- data/Rakefile +45 -48
- data/appveyor.yml +46 -0
- data/certs/djberg96_pub.pem +21 -0
- data/examples/example_mmap_client.rb +19 -19
- data/examples/example_mmap_file.rb +24 -24
- data/examples/example_mmap_server.rb +19 -19
- data/lib/win32-mmap.rb +1 -0
- data/lib/win32/mmap.rb +406 -406
- data/lib/win32/windows/constants.rb +41 -41
- data/lib/win32/windows/functions.rb +50 -50
- data/lib/win32/windows/structs.rb +28 -28
- data/test/test_win32_mmap.rb +113 -113
- data/win32-mmap.gemspec +25 -25
- metadata +44 -13
- metadata.gz.sig +0 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MREwDwYDVQQDDAhkamJl
|
3
|
+
cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
|
4
|
+
MB4XDTE1MDkwMjIwNDkxOFoXDTE2MDkwMTIwNDkxOFowPzERMA8GA1UEAwwIZGpi
|
5
|
+
ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
|
6
|
+
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMyTkvXqRp6hLs9eoJOS
|
7
|
+
Hmi8kRYbq9Vkf15/hMxJpotYMgJVHHWrmDcC5Dye2PbnXjTkKf266Zw0PtT9h+lI
|
8
|
+
S3ts9HO+vaCFSMwFFZmnWJSpQ3CNw2RcHxjWkk9yF7imEM8Kz9ojhiDXzBetdV6M
|
9
|
+
gr0lV/alUr7TNVBDngbXEfTWscyXh1qd7xZ4EcOdsDktCe5G45N/o3662tPQvJsi
|
10
|
+
FOF0CM/KuBsa/HL1/eoEmF4B3EKIRfTHrQ3hu20Kv3RJ88QM4ec2+0dd97uX693O
|
11
|
+
zv6981fyEg+aXLkxrkViM/tz2qR2ZE0jPhHTREPYeMEgptRkTmWSKAuLVWrJEfgl
|
12
|
+
DtkCAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFEwe
|
13
|
+
nn6bfJADmuIDiMSOzedOrL+xMB0GA1UdEQQWMBSBEmRqYmVyZzk2QGdtYWlsLmNv
|
14
|
+
bTAdBgNVHRIEFjAUgRJkamJlcmc5NkBnbWFpbC5jb20wDQYJKoZIhvcNAQEFBQAD
|
15
|
+
ggEBAHmNOCWoDVD75zHFueY0viwGDVP1BNGFC+yXcb7u2GlK+nEMCORqzURbYPf7
|
16
|
+
tL+/hzmePIRz7i30UM//64GI1NLv9jl7nIwjhPpXpf7/lu2I9hOTsvwSumb5UiKC
|
17
|
+
/sqBxI3sfj9pr79Wpv4MuikX1XPik7Ncb7NPsJPw06Lvyc3Hkg5X2XpPtLtS+Gr2
|
18
|
+
wKJnmzb5rIPS1cmsqv0M9LPWflzfwoZ/SpnmhagP+g05p8bRNKjZSA2iImM/GyYZ
|
19
|
+
EJYzxdPOrx2n6NYR3Hk+vHP0U7UBSveI6+qx+ndQYaeyCn+GRX2PKS9h66YF/Q1V
|
20
|
+
tGSHgAmcLlkdGgan182qsE/4kKM=
|
21
|
+
-----END CERTIFICATE-----
|
@@ -1,20 +1,20 @@
|
|
1
|
-
#######################################################################
|
2
|
-
# example_mmap_client.rb
|
3
|
-
#
|
4
|
-
# This program demonstrates a simple mmap client. You should run this
|
5
|
-
# program *after* you have run the example server program in a
|
6
|
-
# separate terminal.
|
7
|
-
#
|
8
|
-
# You can run this program via the 'rake example_client' task.
|
9
|
-
#
|
10
|
-
# Modify this program as you see fit.
|
11
|
-
#######################################################################
|
12
|
-
require 'win32/mmap'
|
13
|
-
include Win32
|
14
|
-
|
15
|
-
mmap = MMap.open('alpha')
|
16
|
-
|
17
|
-
p mmap.foo
|
18
|
-
p mmap.bar
|
19
|
-
|
1
|
+
#######################################################################
|
2
|
+
# example_mmap_client.rb
|
3
|
+
#
|
4
|
+
# This program demonstrates a simple mmap client. You should run this
|
5
|
+
# program *after* you have run the example server program in a
|
6
|
+
# separate terminal.
|
7
|
+
#
|
8
|
+
# You can run this program via the 'rake example_client' task.
|
9
|
+
#
|
10
|
+
# Modify this program as you see fit.
|
11
|
+
#######################################################################
|
12
|
+
require 'win32/mmap'
|
13
|
+
include Win32
|
14
|
+
|
15
|
+
mmap = MMap.open('alpha')
|
16
|
+
|
17
|
+
p mmap.foo
|
18
|
+
p mmap.bar
|
19
|
+
|
20
20
|
mmap.close
|
@@ -1,25 +1,25 @@
|
|
1
|
-
#######################################################################
|
2
|
-
# example_mmap_file.rb
|
3
|
-
#
|
4
|
-
# This sample script demonstrates creating a memory mapped file, then
|
5
|
-
# later reading from it. You can run this example via the
|
6
|
-
# 'rake example_file' task.
|
7
|
-
#
|
8
|
-
# Modify this program as you see fit.
|
9
|
-
#######################################################################
|
10
|
-
require 'win32/mmap'
|
11
|
-
include Win32
|
12
|
-
|
13
|
-
map1 = MMap.new(:file => "C:\\mmap.test", :size => 1024)
|
14
|
-
|
15
|
-
map1.foo = 'hello'
|
16
|
-
map1.bar = 77
|
17
|
-
|
18
|
-
map1.close
|
19
|
-
|
20
|
-
map2 = MMap.new(:file => "C:\\mmap.test")
|
21
|
-
|
22
|
-
p map2.foo
|
23
|
-
p map2.bar
|
24
|
-
|
1
|
+
#######################################################################
|
2
|
+
# example_mmap_file.rb
|
3
|
+
#
|
4
|
+
# This sample script demonstrates creating a memory mapped file, then
|
5
|
+
# later reading from it. You can run this example via the
|
6
|
+
# 'rake example_file' task.
|
7
|
+
#
|
8
|
+
# Modify this program as you see fit.
|
9
|
+
#######################################################################
|
10
|
+
require 'win32/mmap'
|
11
|
+
include Win32
|
12
|
+
|
13
|
+
map1 = MMap.new(:file => "C:\\mmap.test", :size => 1024)
|
14
|
+
|
15
|
+
map1.foo = 'hello'
|
16
|
+
map1.bar = 77
|
17
|
+
|
18
|
+
map1.close
|
19
|
+
|
20
|
+
map2 = MMap.new(:file => "C:\\mmap.test")
|
21
|
+
|
22
|
+
p map2.foo
|
23
|
+
p map2.bar
|
24
|
+
|
25
25
|
map2.close
|
@@ -1,20 +1,20 @@
|
|
1
|
-
#######################################################################
|
2
|
-
# example_mmap_server.rb
|
3
|
-
#
|
4
|
-
# A test script for general futzing. Run this in its own terminal
|
5
|
-
# then run the example_mmap_client.rb program in a separate terminal.
|
6
|
-
# You can run this program via the 'rake example_server' task.
|
7
|
-
#
|
8
|
-
# Modify as you see fit.
|
9
|
-
#######################################################################
|
10
|
-
require 'win32/mmap'
|
11
|
-
include Win32
|
12
|
-
|
13
|
-
mmap = MMap.new(:name => 'alpha', :size => 2000)
|
14
|
-
|
15
|
-
mmap.foo = 'hello'
|
16
|
-
mmap.bar = 27
|
17
|
-
|
18
|
-
sleep 100
|
19
|
-
|
1
|
+
#######################################################################
|
2
|
+
# example_mmap_server.rb
|
3
|
+
#
|
4
|
+
# A test script for general futzing. Run this in its own terminal
|
5
|
+
# then run the example_mmap_client.rb program in a separate terminal.
|
6
|
+
# You can run this program via the 'rake example_server' task.
|
7
|
+
#
|
8
|
+
# Modify as you see fit.
|
9
|
+
#######################################################################
|
10
|
+
require 'win32/mmap'
|
11
|
+
include Win32
|
12
|
+
|
13
|
+
mmap = MMap.new(:name => 'alpha', :size => 2000)
|
14
|
+
|
15
|
+
mmap.foo = 'hello'
|
16
|
+
mmap.bar = 27
|
17
|
+
|
18
|
+
sleep 100
|
19
|
+
|
20
20
|
mmap.close
|
data/lib/win32-mmap.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'win32/mmap'
|
data/lib/win32/mmap.rb
CHANGED
@@ -1,406 +1,406 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
# The Win32 module serves as a namespace only.
|
6
|
-
#
|
7
|
-
module Win32
|
8
|
-
# The MMap class encapsulates functions for memory mapped files.
|
9
|
-
#
|
10
|
-
class MMap
|
11
|
-
# The version of the win32-mmap library.
|
12
|
-
VERSION = '0.4.
|
13
|
-
|
14
|
-
include Windows::Constants
|
15
|
-
include Windows::Functions
|
16
|
-
include Windows::Structs
|
17
|
-
|
18
|
-
# The name of the file from which to create a mapping object. This
|
19
|
-
# value may be nil.
|
20
|
-
#
|
21
|
-
attr_accessor :file
|
22
|
-
|
23
|
-
# The protection for the file view. This may be any of the following
|
24
|
-
# values:
|
25
|
-
#
|
26
|
-
# * PAGE_READONLY
|
27
|
-
# * PAGE_READWRITE
|
28
|
-
# * PAGE_WRITECOPY
|
29
|
-
# * PAGE_WRITECOPY
|
30
|
-
# * PAGE_EXECUTE_READ
|
31
|
-
# * PAGE_EXECUTE_READWRITE
|
32
|
-
#
|
33
|
-
# You can OR the protection value with any of these section attributes:
|
34
|
-
#
|
35
|
-
# * SEC_COMMIT
|
36
|
-
# * SEC_IMAGE
|
37
|
-
# * SEC_LARGE_PAGES
|
38
|
-
# * SEC_NOCACHE
|
39
|
-
# * SEC_RESERVE
|
40
|
-
#
|
41
|
-
# The default protection is PAGE_READWRITE.
|
42
|
-
#
|
43
|
-
attr_accessor :protection
|
44
|
-
|
45
|
-
# A string that determines the name of the mapping object.
|
46
|
-
# By default this value is nil, i.e. anonymous. If you specify a +name+
|
47
|
-
# that already exists then an attempt is made to access that object.
|
48
|
-
#
|
49
|
-
attr_accessor :name
|
50
|
-
|
51
|
-
# The maximum size for the file mapping object. If a +file+ is
|
52
|
-
# specified, this value defaults to the size of +file+. Otherwise
|
53
|
-
# it defaults to zero (though you should set it manually).
|
54
|
-
#
|
55
|
-
attr_accessor :size
|
56
|
-
|
57
|
-
# Access desired to the file mapping object. This may be
|
58
|
-
# any of the following values:
|
59
|
-
#
|
60
|
-
# * FILE_MAP_WRITE
|
61
|
-
# * FILE_MAP_READ
|
62
|
-
# * FILE_MAP_COPY
|
63
|
-
# * FILE_MAP_ALL_ACCESS
|
64
|
-
#
|
65
|
-
# The default access is FILE_MAP_WRITE.
|
66
|
-
#
|
67
|
-
attr_accessor :access
|
68
|
-
|
69
|
-
# The address of the file view mapping.
|
70
|
-
#
|
71
|
-
attr_reader :address
|
72
|
-
|
73
|
-
# Suggested starting address of mapped view. If this value is not
|
74
|
-
# specified, the system will choose the base mapping address. If you do
|
75
|
-
# specify a value, it must be a multiple of the system's allocation
|
76
|
-
# granularity.
|
77
|
-
#
|
78
|
-
# Note: The MSDN documentation recommends that, in most case cases, you
|
79
|
-
# should not set this value.
|
80
|
-
#--
|
81
|
-
# A system's allocation granularity can be found via GetSystemInfo()
|
82
|
-
# and the dwAllocationGranularity member of the SYSTEM_INFO struct.
|
83
|
-
#
|
84
|
-
attr_accessor :base_address
|
85
|
-
|
86
|
-
# Sets whether or not a semaphore lock is automatically placed on the
|
87
|
-
# mapped view for read and write operations. This is true by default.
|
88
|
-
#
|
89
|
-
attr_writer :autolock
|
90
|
-
|
91
|
-
# The value, in milliseconds, used to wait on the semaphore lock. Only
|
92
|
-
# used if the +autolock+ option is true. The default is 10 milliseconds.
|
93
|
-
#
|
94
|
-
attr_accessor :timeout
|
95
|
-
|
96
|
-
# :call-seq:
|
97
|
-
# MMap.new(opts = {})
|
98
|
-
# MMap.new(opts = {}){ |address| block }
|
99
|
-
#
|
100
|
-
# Creates and returns a new Win32::MMap object. If +file+ is set, then
|
101
|
-
# that file is used to create the mapping. If a block is provided the
|
102
|
-
# address is yielded and the mapping object is automatically closed at the
|
103
|
-
# end of the block.
|
104
|
-
#
|
105
|
-
# Accepts any one of the following hash attributes:
|
106
|
-
#
|
107
|
-
# * protection
|
108
|
-
# * size
|
109
|
-
# * access
|
110
|
-
# * inherit
|
111
|
-
# * name
|
112
|
-
# * base_address
|
113
|
-
# * autolock
|
114
|
-
# * timeout
|
115
|
-
# * file
|
116
|
-
#
|
117
|
-
# Please see the documentation on the individual attributes for
|
118
|
-
# further details. Note that, although these are accessors, they
|
119
|
-
# *must* be set in the constructor (if set at all). Setting them
|
120
|
-
# after the call to MMap.new will not have any effect.
|
121
|
-
#
|
122
|
-
# == Example
|
123
|
-
# require 'win32/mmap'
|
124
|
-
# require 'windows/msvcrt/string'
|
125
|
-
# include Windows::MSVCRT::String
|
126
|
-
# include Win32
|
127
|
-
#
|
128
|
-
# # Reverse the contents of a file.
|
129
|
-
# mmap = MMap.new(:file => 'test.txt') do |addr|
|
130
|
-
# strrev(addr)
|
131
|
-
# end
|
132
|
-
#
|
133
|
-
def initialize(opts = {})
|
134
|
-
valid = %w[
|
135
|
-
file name protection access inherit size
|
136
|
-
base_address open autolock timeout
|
137
|
-
]
|
138
|
-
|
139
|
-
@open = nil
|
140
|
-
@file = nil
|
141
|
-
@autolock = nil
|
142
|
-
|
143
|
-
# Validate the keys, handle inherit specially.
|
144
|
-
opts.each{ |key, value|
|
145
|
-
key = key.to_s.downcase
|
146
|
-
|
147
|
-
unless valid.include?(key)
|
148
|
-
raise ArgumentError, "Invalid key '#{key}'"
|
149
|
-
end
|
150
|
-
|
151
|
-
if key == 'inherit'
|
152
|
-
self.inherit = value # To force inherit= method call
|
153
|
-
else
|
154
|
-
instance_variable_set("@#{key}", value)
|
155
|
-
end
|
156
|
-
}
|
157
|
-
|
158
|
-
@protection ||= PAGE_READWRITE
|
159
|
-
@access ||= FILE_MAP_WRITE
|
160
|
-
@size ||= 0
|
161
|
-
@inherit ||= nil
|
162
|
-
@base_address ||= 0
|
163
|
-
@timeout ||= 10 # Milliseconds
|
164
|
-
|
165
|
-
self.inherit = false unless @inherit
|
166
|
-
|
167
|
-
@hash = {}
|
168
|
-
|
169
|
-
# Anything except an explicit false means the autolock is on.
|
170
|
-
@autolock = true unless @autolock == false
|
171
|
-
|
172
|
-
@lock_flag = 0 # Internal use only
|
173
|
-
|
174
|
-
if @file
|
175
|
-
if File.exists?(@file)
|
176
|
-
fsize = File.size(@file)
|
177
|
-
raise ArgumentError, 'cannot open 0 byte file' if fsize.zero?
|
178
|
-
@size = fsize if @size < fsize
|
179
|
-
end
|
180
|
-
|
181
|
-
rights = GENERIC_READ|GENERIC_WRITE
|
182
|
-
|
183
|
-
@fh = CreateFile(@file, rights, 0, nil, OPEN_ALWAYS, 0, 0)
|
184
|
-
|
185
|
-
if @fh == INVALID_HANDLE_VALUE
|
186
|
-
raise SystemCallError.new('CreateFile', FFI.errno)
|
187
|
-
end
|
188
|
-
else
|
189
|
-
@fh = INVALID_HANDLE_VALUE
|
190
|
-
end
|
191
|
-
|
192
|
-
if @open
|
193
|
-
@mh = OpenFileMapping(@access, @inherit[:bInheritHandle], @name)
|
194
|
-
raise SystemCallError.new('OpenFileMapping', FFI.errno) if @mh == 0
|
195
|
-
else
|
196
|
-
@mh = CreateFileMapping(@fh, @inherit, @protection, 0, @size, @name)
|
197
|
-
raise SystemCallError.new('CreateFileMapping', FFI.errno) if @mh == 0
|
198
|
-
end
|
199
|
-
|
200
|
-
if @open
|
201
|
-
@address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
|
202
|
-
@size = get_view_size()
|
203
|
-
else
|
204
|
-
@address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
|
205
|
-
end
|
206
|
-
|
207
|
-
if @address == 0
|
208
|
-
raise SystemCallError.new('MapviewOfFileEx', FFI.errno)
|
209
|
-
end
|
210
|
-
|
211
|
-
if @autolock
|
212
|
-
@semaphore = CreateSemaphore(nil, 1, 1, "#{@name}.ruby_lock")
|
213
|
-
if @semaphore == 0
|
214
|
-
raise Error, get_last_error
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
if block_given?
|
219
|
-
begin
|
220
|
-
yield @address
|
221
|
-
ensure
|
222
|
-
close
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
@address
|
227
|
-
end
|
228
|
-
|
229
|
-
# Opens an existing file mapping using +name+. You may pass a hash
|
230
|
-
# of +opts+ as you would to MMap.new. If you don't specify a size
|
231
|
-
# as part of the +opts+, it will be dynamically determined for you
|
232
|
-
# (in blocks equal to your system's page size, typically 4k).
|
233
|
-
#
|
234
|
-
# This method is otherwise identical to MMap.new.
|
235
|
-
#--
|
236
|
-
# This forces MMap.new to use OpenFileMapping() behind the scenes.
|
237
|
-
#
|
238
|
-
def self.open(name, opts={}, &block)
|
239
|
-
opts[:name] = name
|
240
|
-
opts[:open] = true
|
241
|
-
self.new(opts, &block)
|
242
|
-
end
|
243
|
-
|
244
|
-
# Sets whether or not the mapping handle can be inherited
|
245
|
-
# by child processes.
|
246
|
-
#--
|
247
|
-
# If true, we have to create a SECURITY_ATTRIBUTES struct and set
|
248
|
-
# its nLength member to 12 and its bInheritHandle member to TRUE.
|
249
|
-
#
|
250
|
-
def inherit=(bool)
|
251
|
-
@inherit = SECURITY_ATTRIBUTES.new
|
252
|
-
@inherit[:nLength] = SECURITY_ATTRIBUTES.size
|
253
|
-
|
254
|
-
if bool
|
255
|
-
@inherit[:bInheritHandle] = true
|
256
|
-
else
|
257
|
-
@inherit[:bInheritHandle] = false
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
# Returns whether or not the mapping handle can be
|
262
|
-
# inherited by child processes. The default is false.
|
263
|
-
#
|
264
|
-
def inherit?
|
265
|
-
@inherit and @inherit[:bInheritHandle]
|
266
|
-
end
|
267
|
-
|
268
|
-
# Writes +num_bytes+ to the disk within a mapped view of a file, or to
|
269
|
-
# the end of the mapping if +num_bytes+ is not specified.
|
270
|
-
#
|
271
|
-
def flush(num_bytes = 0)
|
272
|
-
unless FlushViewOfFile(@address, num_bytes)
|
273
|
-
SystemCallError.new('FlushViewOfFile', FFI.errno)
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
# Unmaps the file view and closes all open file handles. You should
|
278
|
-
# always call this when you are finished with the object (when using
|
279
|
-
# the non-block form).
|
280
|
-
#
|
281
|
-
def close
|
282
|
-
UnmapViewOfFile(@address) if @address
|
283
|
-
CloseHandle(@fh) if @fh
|
284
|
-
CloseHandle(@mh) if @mh
|
285
|
-
ReleaseSemaphore(@semaphore, 1, nil) if @semaphore
|
286
|
-
end
|
287
|
-
|
288
|
-
# Returns whether or not a semaphore lock is automatically placed on the
|
289
|
-
# mapped view for read and write operations. This is true by default.
|
290
|
-
#
|
291
|
-
def autolock?
|
292
|
-
@autolock
|
293
|
-
end
|
294
|
-
|
295
|
-
# Writes a string directly to the underlying file
|
296
|
-
def write_string(content)
|
297
|
-
lock_pattern do
|
298
|
-
ptr = FFI::Pointer.new(:char, @address)
|
299
|
-
ptr.write_string(content,content.length)
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
|
-
# Reads a string of a given length from the beginning of the file
|
304
|
-
# if no length is given, reads the file with the @size attribute
|
305
|
-
# of this instance
|
306
|
-
def read_string(length = @size)
|
307
|
-
lock_pattern do
|
308
|
-
FFI::MemoryPointer.new(:char, length)
|
309
|
-
ptr = FFI::Pointer.new(:char, @address)
|
310
|
-
ptr.read_string(length)
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
private
|
315
|
-
|
316
|
-
# :stopdoc:
|
317
|
-
|
318
|
-
# This is used to allow dynamic getters and setters between memory
|
319
|
-
# mapped objects.
|
320
|
-
#--
|
321
|
-
# This replaces the getvar/setvar API from 0.1.0.
|
322
|
-
#
|
323
|
-
def method_missing(method_id, *args)
|
324
|
-
method = method_id.id2name
|
325
|
-
args = args.first if args.length == 1
|
326
|
-
|
327
|
-
if method[-1,1] == '=' # Setter
|
328
|
-
method.chop!
|
329
|
-
@hash["#{method}"] = args
|
330
|
-
|
331
|
-
lock_pattern do
|
332
|
-
instance_variable_set("@#{method}", args)
|
333
|
-
marshal = Marshal.dump(@hash)
|
334
|
-
ptr = FFI::Pointer.new(:char, @address)
|
335
|
-
ptr.write_string(marshal,marshal.length)
|
336
|
-
end
|
337
|
-
|
338
|
-
else # Getter
|
339
|
-
|
340
|
-
lock_pattern do
|
341
|
-
buf = FFI::MemoryPointer.new(:char, @size)
|
342
|
-
ptr = FFI::Pointer.new(:char, @address)
|
343
|
-
buf = ptr.read_string(@size)
|
344
|
-
hash = Marshal.load(buf)
|
345
|
-
val = hash["#{method}"]
|
346
|
-
instance_variable_set("@#{method}", val)
|
347
|
-
end
|
348
|
-
|
349
|
-
return instance_variable_get("@#{method}")
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
def lock_pattern
|
354
|
-
if @autolock
|
355
|
-
if mmap_lock
|
356
|
-
output = yield
|
357
|
-
mmap_unlock
|
358
|
-
output
|
359
|
-
end
|
360
|
-
else
|
361
|
-
output = yield
|
362
|
-
output
|
363
|
-
end
|
364
|
-
end
|
365
|
-
|
366
|
-
# Adds a semaphore lock the mapping. Only used if +autolock+ is set
|
367
|
-
# to true in the constructor.
|
368
|
-
#
|
369
|
-
def mmap_lock
|
370
|
-
bool = false
|
371
|
-
|
372
|
-
if(@lock_flag == 0)
|
373
|
-
if WaitForSingleObject(@semaphore, @timeout) == WAIT_OBJECT_0
|
374
|
-
bool = true
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
|
-
@lock_flag += 1
|
379
|
-
bool
|
380
|
-
end
|
381
|
-
|
382
|
-
# Releases a semaphore lock on the view. Only used if +autolock+ is
|
383
|
-
# set to true in the constructor.
|
384
|
-
#
|
385
|
-
def mmap_unlock
|
386
|
-
@lock_flag -= 1
|
387
|
-
|
388
|
-
return false if @lock_flag != 0
|
389
|
-
|
390
|
-
if ReleaseSemaphore(@semaphore, 1, nil) == 0
|
391
|
-
raise SystemCallError.new('ReleaseSemaphore', FFI.errno)
|
392
|
-
end
|
393
|
-
|
394
|
-
true
|
395
|
-
end
|
396
|
-
|
397
|
-
# Gets the size of an existing mapping based on the address. This
|
398
|
-
# is used by the MMap.open method when a size isn't specified.
|
399
|
-
#
|
400
|
-
def get_view_size
|
401
|
-
mbi = MEMORY_BASIC_INFORMATION.new
|
402
|
-
VirtualQuery(@address, mbi, mbi.size)
|
403
|
-
mbi[:RegionSize]
|
404
|
-
end
|
405
|
-
end # MMap
|
406
|
-
end # Win32
|
1
|
+
require_relative 'windows/constants'
|
2
|
+
require_relative 'windows/structs'
|
3
|
+
require_relative 'windows/functions'
|
4
|
+
|
5
|
+
# The Win32 module serves as a namespace only.
|
6
|
+
#
|
7
|
+
module Win32
|
8
|
+
# The MMap class encapsulates functions for memory mapped files.
|
9
|
+
#
|
10
|
+
class MMap
|
11
|
+
# The version of the win32-mmap library.
|
12
|
+
VERSION = '0.4.2'
|
13
|
+
|
14
|
+
include Windows::Constants
|
15
|
+
include Windows::Functions
|
16
|
+
include Windows::Structs
|
17
|
+
|
18
|
+
# The name of the file from which to create a mapping object. This
|
19
|
+
# value may be nil.
|
20
|
+
#
|
21
|
+
attr_accessor :file
|
22
|
+
|
23
|
+
# The protection for the file view. This may be any of the following
|
24
|
+
# values:
|
25
|
+
#
|
26
|
+
# * PAGE_READONLY
|
27
|
+
# * PAGE_READWRITE
|
28
|
+
# * PAGE_WRITECOPY
|
29
|
+
# * PAGE_WRITECOPY
|
30
|
+
# * PAGE_EXECUTE_READ
|
31
|
+
# * PAGE_EXECUTE_READWRITE
|
32
|
+
#
|
33
|
+
# You can OR the protection value with any of these section attributes:
|
34
|
+
#
|
35
|
+
# * SEC_COMMIT
|
36
|
+
# * SEC_IMAGE
|
37
|
+
# * SEC_LARGE_PAGES
|
38
|
+
# * SEC_NOCACHE
|
39
|
+
# * SEC_RESERVE
|
40
|
+
#
|
41
|
+
# The default protection is PAGE_READWRITE.
|
42
|
+
#
|
43
|
+
attr_accessor :protection
|
44
|
+
|
45
|
+
# A string that determines the name of the mapping object.
|
46
|
+
# By default this value is nil, i.e. anonymous. If you specify a +name+
|
47
|
+
# that already exists then an attempt is made to access that object.
|
48
|
+
#
|
49
|
+
attr_accessor :name
|
50
|
+
|
51
|
+
# The maximum size for the file mapping object. If a +file+ is
|
52
|
+
# specified, this value defaults to the size of +file+. Otherwise
|
53
|
+
# it defaults to zero (though you should set it manually).
|
54
|
+
#
|
55
|
+
attr_accessor :size
|
56
|
+
|
57
|
+
# Access desired to the file mapping object. This may be
|
58
|
+
# any of the following values:
|
59
|
+
#
|
60
|
+
# * FILE_MAP_WRITE
|
61
|
+
# * FILE_MAP_READ
|
62
|
+
# * FILE_MAP_COPY
|
63
|
+
# * FILE_MAP_ALL_ACCESS
|
64
|
+
#
|
65
|
+
# The default access is FILE_MAP_WRITE.
|
66
|
+
#
|
67
|
+
attr_accessor :access
|
68
|
+
|
69
|
+
# The address of the file view mapping.
|
70
|
+
#
|
71
|
+
attr_reader :address
|
72
|
+
|
73
|
+
# Suggested starting address of mapped view. If this value is not
|
74
|
+
# specified, the system will choose the base mapping address. If you do
|
75
|
+
# specify a value, it must be a multiple of the system's allocation
|
76
|
+
# granularity.
|
77
|
+
#
|
78
|
+
# Note: The MSDN documentation recommends that, in most case cases, you
|
79
|
+
# should not set this value.
|
80
|
+
#--
|
81
|
+
# A system's allocation granularity can be found via GetSystemInfo()
|
82
|
+
# and the dwAllocationGranularity member of the SYSTEM_INFO struct.
|
83
|
+
#
|
84
|
+
attr_accessor :base_address
|
85
|
+
|
86
|
+
# Sets whether or not a semaphore lock is automatically placed on the
|
87
|
+
# mapped view for read and write operations. This is true by default.
|
88
|
+
#
|
89
|
+
attr_writer :autolock
|
90
|
+
|
91
|
+
# The value, in milliseconds, used to wait on the semaphore lock. Only
|
92
|
+
# used if the +autolock+ option is true. The default is 10 milliseconds.
|
93
|
+
#
|
94
|
+
attr_accessor :timeout
|
95
|
+
|
96
|
+
# :call-seq:
|
97
|
+
# MMap.new(opts = {})
|
98
|
+
# MMap.new(opts = {}){ |address| block }
|
99
|
+
#
|
100
|
+
# Creates and returns a new Win32::MMap object. If +file+ is set, then
|
101
|
+
# that file is used to create the mapping. If a block is provided the
|
102
|
+
# address is yielded and the mapping object is automatically closed at the
|
103
|
+
# end of the block.
|
104
|
+
#
|
105
|
+
# Accepts any one of the following hash attributes:
|
106
|
+
#
|
107
|
+
# * protection
|
108
|
+
# * size
|
109
|
+
# * access
|
110
|
+
# * inherit
|
111
|
+
# * name
|
112
|
+
# * base_address
|
113
|
+
# * autolock
|
114
|
+
# * timeout
|
115
|
+
# * file
|
116
|
+
#
|
117
|
+
# Please see the documentation on the individual attributes for
|
118
|
+
# further details. Note that, although these are accessors, they
|
119
|
+
# *must* be set in the constructor (if set at all). Setting them
|
120
|
+
# after the call to MMap.new will not have any effect.
|
121
|
+
#
|
122
|
+
# == Example
|
123
|
+
# require 'win32/mmap'
|
124
|
+
# require 'windows/msvcrt/string'
|
125
|
+
# include Windows::MSVCRT::String
|
126
|
+
# include Win32
|
127
|
+
#
|
128
|
+
# # Reverse the contents of a file.
|
129
|
+
# mmap = MMap.new(:file => 'test.txt') do |addr|
|
130
|
+
# strrev(addr)
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
def initialize(opts = {})
|
134
|
+
valid = %w[
|
135
|
+
file name protection access inherit size
|
136
|
+
base_address open autolock timeout
|
137
|
+
]
|
138
|
+
|
139
|
+
@open = nil
|
140
|
+
@file = nil
|
141
|
+
@autolock = nil
|
142
|
+
|
143
|
+
# Validate the keys, handle inherit specially.
|
144
|
+
opts.each{ |key, value|
|
145
|
+
key = key.to_s.downcase
|
146
|
+
|
147
|
+
unless valid.include?(key)
|
148
|
+
raise ArgumentError, "Invalid key '#{key}'"
|
149
|
+
end
|
150
|
+
|
151
|
+
if key == 'inherit'
|
152
|
+
self.inherit = value # To force inherit= method call
|
153
|
+
else
|
154
|
+
instance_variable_set("@#{key}", value)
|
155
|
+
end
|
156
|
+
}
|
157
|
+
|
158
|
+
@protection ||= PAGE_READWRITE
|
159
|
+
@access ||= FILE_MAP_WRITE
|
160
|
+
@size ||= 0
|
161
|
+
@inherit ||= nil
|
162
|
+
@base_address ||= 0
|
163
|
+
@timeout ||= 10 # Milliseconds
|
164
|
+
|
165
|
+
self.inherit = false unless @inherit
|
166
|
+
|
167
|
+
@hash = {}
|
168
|
+
|
169
|
+
# Anything except an explicit false means the autolock is on.
|
170
|
+
@autolock = true unless @autolock == false
|
171
|
+
|
172
|
+
@lock_flag = 0 # Internal use only
|
173
|
+
|
174
|
+
if @file
|
175
|
+
if File.exists?(@file)
|
176
|
+
fsize = File.size(@file)
|
177
|
+
raise ArgumentError, 'cannot open 0 byte file' if fsize.zero?
|
178
|
+
@size = fsize if @size < fsize
|
179
|
+
end
|
180
|
+
|
181
|
+
rights = GENERIC_READ|GENERIC_WRITE
|
182
|
+
|
183
|
+
@fh = CreateFile(@file, rights, 0, nil, OPEN_ALWAYS, 0, 0)
|
184
|
+
|
185
|
+
if @fh == INVALID_HANDLE_VALUE
|
186
|
+
raise SystemCallError.new('CreateFile', FFI.errno)
|
187
|
+
end
|
188
|
+
else
|
189
|
+
@fh = INVALID_HANDLE_VALUE
|
190
|
+
end
|
191
|
+
|
192
|
+
if @open
|
193
|
+
@mh = OpenFileMapping(@access, @inherit[:bInheritHandle], @name)
|
194
|
+
raise SystemCallError.new('OpenFileMapping', FFI.errno) if @mh == 0
|
195
|
+
else
|
196
|
+
@mh = CreateFileMapping(@fh, @inherit, @protection, 0, @size, @name)
|
197
|
+
raise SystemCallError.new('CreateFileMapping', FFI.errno) if @mh == 0
|
198
|
+
end
|
199
|
+
|
200
|
+
if @open
|
201
|
+
@address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
|
202
|
+
@size = get_view_size()
|
203
|
+
else
|
204
|
+
@address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
|
205
|
+
end
|
206
|
+
|
207
|
+
if @address == 0
|
208
|
+
raise SystemCallError.new('MapviewOfFileEx', FFI.errno)
|
209
|
+
end
|
210
|
+
|
211
|
+
if @autolock
|
212
|
+
@semaphore = CreateSemaphore(nil, 1, 1, "#{@name}.ruby_lock")
|
213
|
+
if @semaphore == 0
|
214
|
+
raise Error, get_last_error
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
if block_given?
|
219
|
+
begin
|
220
|
+
yield @address
|
221
|
+
ensure
|
222
|
+
close
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
@address
|
227
|
+
end
|
228
|
+
|
229
|
+
# Opens an existing file mapping using +name+. You may pass a hash
|
230
|
+
# of +opts+ as you would to MMap.new. If you don't specify a size
|
231
|
+
# as part of the +opts+, it will be dynamically determined for you
|
232
|
+
# (in blocks equal to your system's page size, typically 4k).
|
233
|
+
#
|
234
|
+
# This method is otherwise identical to MMap.new.
|
235
|
+
#--
|
236
|
+
# This forces MMap.new to use OpenFileMapping() behind the scenes.
|
237
|
+
#
|
238
|
+
def self.open(name, opts={}, &block)
|
239
|
+
opts[:name] = name
|
240
|
+
opts[:open] = true
|
241
|
+
self.new(opts, &block)
|
242
|
+
end
|
243
|
+
|
244
|
+
# Sets whether or not the mapping handle can be inherited
|
245
|
+
# by child processes.
|
246
|
+
#--
|
247
|
+
# If true, we have to create a SECURITY_ATTRIBUTES struct and set
|
248
|
+
# its nLength member to 12 and its bInheritHandle member to TRUE.
|
249
|
+
#
|
250
|
+
def inherit=(bool)
|
251
|
+
@inherit = SECURITY_ATTRIBUTES.new
|
252
|
+
@inherit[:nLength] = SECURITY_ATTRIBUTES.size
|
253
|
+
|
254
|
+
if bool
|
255
|
+
@inherit[:bInheritHandle] = true
|
256
|
+
else
|
257
|
+
@inherit[:bInheritHandle] = false
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# Returns whether or not the mapping handle can be
|
262
|
+
# inherited by child processes. The default is false.
|
263
|
+
#
|
264
|
+
def inherit?
|
265
|
+
@inherit and @inherit[:bInheritHandle]
|
266
|
+
end
|
267
|
+
|
268
|
+
# Writes +num_bytes+ to the disk within a mapped view of a file, or to
|
269
|
+
# the end of the mapping if +num_bytes+ is not specified.
|
270
|
+
#
|
271
|
+
def flush(num_bytes = 0)
|
272
|
+
unless FlushViewOfFile(@address, num_bytes)
|
273
|
+
SystemCallError.new('FlushViewOfFile', FFI.errno)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# Unmaps the file view and closes all open file handles. You should
|
278
|
+
# always call this when you are finished with the object (when using
|
279
|
+
# the non-block form).
|
280
|
+
#
|
281
|
+
def close
|
282
|
+
UnmapViewOfFile(@address) if @address
|
283
|
+
CloseHandle(@fh) if @fh
|
284
|
+
CloseHandle(@mh) if @mh
|
285
|
+
ReleaseSemaphore(@semaphore, 1, nil) if @semaphore
|
286
|
+
end
|
287
|
+
|
288
|
+
# Returns whether or not a semaphore lock is automatically placed on the
|
289
|
+
# mapped view for read and write operations. This is true by default.
|
290
|
+
#
|
291
|
+
def autolock?
|
292
|
+
@autolock
|
293
|
+
end
|
294
|
+
|
295
|
+
# Writes a string directly to the underlying file
|
296
|
+
def write_string(content)
|
297
|
+
lock_pattern do
|
298
|
+
ptr = FFI::Pointer.new(:char, @address)
|
299
|
+
ptr.write_string(content,content.length)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# Reads a string of a given length from the beginning of the file
|
304
|
+
# if no length is given, reads the file with the @size attribute
|
305
|
+
# of this instance
|
306
|
+
def read_string(length = @size)
|
307
|
+
lock_pattern do
|
308
|
+
FFI::MemoryPointer.new(:char, length)
|
309
|
+
ptr = FFI::Pointer.new(:char, @address)
|
310
|
+
ptr.read_string(length)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
private
|
315
|
+
|
316
|
+
# :stopdoc:
|
317
|
+
|
318
|
+
# This is used to allow dynamic getters and setters between memory
|
319
|
+
# mapped objects.
|
320
|
+
#--
|
321
|
+
# This replaces the getvar/setvar API from 0.1.0.
|
322
|
+
#
|
323
|
+
def method_missing(method_id, *args)
|
324
|
+
method = method_id.id2name
|
325
|
+
args = args.first if args.length == 1
|
326
|
+
|
327
|
+
if method[-1,1] == '=' # Setter
|
328
|
+
method.chop!
|
329
|
+
@hash["#{method}"] = args
|
330
|
+
|
331
|
+
lock_pattern do
|
332
|
+
instance_variable_set("@#{method}", args)
|
333
|
+
marshal = Marshal.dump(@hash)
|
334
|
+
ptr = FFI::Pointer.new(:char, @address)
|
335
|
+
ptr.write_string(marshal,marshal.length)
|
336
|
+
end
|
337
|
+
|
338
|
+
else # Getter
|
339
|
+
|
340
|
+
lock_pattern do
|
341
|
+
buf = FFI::MemoryPointer.new(:char, @size)
|
342
|
+
ptr = FFI::Pointer.new(:char, @address)
|
343
|
+
buf = ptr.read_string(@size)
|
344
|
+
hash = Marshal.load(buf)
|
345
|
+
val = hash["#{method}"]
|
346
|
+
instance_variable_set("@#{method}", val)
|
347
|
+
end
|
348
|
+
|
349
|
+
return instance_variable_get("@#{method}")
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def lock_pattern
|
354
|
+
if @autolock
|
355
|
+
if mmap_lock
|
356
|
+
output = yield
|
357
|
+
mmap_unlock
|
358
|
+
output
|
359
|
+
end
|
360
|
+
else
|
361
|
+
output = yield
|
362
|
+
output
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
# Adds a semaphore lock the mapping. Only used if +autolock+ is set
|
367
|
+
# to true in the constructor.
|
368
|
+
#
|
369
|
+
def mmap_lock
|
370
|
+
bool = false
|
371
|
+
|
372
|
+
if(@lock_flag == 0)
|
373
|
+
if WaitForSingleObject(@semaphore, @timeout) == WAIT_OBJECT_0
|
374
|
+
bool = true
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
@lock_flag += 1
|
379
|
+
bool
|
380
|
+
end
|
381
|
+
|
382
|
+
# Releases a semaphore lock on the view. Only used if +autolock+ is
|
383
|
+
# set to true in the constructor.
|
384
|
+
#
|
385
|
+
def mmap_unlock
|
386
|
+
@lock_flag -= 1
|
387
|
+
|
388
|
+
return false if @lock_flag != 0
|
389
|
+
|
390
|
+
if ReleaseSemaphore(@semaphore, 1, nil) == 0
|
391
|
+
raise SystemCallError.new('ReleaseSemaphore', FFI.errno)
|
392
|
+
end
|
393
|
+
|
394
|
+
true
|
395
|
+
end
|
396
|
+
|
397
|
+
# Gets the size of an existing mapping based on the address. This
|
398
|
+
# is used by the MMap.open method when a size isn't specified.
|
399
|
+
#
|
400
|
+
def get_view_size
|
401
|
+
mbi = MEMORY_BASIC_INFORMATION.new
|
402
|
+
VirtualQuery(@address, mbi, mbi.size)
|
403
|
+
mbi[:RegionSize]
|
404
|
+
end
|
405
|
+
end # MMap
|
406
|
+
end # Win32
|