ruby-ole 1.2.6 → 1.2.7
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.
- data/ChangeLog +12 -0
- data/README +27 -0
- data/Rakefile +2 -1
- data/bin/oletool +7 -1
- data/lib/ole/file_system.rb +2 -424
- data/lib/ole/storage.rb +3 -949
- data/lib/ole/storage/base.rb +948 -0
- data/lib/ole/storage/file_system.rb +444 -0
- data/lib/ole/storage/meta_data.rb +142 -0
- data/lib/ole/support.rb +23 -27
- data/lib/ole/types.rb +2 -243
- data/lib/ole/types/base.rb +247 -0
- data/lib/ole/types/property_set.rb +165 -0
- data/test/test_filesystem.rb +12 -8
- data/test/test_mbat.rb +2 -2
- data/test/test_meta_data.rb +45 -0
- data/test/test_property_set.rb +13 -13
- data/test/test_ranges_io.rb +10 -0
- data/test/test_storage.rb +91 -4
- data/test/test_types.rb +18 -5
- metadata +56 -42
- data/lib/ole/property_set.rb +0 -172
data/lib/ole/support.rb
CHANGED
@@ -99,6 +99,8 @@ module RecursivelyEnumerable
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
+
# don't think this is actually a proper breadth first recursion. only first
|
103
|
+
# level is breadth first.
|
102
104
|
def each_recursive_breadth_first(&block)
|
103
105
|
children = []
|
104
106
|
each_child do |child|
|
@@ -131,41 +133,35 @@ module RecursivelyEnumerable
|
|
131
133
|
# streams a "tree" form of the recursively enumerable structure to +io+, or
|
132
134
|
# return a string form instead if +io+ is not specified.
|
133
135
|
#
|
134
|
-
# mostly a debugging aid. can specify a different
|
135
|
-
#
|
136
|
-
def to_tree io=
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
def to_tree_helper io, method, prefix
|
146
|
-
# i need to know when i get to the last child. use delay to know
|
147
|
-
child = nil
|
148
|
-
each_child do |next_child|
|
149
|
-
if child
|
150
|
-
io << "#{prefix}|- #{child.send method}\n"
|
151
|
-
if child.respond_to? :to_tree_helper
|
152
|
-
child.to_tree_helper io, method, prefix + '| '
|
136
|
+
# mostly a debugging aid. can specify a different block which will be called
|
137
|
+
# to provide the string form for each node.
|
138
|
+
def to_tree io='', &inspect
|
139
|
+
inspect ||= :inspect.to_proc
|
140
|
+
io << "- #{inspect[self]}\n"
|
141
|
+
recurse = proc do |node, prefix|
|
142
|
+
child = nil
|
143
|
+
node.each_child do |next_child|
|
144
|
+
if child
|
145
|
+
io << "#{prefix}|- #{inspect[child]}\n"
|
146
|
+
recurse.call child, prefix + '| '
|
153
147
|
end
|
148
|
+
child = next_child
|
149
|
+
end if node.respond_to?(:each_child)
|
150
|
+
if child
|
151
|
+
io << "#{prefix}\\- #{inspect[child]}\n"
|
152
|
+
recurse.call child, prefix + ' '
|
154
153
|
end
|
155
|
-
child = next_child
|
156
|
-
end
|
157
|
-
return unless child
|
158
|
-
# child is the last child
|
159
|
-
io << "#{prefix}\\- #{child.send method}\n"
|
160
|
-
if child.respond_to? :to_tree_helper
|
161
|
-
child.to_tree_helper io, method, prefix + ' '
|
162
154
|
end
|
155
|
+
recurse.call self, ' '
|
156
|
+
io
|
163
157
|
end
|
164
|
-
protected :to_tree_helper
|
165
158
|
end
|
166
159
|
|
167
160
|
# can include File::Constants
|
168
161
|
class IO
|
162
|
+
# this is for jruby
|
163
|
+
include File::Constants unless defined?(RDONLY)
|
164
|
+
|
169
165
|
# nabbed from rubinius, and modified
|
170
166
|
def self.parse_mode mode
|
171
167
|
ret = 0
|
data/lib/ole/types.rb
CHANGED
@@ -1,243 +1,2 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
|
4
|
-
require 'ole/base'
|
5
|
-
|
6
|
-
module Ole # :nodoc:
|
7
|
-
#
|
8
|
-
# The Types module contains all the serialization and deserialization code for standard ole
|
9
|
-
# types.
|
10
|
-
#
|
11
|
-
# It also defines all the variant type constants, and symbolic names.
|
12
|
-
#
|
13
|
-
module Types
|
14
|
-
# for anything that we don't have serialization code for
|
15
|
-
class Data < String
|
16
|
-
def self.load str
|
17
|
-
new str
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.dump str
|
21
|
-
str.to_s
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Lpstr < String
|
26
|
-
def self.load str
|
27
|
-
# not sure if its always there, but there is often a trailing
|
28
|
-
# null byte.
|
29
|
-
new str.chomp(0.chr)
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.dump str
|
33
|
-
# do i need to append the null byte?
|
34
|
-
str.to_s
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# for VT_LPWSTR
|
39
|
-
class Lpwstr < String
|
40
|
-
FROM_UTF16 = Iconv.new 'utf-8', 'utf-16le'
|
41
|
-
TO_UTF16 = Iconv.new 'utf-16le', 'utf-8'
|
42
|
-
|
43
|
-
def self.load str
|
44
|
-
new FROM_UTF16.iconv(str).chomp(0.chr)
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.dump str
|
48
|
-
# need to append nulls?
|
49
|
-
TO_UTF16.iconv str
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# for VT_FILETIME
|
54
|
-
class FileTime < DateTime
|
55
|
-
SIZE = 8
|
56
|
-
EPOCH = new 1601, 1, 1
|
57
|
-
|
58
|
-
# Create a +DateTime+ object from a struct +FILETIME+
|
59
|
-
# (http://msdn2.microsoft.com/en-us/library/ms724284.aspx).
|
60
|
-
#
|
61
|
-
# Converts +str+ to two 32 bit time values, comprising the high and low 32 bits of
|
62
|
-
# the 100's of nanoseconds since 1st january 1601 (Epoch).
|
63
|
-
def self.load str
|
64
|
-
low, high = str.to_s.unpack 'V2'
|
65
|
-
# we ignore these, without even warning about it
|
66
|
-
return nil if low == 0 and high == 0
|
67
|
-
# switched to rational, and fixed the off by 1 second error i sometimes got.
|
68
|
-
# time = EPOCH + (high * (1 << 32) + low) / 1e7 / 86400 rescue return
|
69
|
-
# use const_get to ensure we can return anything which subclasses this (VT_DATE?)
|
70
|
-
const_get('EPOCH') + Rational(high * (1 << 32) + low, 1e7.to_i * 86400) rescue return
|
71
|
-
# extra sanity check...
|
72
|
-
#unless (1800...2100) === time.year
|
73
|
-
# Log.warn "ignoring unlikely time value #{time.to_s}"
|
74
|
-
# return nil
|
75
|
-
#end
|
76
|
-
#time
|
77
|
-
end
|
78
|
-
|
79
|
-
# +time+ should be able to be either a Time, Date, or DateTime.
|
80
|
-
def self.dump time
|
81
|
-
# i think i'll convert whatever i get to be a datetime, because of
|
82
|
-
# the covered range.
|
83
|
-
return 0.chr * SIZE unless time
|
84
|
-
time = time.send(:to_datetime) if Time === time
|
85
|
-
# don't bother to use const_get here
|
86
|
-
bignum = (time - EPOCH) * 86400 * 1e7.to_i
|
87
|
-
high, low = bignum.divmod 1 << 32
|
88
|
-
[low, high].pack 'V2'
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# for VT_CLSID
|
93
|
-
# Unlike most of the other conversions, the Guid's are serialized/deserialized by actually
|
94
|
-
# doing nothing! (eg, _load & _dump are null ops)
|
95
|
-
# Rather, its just a string with a different inspect string, and it includes a
|
96
|
-
# helper method for creating a Guid from that readable form (#format).
|
97
|
-
class Clsid < String
|
98
|
-
SIZE = 16
|
99
|
-
PACK = 'V v v CC C6'
|
100
|
-
|
101
|
-
def self.load str
|
102
|
-
new str.to_s
|
103
|
-
end
|
104
|
-
|
105
|
-
def self.dump guid
|
106
|
-
return 0.chr * SIZE unless guid
|
107
|
-
# allow use of plain strings in place of guids.
|
108
|
-
guid['-'] ? parse(guid) : guid
|
109
|
-
end
|
110
|
-
|
111
|
-
def self.parse str
|
112
|
-
vals = str.scan(/[a-f\d]+/i).map(&:hex)
|
113
|
-
if vals.length == 5
|
114
|
-
# this is pretty ugly
|
115
|
-
vals[3] = ('%04x' % vals[3]).scan(/../).map(&:hex)
|
116
|
-
vals[4] = ('%012x' % vals[4]).scan(/../).map(&:hex)
|
117
|
-
guid = new vals.flatten.pack(PACK)
|
118
|
-
return guid unless guid.delete('{}') == str.downcase.delete('{}')
|
119
|
-
end
|
120
|
-
raise ArgumentError, 'invalid guid - %p' % str
|
121
|
-
end
|
122
|
-
|
123
|
-
def format
|
124
|
-
"%08x-%04x-%04x-%02x%02x-#{'%02x' * 6}" % unpack(PACK)
|
125
|
-
end
|
126
|
-
|
127
|
-
def inspect
|
128
|
-
"#<#{self.class}:{#{format}}>"
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
#
|
133
|
-
# The OLE variant types, extracted from
|
134
|
-
# http://www.marin.clara.net/COM/variant_type_definitions.htm.
|
135
|
-
#
|
136
|
-
# A subset is also in WIN32OLE::VARIANT, but its not cross platform (obviously).
|
137
|
-
#
|
138
|
-
# Use like:
|
139
|
-
#
|
140
|
-
# p Ole::Types::Variant::NAMES[0x001f] => 'VT_LPWSTR'
|
141
|
-
# p Ole::Types::VT_DATE # => 7
|
142
|
-
#
|
143
|
-
# The serialization / deserialization functions should be fixed to make it easier
|
144
|
-
# to work with. like
|
145
|
-
#
|
146
|
-
# Ole::Types.from_str(VT_DATE, data) # and
|
147
|
-
# Ole::Types.to_str(VT_DATE, data)
|
148
|
-
#
|
149
|
-
# Or similar, rather than having to do VT_* <=> ad hoc class name etc as it is
|
150
|
-
# currently.
|
151
|
-
#
|
152
|
-
module Variant
|
153
|
-
NAMES = {
|
154
|
-
0x0000 => 'VT_EMPTY',
|
155
|
-
0x0001 => 'VT_NULL',
|
156
|
-
0x0002 => 'VT_I2',
|
157
|
-
0x0003 => 'VT_I4',
|
158
|
-
0x0004 => 'VT_R4',
|
159
|
-
0x0005 => 'VT_R8',
|
160
|
-
0x0006 => 'VT_CY',
|
161
|
-
0x0007 => 'VT_DATE',
|
162
|
-
0x0008 => 'VT_BSTR',
|
163
|
-
0x0009 => 'VT_DISPATCH',
|
164
|
-
0x000a => 'VT_ERROR',
|
165
|
-
0x000b => 'VT_BOOL',
|
166
|
-
0x000c => 'VT_VARIANT',
|
167
|
-
0x000d => 'VT_UNKNOWN',
|
168
|
-
0x000e => 'VT_DECIMAL',
|
169
|
-
0x0010 => 'VT_I1',
|
170
|
-
0x0011 => 'VT_UI1',
|
171
|
-
0x0012 => 'VT_UI2',
|
172
|
-
0x0013 => 'VT_UI4',
|
173
|
-
0x0014 => 'VT_I8',
|
174
|
-
0x0015 => 'VT_UI8',
|
175
|
-
0x0016 => 'VT_INT',
|
176
|
-
0x0017 => 'VT_UINT',
|
177
|
-
0x0018 => 'VT_VOID',
|
178
|
-
0x0019 => 'VT_HRESULT',
|
179
|
-
0x001a => 'VT_PTR',
|
180
|
-
0x001b => 'VT_SAFEARRAY',
|
181
|
-
0x001c => 'VT_CARRAY',
|
182
|
-
0x001d => 'VT_USERDEFINED',
|
183
|
-
0x001e => 'VT_LPSTR',
|
184
|
-
0x001f => 'VT_LPWSTR',
|
185
|
-
0x0040 => 'VT_FILETIME',
|
186
|
-
0x0041 => 'VT_BLOB',
|
187
|
-
0x0042 => 'VT_STREAM',
|
188
|
-
0x0043 => 'VT_STORAGE',
|
189
|
-
0x0044 => 'VT_STREAMED_OBJECT',
|
190
|
-
0x0045 => 'VT_STORED_OBJECT',
|
191
|
-
0x0046 => 'VT_BLOB_OBJECT',
|
192
|
-
0x0047 => 'VT_CF',
|
193
|
-
0x0048 => 'VT_CLSID',
|
194
|
-
0x0fff => 'VT_ILLEGALMASKED',
|
195
|
-
0x0fff => 'VT_TYPEMASK',
|
196
|
-
0x1000 => 'VT_VECTOR',
|
197
|
-
0x2000 => 'VT_ARRAY',
|
198
|
-
0x4000 => 'VT_BYREF',
|
199
|
-
0x8000 => 'VT_RESERVED',
|
200
|
-
0xffff => 'VT_ILLEGAL'
|
201
|
-
}
|
202
|
-
|
203
|
-
CLASS_MAP = {
|
204
|
-
# haven't seen one of these. wonder if its same as FILETIME?
|
205
|
-
#'VT_DATE' => ?,
|
206
|
-
'VT_LPSTR' => Lpstr,
|
207
|
-
'VT_LPWSTR' => Lpwstr,
|
208
|
-
'VT_FILETIME' => FileTime,
|
209
|
-
'VT_CLSID' => Clsid
|
210
|
-
}
|
211
|
-
|
212
|
-
module Constants
|
213
|
-
NAMES.each { |num, name| const_set name, num }
|
214
|
-
end
|
215
|
-
|
216
|
-
def self.load type, str
|
217
|
-
type = NAMES[type] or raise ArgumentError, 'unknown ole type - 0x%04x' % type
|
218
|
-
(CLASS_MAP[type] || Data).load str
|
219
|
-
end
|
220
|
-
|
221
|
-
def self.dump type, variant
|
222
|
-
type = NAMES[type] or raise ArgumentError, 'unknown ole type - 0x%04x' % type
|
223
|
-
(CLASS_MAP[type] || Data).dump variant
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
include Variant::Constants
|
228
|
-
|
229
|
-
# deprecated aliases, kept mostly for the benefit of ruby-msg, until
|
230
|
-
# i release a new version.
|
231
|
-
def self.load_guid str
|
232
|
-
Variant.load VT_CLSID, str
|
233
|
-
end
|
234
|
-
|
235
|
-
def self.load_time str
|
236
|
-
Variant.load VT_FILETIME, str
|
237
|
-
end
|
238
|
-
|
239
|
-
FROM_UTF16 = Lpwstr::FROM_UTF16
|
240
|
-
TO_UTF16 = Lpwstr::TO_UTF16
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
1
|
+
require 'ole/types/base'
|
2
|
+
require 'ole/types/property_set'
|
@@ -0,0 +1,247 @@
|
|
1
|
+
require 'iconv'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
require 'ole/base'
|
5
|
+
|
6
|
+
module Ole # :nodoc:
|
7
|
+
#
|
8
|
+
# The Types module contains all the serialization and deserialization code for standard ole
|
9
|
+
# types.
|
10
|
+
#
|
11
|
+
# It also defines all the variant type constants, and symbolic names.
|
12
|
+
#
|
13
|
+
module Types
|
14
|
+
# for anything that we don't have serialization code for
|
15
|
+
class Data < String
|
16
|
+
def self.load str
|
17
|
+
new str
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.dump str
|
21
|
+
str.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Lpstr < String
|
26
|
+
def self.load str
|
27
|
+
# not sure if its always there, but there is often a trailing
|
28
|
+
# null byte.
|
29
|
+
new str.chomp(0.chr)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.dump str
|
33
|
+
# do i need to append the null byte?
|
34
|
+
str.to_s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# for VT_LPWSTR
|
39
|
+
class Lpwstr < String
|
40
|
+
FROM_UTF16 = Iconv.new 'utf-8', 'utf-16le'
|
41
|
+
TO_UTF16 = Iconv.new 'utf-16le', 'utf-8'
|
42
|
+
|
43
|
+
def self.load str
|
44
|
+
new FROM_UTF16.iconv(str).chomp(0.chr)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.dump str
|
48
|
+
# need to append nulls?
|
49
|
+
TO_UTF16.iconv str
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# for VT_FILETIME
|
54
|
+
class FileTime < DateTime
|
55
|
+
SIZE = 8
|
56
|
+
EPOCH = new 1601, 1, 1
|
57
|
+
|
58
|
+
# Create a +DateTime+ object from a struct +FILETIME+
|
59
|
+
# (http://msdn2.microsoft.com/en-us/library/ms724284.aspx).
|
60
|
+
#
|
61
|
+
# Converts +str+ to two 32 bit time values, comprising the high and low 32 bits of
|
62
|
+
# the 100's of nanoseconds since 1st january 1601 (Epoch).
|
63
|
+
def self.load str
|
64
|
+
low, high = str.to_s.unpack 'V2'
|
65
|
+
# we ignore these, without even warning about it
|
66
|
+
return nil if low == 0 and high == 0
|
67
|
+
# switched to rational, and fixed the off by 1 second error i sometimes got.
|
68
|
+
# time = EPOCH + (high * (1 << 32) + low) / 1e7 / 86400 rescue return
|
69
|
+
# use const_get to ensure we can return anything which subclasses this (VT_DATE?)
|
70
|
+
const_get('EPOCH') + Rational(high * (1 << 32) + low, 1e7.to_i * 86400) rescue return
|
71
|
+
# extra sanity check...
|
72
|
+
#unless (1800...2100) === time.year
|
73
|
+
# Log.warn "ignoring unlikely time value #{time.to_s}"
|
74
|
+
# return nil
|
75
|
+
#end
|
76
|
+
#time
|
77
|
+
end
|
78
|
+
|
79
|
+
# +time+ should be able to be either a Time, Date, or DateTime.
|
80
|
+
def self.dump time
|
81
|
+
# i think i'll convert whatever i get to be a datetime, because of
|
82
|
+
# the covered range.
|
83
|
+
return 0.chr * SIZE unless time
|
84
|
+
time = time.send(:to_datetime) if Time === time
|
85
|
+
# don't bother to use const_get here
|
86
|
+
bignum = (time - EPOCH) * 86400 * 1e7.to_i
|
87
|
+
high, low = bignum.divmod 1 << 32
|
88
|
+
[low, high].pack 'V2'
|
89
|
+
end
|
90
|
+
|
91
|
+
def inspect
|
92
|
+
"#<#{self.class} #{to_s}>"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# for VT_CLSID
|
97
|
+
# Unlike most of the other conversions, the Guid's are serialized/deserialized by actually
|
98
|
+
# doing nothing! (eg, _load & _dump are null ops)
|
99
|
+
# Rather, its just a string with a different inspect string, and it includes a
|
100
|
+
# helper method for creating a Guid from that readable form (#format).
|
101
|
+
class Clsid < String
|
102
|
+
SIZE = 16
|
103
|
+
PACK = 'V v v CC C6'
|
104
|
+
|
105
|
+
def self.load str
|
106
|
+
new str.to_s
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.dump guid
|
110
|
+
return 0.chr * SIZE unless guid
|
111
|
+
# allow use of plain strings in place of guids.
|
112
|
+
guid['-'] ? parse(guid) : guid
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.parse str
|
116
|
+
vals = str.scan(/[a-f\d]+/i).map(&:hex)
|
117
|
+
if vals.length == 5
|
118
|
+
# this is pretty ugly
|
119
|
+
vals[3] = ('%04x' % vals[3]).scan(/../).map(&:hex)
|
120
|
+
vals[4] = ('%012x' % vals[4]).scan(/../).map(&:hex)
|
121
|
+
guid = new vals.flatten.pack(PACK)
|
122
|
+
return guid if guid.format.delete('{}') == str.downcase.delete('{}')
|
123
|
+
end
|
124
|
+
raise ArgumentError, 'invalid guid - %p' % str
|
125
|
+
end
|
126
|
+
|
127
|
+
def format
|
128
|
+
"%08x-%04x-%04x-%02x%02x-#{'%02x' * 6}" % unpack(PACK)
|
129
|
+
end
|
130
|
+
|
131
|
+
def inspect
|
132
|
+
"#<#{self.class}:{#{format}}>"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
#
|
137
|
+
# The OLE variant types, extracted from
|
138
|
+
# http://www.marin.clara.net/COM/variant_type_definitions.htm.
|
139
|
+
#
|
140
|
+
# A subset is also in WIN32OLE::VARIANT, but its not cross platform (obviously).
|
141
|
+
#
|
142
|
+
# Use like:
|
143
|
+
#
|
144
|
+
# p Ole::Types::Variant::NAMES[0x001f] => 'VT_LPWSTR'
|
145
|
+
# p Ole::Types::VT_DATE # => 7
|
146
|
+
#
|
147
|
+
# The serialization / deserialization functions should be fixed to make it easier
|
148
|
+
# to work with. like
|
149
|
+
#
|
150
|
+
# Ole::Types.from_str(VT_DATE, data) # and
|
151
|
+
# Ole::Types.to_str(VT_DATE, data)
|
152
|
+
#
|
153
|
+
# Or similar, rather than having to do VT_* <=> ad hoc class name etc as it is
|
154
|
+
# currently.
|
155
|
+
#
|
156
|
+
module Variant
|
157
|
+
NAMES = {
|
158
|
+
0x0000 => 'VT_EMPTY',
|
159
|
+
0x0001 => 'VT_NULL',
|
160
|
+
0x0002 => 'VT_I2',
|
161
|
+
0x0003 => 'VT_I4',
|
162
|
+
0x0004 => 'VT_R4',
|
163
|
+
0x0005 => 'VT_R8',
|
164
|
+
0x0006 => 'VT_CY',
|
165
|
+
0x0007 => 'VT_DATE',
|
166
|
+
0x0008 => 'VT_BSTR',
|
167
|
+
0x0009 => 'VT_DISPATCH',
|
168
|
+
0x000a => 'VT_ERROR',
|
169
|
+
0x000b => 'VT_BOOL',
|
170
|
+
0x000c => 'VT_VARIANT',
|
171
|
+
0x000d => 'VT_UNKNOWN',
|
172
|
+
0x000e => 'VT_DECIMAL',
|
173
|
+
0x0010 => 'VT_I1',
|
174
|
+
0x0011 => 'VT_UI1',
|
175
|
+
0x0012 => 'VT_UI2',
|
176
|
+
0x0013 => 'VT_UI4',
|
177
|
+
0x0014 => 'VT_I8',
|
178
|
+
0x0015 => 'VT_UI8',
|
179
|
+
0x0016 => 'VT_INT',
|
180
|
+
0x0017 => 'VT_UINT',
|
181
|
+
0x0018 => 'VT_VOID',
|
182
|
+
0x0019 => 'VT_HRESULT',
|
183
|
+
0x001a => 'VT_PTR',
|
184
|
+
0x001b => 'VT_SAFEARRAY',
|
185
|
+
0x001c => 'VT_CARRAY',
|
186
|
+
0x001d => 'VT_USERDEFINED',
|
187
|
+
0x001e => 'VT_LPSTR',
|
188
|
+
0x001f => 'VT_LPWSTR',
|
189
|
+
0x0040 => 'VT_FILETIME',
|
190
|
+
0x0041 => 'VT_BLOB',
|
191
|
+
0x0042 => 'VT_STREAM',
|
192
|
+
0x0043 => 'VT_STORAGE',
|
193
|
+
0x0044 => 'VT_STREAMED_OBJECT',
|
194
|
+
0x0045 => 'VT_STORED_OBJECT',
|
195
|
+
0x0046 => 'VT_BLOB_OBJECT',
|
196
|
+
0x0047 => 'VT_CF',
|
197
|
+
0x0048 => 'VT_CLSID',
|
198
|
+
0x0fff => 'VT_ILLEGALMASKED',
|
199
|
+
0x0fff => 'VT_TYPEMASK',
|
200
|
+
0x1000 => 'VT_VECTOR',
|
201
|
+
0x2000 => 'VT_ARRAY',
|
202
|
+
0x4000 => 'VT_BYREF',
|
203
|
+
0x8000 => 'VT_RESERVED',
|
204
|
+
0xffff => 'VT_ILLEGAL'
|
205
|
+
}
|
206
|
+
|
207
|
+
CLASS_MAP = {
|
208
|
+
# haven't seen one of these. wonder if its same as FILETIME?
|
209
|
+
#'VT_DATE' => ?,
|
210
|
+
'VT_LPSTR' => Lpstr,
|
211
|
+
'VT_LPWSTR' => Lpwstr,
|
212
|
+
'VT_FILETIME' => FileTime,
|
213
|
+
'VT_CLSID' => Clsid
|
214
|
+
}
|
215
|
+
|
216
|
+
module Constants
|
217
|
+
NAMES.each { |num, name| const_set name, num }
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.load type, str
|
221
|
+
type = NAMES[type] or raise ArgumentError, 'unknown ole type - 0x%04x' % type
|
222
|
+
(CLASS_MAP[type] || Data).load str
|
223
|
+
end
|
224
|
+
|
225
|
+
def self.dump type, variant
|
226
|
+
type = NAMES[type] or raise ArgumentError, 'unknown ole type - 0x%04x' % type
|
227
|
+
(CLASS_MAP[type] || Data).dump variant
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
include Variant::Constants
|
232
|
+
|
233
|
+
# deprecated aliases, kept mostly for the benefit of ruby-msg, until
|
234
|
+
# i release a new version.
|
235
|
+
def self.load_guid str
|
236
|
+
Variant.load VT_CLSID, str
|
237
|
+
end
|
238
|
+
|
239
|
+
def self.load_time str
|
240
|
+
Variant.load VT_FILETIME, str
|
241
|
+
end
|
242
|
+
|
243
|
+
FROM_UTF16 = Lpwstr::FROM_UTF16
|
244
|
+
TO_UTF16 = Lpwstr::TO_UTF16
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|