windows-api 0.4.4 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
data/lib/windows/api.rb CHANGED
@@ -1,503 +1,505 @@
1
- require 'win32/api'
2
- require 'rbconfig'
3
- require 'forwardable'
4
- include RbConfig
5
-
6
- # The Windows module serves as a namespace only
7
- module Windows
8
-
9
- # With Microsoft Visual C++ 8 and later users should use the associated
10
- # DLL instead of msvcrt directly, if possible.
11
- if CONFIG['host_os'].split('_')[1]
12
- if CONFIG['host_os'].split('_')[1].to_i >= 80
13
- MSVCRT_DLL = 'msvcr' + CONFIG['host_os'].split('_')[1]
14
- else
15
- MSVCRT_DLL = 'msvcrt'
16
- end
17
- else
18
- MSVCRT_DLL = 'msvcrt'
19
- end
20
-
21
- # Wrapper around the Win32::API class
22
- class API
23
- extend Forwardable
24
-
25
- # The version of the windows-api library
26
- VERSION = '0.4.4'
27
-
28
- # The methods from Win32::API are delegated to the appropriate object
29
- def_delegators(:@api, :function_name, :dll_name, :prototype)
30
- def_delegators(:@api, :return_type, :effective_function_name)
31
-
32
- private
33
-
34
- # Verbose data types that can be used instead of single letters
35
- DATA_TYPES = {
36
- 'ATOM' => 'I',
37
- 'BOOL' => 'B',
38
- 'BOOLEAN' => 'B',
39
- 'BYTE' => 'I',
40
- 'CALLBACK' => 'K',
41
- 'CHAR' => 'I',
42
- 'COLORREF' => 'L',
43
- 'DWORD' => 'L',
44
- 'DWORDLONG' => 'L',
45
- 'DWORD_PTR' => 'P',
46
- 'DWORD32' => 'I',
47
- 'DWORD64' => 'L',
48
- 'HACCEL' => 'L',
49
- 'HANDLE' => 'L',
50
- 'HBITMAP' => 'L',
51
- 'HBRUSH' => 'L',
52
- 'HCOLORSPACE' => 'L',
53
- 'HCONV' => 'L',
54
- 'HDC' => 'L',
55
- 'HFILE' => 'I',
56
- 'HFONT' => 'L',
57
- 'HINSTANCE' => 'L',
58
- 'HKEY' => 'L',
59
- 'HLOCAL' => 'L',
60
- 'HMENU' => 'L',
61
- 'HMODULE' => 'L',
62
- 'HRESULT' => 'L',
63
- 'HWND' => 'L',
64
- 'INT' => 'I',
65
- 'INT_PTR' => 'P',
66
- 'INT32' => 'I',
67
- 'INT64' => 'L',
68
- 'LANGID' => 'I',
69
- 'LCID' => 'L',
70
- 'LCTYPE' => 'L',
71
- 'LONG' => 'L',
72
- 'LONGLONG' => 'L',
73
- 'LONG_PTR' => 'P',
74
- 'LONG32' => 'L',
75
- 'LONG64' => 'L',
76
- 'LPARAM' => 'P',
77
- 'LPBOOL' => 'P',
78
- 'LPBYTE' => 'P',
79
- 'LPCOLORREF' => 'P',
80
- 'LPCSTR' => 'P',
81
- 'LPCTSTR' => 'P',
82
- 'LPCVOID' => 'L',
83
- 'LPCWSTR' => 'P',
84
- 'LPDWORD' => 'P',
85
- 'LPHANDLE' => 'P',
86
- 'LPINT' => 'P',
87
- 'LPLONG' => 'P',
88
- 'LPSTR' => 'P',
89
- 'LPTSTR' => 'P',
90
- 'LPVOID' => 'L',
91
- 'LPWORD' => 'P',
92
- 'LPWSTR' => 'P',
93
- 'LRESULT' => 'P',
94
- 'PBOOL' => 'P',
95
- 'PBOOLEAN' => 'P',
96
- 'PBYTE' => 'P',
97
- 'PHKEY' => 'P',
98
- 'SC_HANDLE' => 'L',
99
- 'SC_LOCK' => 'L',
100
- 'SERVICE_STATUS_HANDLE' => 'L',
101
- 'SHORT' => 'I',
102
- 'SIZE_T' => 'P',
103
- 'TCHAR' => 'L',
104
- 'UINT' => 'I',
105
- 'UINT_PTR' => 'P',
106
- 'UINT32' => 'I',
107
- 'UINT64' => 'L',
108
- 'ULONG' => 'L',
109
- 'ULONGLONG' => 'L',
110
- 'ULONG_PTR' => 'P',
111
- 'ULONG32' => 'L',
112
- 'ULONG64' => 'L',
113
- 'USHORT' => 'I',
114
- 'USN' => 'L',
115
- 'WINAPI' => 'L',
116
- 'WORD' => 'I'
117
- }
118
-
119
- public
120
-
121
- @auto_constant = false
122
- @auto_method = false
123
- @auto_unicode = false
124
- @auto_namespace = nil
125
-
126
- # Returns the value of the @auto_constant class instance variable. The
127
- # default is nil, i.e. none. See the Windows::API.auto_constant=
128
- # documentation for more information.
129
- #
130
- def self.auto_constant
131
- @auto_constant
132
- end
133
-
134
- # Automatically sets a constant to match the function name.
135
- #
136
- # The standard practice for defining Windows::API objects is to use
137
- # a constant that matches the function name. For example, this is a
138
- # typical idiom:
139
- #
140
- # module Windows
141
- # module File
142
- # GetFileAttributes = API.new('GetFileAttributes', 'P','L')
143
- # end
144
- # end
145
- #
146
- # With the API.auto_constant value set to true you can avoid the
147
- # assignment step and the matching constant name will be automatically
148
- # set for you in the namespace defined in API.auto_namespace. In other
149
- # words, this example is identical to the one above:
150
- #
151
- # module Windows
152
- # module File
153
- # API.auto_constant = true
154
- # API.auto_namespace = 'Windows::File'
155
- # API.new('GetFileAttributes', 'P', 'L')
156
- # end
157
- # end
158
- #
159
- # If the auto_constant class variable is set to true, but no
160
- # auto_namespace is set, an error will be raised. Note that the
161
- # namespace must refer to an existing module (not a class).
162
- #--
163
- # TODO: If there's a way to automatically grab the namespace internally,
164
- # nesting and all, I'd love to know the solution.
165
- #
166
- def self.auto_constant=(bool)
167
- @auto_constant = bool
168
- end
169
-
170
- # Returns the value of the auto_namespace class instance variable. Used
171
- # in conjunction with API.auto_constant and/or API.auto_method.
172
- #
173
- def self.auto_namespace
174
- @auto_namespace
175
- end
176
-
177
- # Sets the value of the auto_namespace class nstance variable. The
178
- # default is nil, i.e. none. Use in conjunction with the auto_constant
179
- # and/or auto_method class variables, this method will automatically set
180
- # a constant and/or method in +namespace+ equal to the function name set
181
- # in the constructor.
182
- #
183
- # The +namespace+ must refer to an existing module, not a class.
184
- #
185
- def self.auto_namespace=(namespace)
186
- @auto_namespace = namespace
187
- end
188
-
189
- # Returns the value of the auto_method class instance variable. Used in
190
- # conjunction with auto_unicode. See API.auto_method= for more
191
- # information.
192
- #
193
- def self.auto_method
194
- @auto_method
195
- end
196
-
197
- # If this option is set to true then a corresponding method is
198
- # automatically generated when you create a new Windows::API object.
199
- #
200
- # For example, instead of doing this:
201
- #
202
- # module Windows
203
- # module File
204
- # GetFileAttributes = API.new('GetFileAttributes', 'P', 'L')
205
- #
206
- # def GetFileAttributes(x)
207
- # GetFileAttributes.call(x)
208
- # end
209
- # end
210
- # end
211
- #
212
- # You can do this, and have the method autogenerated for you.
213
- #
214
- # module Windows
215
- # module File
216
- # API.auto_namespace = 'Windows::File'
217
- # API.auto_constant = true
218
- # API.auto_method = true
219
- # API.new('GetFileAttributes', 'P', 'L')
220
- # end
221
- # end
222
- #
223
- # include Windows::File
224
- # GetFileAttributes('C:/test.txt') # vs. GetFileAttributes.call
225
- #
226
- # If the Windows::API object is declared to be a boolean in the
227
- # constructor, then the method definition automatically includes a
228
- # '!= 0' clause at the end of the call. That way, you can do
229
- # 'if SomeMethod(x)' instead of having to do 'if SomeMethod(x) != 0',
230
- # and it will do the right thing.
231
- #
232
- # If the API.auto_unicode option is also set to true, then you will
233
- # get three method definitions. The standard function name, the explicit
234
- # ANSI ('A') version and the explicit Unicode/wide version ('W'). The
235
- # exception to this rule is that the explicit ANSI and Unicode methods
236
- # will NOT be generated if the function passed to the constructor
237
- # already ends with 'A' or 'W'.
238
- #
239
- def self.auto_method=(bool)
240
- @auto_method = bool
241
- end
242
-
243
- # Returns the value of the auto_unicode class instance variable. This
244
- # is used in conjunction with the auto_method and/or auto_constant class
245
- # variables. Not significant if neither of those variables are set.
246
- #
247
- def self.auto_unicode
248
- @auto_unicode
249
- end
250
-
251
- # If set to true, and the auto_constant variable is set, then the
252
- # automatic constant generation will generate the explicit ANSI ('A')
253
- # and Unicode/wide ('W') versions of the function passed to the
254
- # constructor, if such versions exist. Likewise, if the the
255
- # auto_method variable is set, then explicit ANSI and Unicode methods
256
- # are generated.
257
- #
258
- # Here's a typical idiom:
259
- #
260
- # module Windows
261
- # module Path
262
- # API.auto_namespace = Windows::Path
263
- # API.auto_constant = true
264
- # API.new('shlwapi', 'PathAddBackslash', 'P', 'P')
265
- # API.new('shlwapi', 'PathAddBackslashA', 'P', 'P')
266
- # API.new('shlwapi', 'PathAddBackslashW', 'P', 'P')
267
- # end
268
- # end
269
- #
270
- # That can be reduced to this:
271
- #
272
- # module Windows
273
- # module Path
274
- # API.auto_namespace = Windows::Path
275
- # API.auto_constant = true
276
- # API.auto_unicode = true
277
- # API.new('shlwapi', 'PathAddBackslash', 'P', 'P')
278
- # end
279
- # end
280
- #
281
- # This value is ignored if the function passed to the constructor
282
- # already ends with an 'A' or 'W'.
283
- #
284
- def self.auto_unicode=(bool)
285
- @auto_unicode = bool
286
- end
287
-
288
- # call-seq:
289
- # API.new(func, proto='V', rtype='L', dll='kernel32')
290
- #
291
- # Creates and returns a new Windows::API object. The +func+ is the
292
- # name of the Windows function.
293
- #
294
- # The +proto+ is the function prototype for +func+. This can be a
295
- # string or an array of characters. The possible valid characters
296
- # are 'I' (integer), 'B' (BOOL), 'L' (long), 'V' (void), or 'P' (pointer).
297
- # The default is void ('V').
298
- #
299
- # The +rtype+ argument is the return type for the function. The valid
300
- # characters are the same as for the +proto+. The default is long ('L').
301
- #
302
- # The 'B' (BOOL) return type is the same as 'I' (Integer). This is
303
- # significant only if the API.auto_method option is set to true, in which
304
- # case it alters the generated method definition slightly. See the
305
- # API.auto_method= class method for more information.
306
- #
307
- # The +dll+ is the name of the DLL file that the function is exported
308
- # from. The default is 'kernel32'.
309
- #
310
- # If the function cannot be found then an API::Error is raised (a subclass
311
- # of RuntimeError).
312
- #
313
- def initialize(func, proto='V', rtype='L', dll='kernel32')
314
- # Convert literal data types to values that win32-api understands
315
- if proto.is_a?(Array)
316
- proto.each_with_index{ |pt, index|
317
- if pt.length > 1
318
- proto[index].replace(DATA_TYPES[pt])
319
- end
320
- }
321
- end
322
-
323
- if rtype.length > 1
324
- rtype.replace(DATA_TYPES[rtype])
325
- end
326
-
327
- @function_name = func
328
- @prototype = proto
329
- @return_type = rtype == 'B' ? 'I' : rtype
330
- @dll_name = dll
331
- @boolean = rtype == 'B' ? true : false
332
-
333
- @api = Win32::API.new(func, proto, rtype, dll)
334
-
335
- api_a = nil
336
- api_w = nil
337
-
338
- # If the auto_unicode option is set, and the func is not already
339
- # an explicit ANSI or Wide function name, generate Win32::API
340
- # objects for those functions as well. Ignore errors because not
341
- # all functions have explicit ANSI or Wide character implementations.
342
- #
343
- # This entire bit of logic is skipped if the DLL is msvcrt, since
344
- # msvcrt functions never have explicit ANSI or Wide character
345
- # versions.
346
- #
347
- if Windows::API.auto_unicode && dll !~ /msvcr/i
348
- begin
349
- unless ['A', 'W'].include?(func[-1].chr)
350
- api_a = Win32::API.new("#{func}A", proto, rtype, dll)
351
- end
352
- rescue RuntimeError
353
- end
354
-
355
- begin
356
- unless ['W', 'A'].include?(func[-1].chr)
357
- api_w = Win32::API.new("#{func}W", proto, rtype, dll)
358
- end
359
- rescue RuntimeError
360
- end
361
- end
362
-
363
- func_upper = nil
364
-
365
- # Automatically define a constant matching the function name if the
366
- # auto_constant option is set. Lower case method names will have a
367
- # capitalized equivalent created, e.g. Memcpy for memcpy, etc.
368
- #
369
- if Windows::API.auto_constant && Windows::API.auto_namespace
370
- if Windows::API.auto_namespace != 'Windows'
371
- namespace = class_for(Windows::API.auto_namespace)
372
- else
373
- namespace = Windows::API.auto_namespace
374
- end
375
-
376
- # Convert e.g. 'strstr' to 'Strstr' as an equivalent constant
377
- if ('A'..'Z').include?(func[0].chr)
378
- namespace.const_set(func, @api)
379
- else
380
- func_upper = func.dup
381
- if func_upper[0].chr == '_'
382
- func_upper = func_upper[1, func_upper.length]
383
- end
384
- func_upper[0, 1] = func_upper[0].chr.capitalize
385
- namespace.const_set(func_upper, @api)
386
- end
387
-
388
- # Automatically define the explicit ANSI and Unicode functions
389
- # as constants as well, if appropriate.
390
- #
391
- if Windows::API.auto_unicode
392
- namespace.const_set("#{func}A", api_a) if api_a
393
- namespace.const_set("#{func}W", api_w) if api_w
394
- end
395
- end
396
-
397
- # Automatically define a method in the auto_namespace if the
398
- # auto_method option is set. The explicit ANSI and Unicode methods
399
- # are added as well if the auto_unicode option is set to true.
400
- #
401
- if Windows::API.auto_method && Windows::API.auto_namespace
402
- if proto == 'V'
403
- proto = ''
404
- else
405
- n = 0
406
- if proto.is_a?(String)
407
- proto = proto.split('').map{ |e|
408
- n += 1
409
- e.downcase + n.to_s
410
- }.join(', ')
411
- else
412
- proto = proto.map{ |e|
413
- n += 1
414
- e.downcase + n.to_s
415
- }.join(', ')
416
- end
417
- end
418
-
419
- # Use the upper case function equivalent if defined
420
- call_func = func_upper || func
421
-
422
- if @boolean
423
- string = <<-EOC
424
- module #{Windows::API.auto_namespace}
425
- def #{func}(#{proto})
426
- #{call_func}.call(#{proto}) != 0
427
- end
428
- EOC
429
-
430
- if api_a
431
- string << %Q{
432
- def #{func}A(#{proto})
433
- #{call_func}A.call(#{proto}) != 0
434
- end
435
- }
436
- end
437
-
438
- if api_w
439
- string << %Q{
440
- def #{func}W(#{proto})
441
- #{call_func}W.call(#{proto}) != 0
442
- end
443
- }
444
- end
445
-
446
- string << 'end'
447
- else
448
- string = <<-EOC
449
- module #{Windows::API.auto_namespace}
450
- def #{func}(#{proto})
451
- #{call_func}.call(#{proto})
452
- end
453
- EOC
454
-
455
- if api_a
456
- string << %Q{
457
- def #{func}A(#{proto})
458
- #{call_func}A.call(#{proto})
459
- end
460
- }
461
- end
462
-
463
- if api_w
464
- string << %Q{
465
- def #{func}W(#{proto})
466
- #{call_func}W.call(#{proto})
467
- end
468
- }
469
- end
470
-
471
- # Create aliases for methods with an underscore that do not
472
- # require an underscore, e.g. umask and _umask.
473
- if func[0].chr == '_'
474
- func_alias = func[1, func.length]
475
- string << "alias #{func_alias} #{func}\n"
476
- end
477
-
478
- string << 'end'
479
- end
480
-
481
- eval(string)
482
- end
483
- end
484
-
485
- # Calls the function name set in the constructor.
486
- #
487
- def call(*args)
488
- @api.call(*args)
489
- end
490
-
491
- private
492
-
493
- # Get a module's namespace. This is basically the equivalent of
494
- # the rb_path2class() function from intern.h
495
- #
496
- def class_for(class_name)
497
- names = class_name.split("::")
498
- result = Object
499
- names.each{ |n| result = result.const_get(n) }
500
- result
501
- end
502
- end
503
- end
1
+ require 'win32/api'
2
+ require 'rbconfig'
3
+ require 'forwardable'
4
+ include RbConfig
5
+
6
+ # The Windows module serves as a namespace only
7
+ module Windows
8
+
9
+ # With Microsoft Visual C++ 8 and later users should use the associated
10
+ # DLL instead of msvcrt directly, if possible.
11
+ if RUBY_PLATFORM.split('-')[-1] == "ucrt"
12
+ MSVCRT_DLL = 'ucrtbase'
13
+ elsif CONFIG['host_os'].split('_')[1]
14
+ if CONFIG['host_os'].split('_')[1].to_i >= 80
15
+ MSVCRT_DLL = 'msvcr' + CONFIG['host_os'].split('_')[1]
16
+ else
17
+ MSVCRT_DLL = 'msvcrt'
18
+ end
19
+ else
20
+ MSVCRT_DLL = 'msvcrt'
21
+ end
22
+
23
+ # Wrapper around the Win32::API class
24
+ class API
25
+ extend Forwardable
26
+
27
+ # The version of the windows-api library
28
+ VERSION = '0.4.4'
29
+
30
+ # The methods from Win32::API are delegated to the appropriate object
31
+ def_delegators(:@api, :function_name, :dll_name, :prototype)
32
+ def_delegators(:@api, :return_type, :effective_function_name)
33
+
34
+ private
35
+
36
+ # Verbose data types that can be used instead of single letters
37
+ DATA_TYPES = {
38
+ 'ATOM' => 'I',
39
+ 'BOOL' => 'B',
40
+ 'BOOLEAN' => 'B',
41
+ 'BYTE' => 'I',
42
+ 'CALLBACK' => 'K',
43
+ 'CHAR' => 'I',
44
+ 'COLORREF' => 'L',
45
+ 'DWORD' => 'L',
46
+ 'DWORDLONG' => 'L',
47
+ 'DWORD_PTR' => 'P',
48
+ 'DWORD32' => 'I',
49
+ 'DWORD64' => 'L',
50
+ 'HACCEL' => 'L',
51
+ 'HANDLE' => 'L',
52
+ 'HBITMAP' => 'L',
53
+ 'HBRUSH' => 'L',
54
+ 'HCOLORSPACE' => 'L',
55
+ 'HCONV' => 'L',
56
+ 'HDC' => 'L',
57
+ 'HFILE' => 'I',
58
+ 'HFONT' => 'L',
59
+ 'HINSTANCE' => 'L',
60
+ 'HKEY' => 'L',
61
+ 'HLOCAL' => 'L',
62
+ 'HMENU' => 'L',
63
+ 'HMODULE' => 'L',
64
+ 'HRESULT' => 'L',
65
+ 'HWND' => 'L',
66
+ 'INT' => 'I',
67
+ 'INT_PTR' => 'P',
68
+ 'INT32' => 'I',
69
+ 'INT64' => 'L',
70
+ 'LANGID' => 'I',
71
+ 'LCID' => 'L',
72
+ 'LCTYPE' => 'L',
73
+ 'LONG' => 'L',
74
+ 'LONGLONG' => 'L',
75
+ 'LONG_PTR' => 'P',
76
+ 'LONG32' => 'L',
77
+ 'LONG64' => 'L',
78
+ 'LPARAM' => 'P',
79
+ 'LPBOOL' => 'P',
80
+ 'LPBYTE' => 'P',
81
+ 'LPCOLORREF' => 'P',
82
+ 'LPCSTR' => 'P',
83
+ 'LPCTSTR' => 'P',
84
+ 'LPCVOID' => 'L',
85
+ 'LPCWSTR' => 'P',
86
+ 'LPDWORD' => 'P',
87
+ 'LPHANDLE' => 'P',
88
+ 'LPINT' => 'P',
89
+ 'LPLONG' => 'P',
90
+ 'LPSTR' => 'P',
91
+ 'LPTSTR' => 'P',
92
+ 'LPVOID' => 'L',
93
+ 'LPWORD' => 'P',
94
+ 'LPWSTR' => 'P',
95
+ 'LRESULT' => 'P',
96
+ 'PBOOL' => 'P',
97
+ 'PBOOLEAN' => 'P',
98
+ 'PBYTE' => 'P',
99
+ 'PHKEY' => 'P',
100
+ 'SC_HANDLE' => 'L',
101
+ 'SC_LOCK' => 'L',
102
+ 'SERVICE_STATUS_HANDLE' => 'L',
103
+ 'SHORT' => 'I',
104
+ 'SIZE_T' => 'P',
105
+ 'TCHAR' => 'L',
106
+ 'UINT' => 'I',
107
+ 'UINT_PTR' => 'P',
108
+ 'UINT32' => 'I',
109
+ 'UINT64' => 'L',
110
+ 'ULONG' => 'L',
111
+ 'ULONGLONG' => 'L',
112
+ 'ULONG_PTR' => 'P',
113
+ 'ULONG32' => 'L',
114
+ 'ULONG64' => 'L',
115
+ 'USHORT' => 'I',
116
+ 'USN' => 'L',
117
+ 'WINAPI' => 'L',
118
+ 'WORD' => 'I'
119
+ }
120
+
121
+ public
122
+
123
+ @auto_constant = false
124
+ @auto_method = false
125
+ @auto_unicode = false
126
+ @auto_namespace = nil
127
+
128
+ # Returns the value of the @auto_constant class instance variable. The
129
+ # default is nil, i.e. none. See the Windows::API.auto_constant=
130
+ # documentation for more information.
131
+ #
132
+ def self.auto_constant
133
+ @auto_constant
134
+ end
135
+
136
+ # Automatically sets a constant to match the function name.
137
+ #
138
+ # The standard practice for defining Windows::API objects is to use
139
+ # a constant that matches the function name. For example, this is a
140
+ # typical idiom:
141
+ #
142
+ # module Windows
143
+ # module File
144
+ # GetFileAttributes = API.new('GetFileAttributes', 'P','L')
145
+ # end
146
+ # end
147
+ #
148
+ # With the API.auto_constant value set to true you can avoid the
149
+ # assignment step and the matching constant name will be automatically
150
+ # set for you in the namespace defined in API.auto_namespace. In other
151
+ # words, this example is identical to the one above:
152
+ #
153
+ # module Windows
154
+ # module File
155
+ # API.auto_constant = true
156
+ # API.auto_namespace = 'Windows::File'
157
+ # API.new('GetFileAttributes', 'P', 'L')
158
+ # end
159
+ # end
160
+ #
161
+ # If the auto_constant class variable is set to true, but no
162
+ # auto_namespace is set, an error will be raised. Note that the
163
+ # namespace must refer to an existing module (not a class).
164
+ #--
165
+ # TODO: If there's a way to automatically grab the namespace internally,
166
+ # nesting and all, I'd love to know the solution.
167
+ #
168
+ def self.auto_constant=(bool)
169
+ @auto_constant = bool
170
+ end
171
+
172
+ # Returns the value of the auto_namespace class instance variable. Used
173
+ # in conjunction with API.auto_constant and/or API.auto_method.
174
+ #
175
+ def self.auto_namespace
176
+ @auto_namespace
177
+ end
178
+
179
+ # Sets the value of the auto_namespace class nstance variable. The
180
+ # default is nil, i.e. none. Use in conjunction with the auto_constant
181
+ # and/or auto_method class variables, this method will automatically set
182
+ # a constant and/or method in +namespace+ equal to the function name set
183
+ # in the constructor.
184
+ #
185
+ # The +namespace+ must refer to an existing module, not a class.
186
+ #
187
+ def self.auto_namespace=(namespace)
188
+ @auto_namespace = namespace
189
+ end
190
+
191
+ # Returns the value of the auto_method class instance variable. Used in
192
+ # conjunction with auto_unicode. See API.auto_method= for more
193
+ # information.
194
+ #
195
+ def self.auto_method
196
+ @auto_method
197
+ end
198
+
199
+ # If this option is set to true then a corresponding method is
200
+ # automatically generated when you create a new Windows::API object.
201
+ #
202
+ # For example, instead of doing this:
203
+ #
204
+ # module Windows
205
+ # module File
206
+ # GetFileAttributes = API.new('GetFileAttributes', 'P', 'L')
207
+ #
208
+ # def GetFileAttributes(x)
209
+ # GetFileAttributes.call(x)
210
+ # end
211
+ # end
212
+ # end
213
+ #
214
+ # You can do this, and have the method autogenerated for you.
215
+ #
216
+ # module Windows
217
+ # module File
218
+ # API.auto_namespace = 'Windows::File'
219
+ # API.auto_constant = true
220
+ # API.auto_method = true
221
+ # API.new('GetFileAttributes', 'P', 'L')
222
+ # end
223
+ # end
224
+ #
225
+ # include Windows::File
226
+ # GetFileAttributes('C:/test.txt') # vs. GetFileAttributes.call
227
+ #
228
+ # If the Windows::API object is declared to be a boolean in the
229
+ # constructor, then the method definition automatically includes a
230
+ # '!= 0' clause at the end of the call. That way, you can do
231
+ # 'if SomeMethod(x)' instead of having to do 'if SomeMethod(x) != 0',
232
+ # and it will do the right thing.
233
+ #
234
+ # If the API.auto_unicode option is also set to true, then you will
235
+ # get three method definitions. The standard function name, the explicit
236
+ # ANSI ('A') version and the explicit Unicode/wide version ('W'). The
237
+ # exception to this rule is that the explicit ANSI and Unicode methods
238
+ # will NOT be generated if the function passed to the constructor
239
+ # already ends with 'A' or 'W'.
240
+ #
241
+ def self.auto_method=(bool)
242
+ @auto_method = bool
243
+ end
244
+
245
+ # Returns the value of the auto_unicode class instance variable. This
246
+ # is used in conjunction with the auto_method and/or auto_constant class
247
+ # variables. Not significant if neither of those variables are set.
248
+ #
249
+ def self.auto_unicode
250
+ @auto_unicode
251
+ end
252
+
253
+ # If set to true, and the auto_constant variable is set, then the
254
+ # automatic constant generation will generate the explicit ANSI ('A')
255
+ # and Unicode/wide ('W') versions of the function passed to the
256
+ # constructor, if such versions exist. Likewise, if the the
257
+ # auto_method variable is set, then explicit ANSI and Unicode methods
258
+ # are generated.
259
+ #
260
+ # Here's a typical idiom:
261
+ #
262
+ # module Windows
263
+ # module Path
264
+ # API.auto_namespace = Windows::Path
265
+ # API.auto_constant = true
266
+ # API.new('shlwapi', 'PathAddBackslash', 'P', 'P')
267
+ # API.new('shlwapi', 'PathAddBackslashA', 'P', 'P')
268
+ # API.new('shlwapi', 'PathAddBackslashW', 'P', 'P')
269
+ # end
270
+ # end
271
+ #
272
+ # That can be reduced to this:
273
+ #
274
+ # module Windows
275
+ # module Path
276
+ # API.auto_namespace = Windows::Path
277
+ # API.auto_constant = true
278
+ # API.auto_unicode = true
279
+ # API.new('shlwapi', 'PathAddBackslash', 'P', 'P')
280
+ # end
281
+ # end
282
+ #
283
+ # This value is ignored if the function passed to the constructor
284
+ # already ends with an 'A' or 'W'.
285
+ #
286
+ def self.auto_unicode=(bool)
287
+ @auto_unicode = bool
288
+ end
289
+
290
+ # call-seq:
291
+ # API.new(func, proto='V', rtype='L', dll='kernel32')
292
+ #
293
+ # Creates and returns a new Windows::API object. The +func+ is the
294
+ # name of the Windows function.
295
+ #
296
+ # The +proto+ is the function prototype for +func+. This can be a
297
+ # string or an array of characters. The possible valid characters
298
+ # are 'I' (integer), 'B' (BOOL), 'L' (long), 'V' (void), or 'P' (pointer).
299
+ # The default is void ('V').
300
+ #
301
+ # The +rtype+ argument is the return type for the function. The valid
302
+ # characters are the same as for the +proto+. The default is long ('L').
303
+ #
304
+ # The 'B' (BOOL) return type is the same as 'I' (Integer). This is
305
+ # significant only if the API.auto_method option is set to true, in which
306
+ # case it alters the generated method definition slightly. See the
307
+ # API.auto_method= class method for more information.
308
+ #
309
+ # The +dll+ is the name of the DLL file that the function is exported
310
+ # from. The default is 'kernel32'.
311
+ #
312
+ # If the function cannot be found then an API::Error is raised (a subclass
313
+ # of RuntimeError).
314
+ #
315
+ def initialize(func, proto='V', rtype='L', dll='kernel32')
316
+ # Convert literal data types to values that win32-api understands
317
+ if proto.is_a?(Array)
318
+ proto.each_with_index{ |pt, index|
319
+ if pt.length > 1
320
+ proto[index].replace(DATA_TYPES[pt])
321
+ end
322
+ }
323
+ end
324
+
325
+ if rtype.length > 1
326
+ rtype.replace(DATA_TYPES[rtype])
327
+ end
328
+
329
+ @function_name = func
330
+ @prototype = proto
331
+ @return_type = rtype == 'B' ? 'I' : rtype
332
+ @dll_name = dll
333
+ @boolean = rtype == 'B' ? true : false
334
+
335
+ @api = Win32::API.new(func, proto, rtype, dll)
336
+
337
+ api_a = nil
338
+ api_w = nil
339
+
340
+ # If the auto_unicode option is set, and the func is not already
341
+ # an explicit ANSI or Wide function name, generate Win32::API
342
+ # objects for those functions as well. Ignore errors because not
343
+ # all functions have explicit ANSI or Wide character implementations.
344
+ #
345
+ # This entire bit of logic is skipped if the DLL is msvcrt, since
346
+ # msvcrt functions never have explicit ANSI or Wide character
347
+ # versions.
348
+ #
349
+ if Windows::API.auto_unicode && dll !~ /msvcr/i
350
+ begin
351
+ unless ['A', 'W'].include?(func[-1].chr)
352
+ api_a = Win32::API.new("#{func}A", proto, rtype, dll)
353
+ end
354
+ rescue RuntimeError
355
+ end
356
+
357
+ begin
358
+ unless ['W', 'A'].include?(func[-1].chr)
359
+ api_w = Win32::API.new("#{func}W", proto, rtype, dll)
360
+ end
361
+ rescue RuntimeError
362
+ end
363
+ end
364
+
365
+ func_upper = nil
366
+
367
+ # Automatically define a constant matching the function name if the
368
+ # auto_constant option is set. Lower case method names will have a
369
+ # capitalized equivalent created, e.g. Memcpy for memcpy, etc.
370
+ #
371
+ if Windows::API.auto_constant && Windows::API.auto_namespace
372
+ if Windows::API.auto_namespace != 'Windows'
373
+ namespace = class_for(Windows::API.auto_namespace)
374
+ else
375
+ namespace = Windows::API.auto_namespace
376
+ end
377
+
378
+ # Convert e.g. 'strstr' to 'Strstr' as an equivalent constant
379
+ if ('A'..'Z').include?(func[0].chr)
380
+ namespace.const_set(func, @api)
381
+ else
382
+ func_upper = func.dup
383
+ if func_upper[0].chr == '_'
384
+ func_upper = func_upper[1, func_upper.length]
385
+ end
386
+ func_upper[0, 1] = func_upper[0].chr.capitalize
387
+ namespace.const_set(func_upper, @api)
388
+ end
389
+
390
+ # Automatically define the explicit ANSI and Unicode functions
391
+ # as constants as well, if appropriate.
392
+ #
393
+ if Windows::API.auto_unicode
394
+ namespace.const_set("#{func}A", api_a) if api_a
395
+ namespace.const_set("#{func}W", api_w) if api_w
396
+ end
397
+ end
398
+
399
+ # Automatically define a method in the auto_namespace if the
400
+ # auto_method option is set. The explicit ANSI and Unicode methods
401
+ # are added as well if the auto_unicode option is set to true.
402
+ #
403
+ if Windows::API.auto_method && Windows::API.auto_namespace
404
+ if proto == 'V'
405
+ proto = ''
406
+ else
407
+ n = 0
408
+ if proto.is_a?(String)
409
+ proto = proto.split('').map{ |e|
410
+ n += 1
411
+ e.downcase + n.to_s
412
+ }.join(', ')
413
+ else
414
+ proto = proto.map{ |e|
415
+ n += 1
416
+ e.downcase + n.to_s
417
+ }.join(', ')
418
+ end
419
+ end
420
+
421
+ # Use the upper case function equivalent if defined
422
+ call_func = func_upper || func
423
+
424
+ if @boolean
425
+ string = <<-EOC
426
+ module #{Windows::API.auto_namespace}
427
+ def #{func}(#{proto})
428
+ #{call_func}.call(#{proto}) != 0
429
+ end
430
+ EOC
431
+
432
+ if api_a
433
+ string << %Q{
434
+ def #{func}A(#{proto})
435
+ #{call_func}A.call(#{proto}) != 0
436
+ end
437
+ }
438
+ end
439
+
440
+ if api_w
441
+ string << %Q{
442
+ def #{func}W(#{proto})
443
+ #{call_func}W.call(#{proto}) != 0
444
+ end
445
+ }
446
+ end
447
+
448
+ string << 'end'
449
+ else
450
+ string = <<-EOC
451
+ module #{Windows::API.auto_namespace}
452
+ def #{func}(#{proto})
453
+ #{call_func}.call(#{proto})
454
+ end
455
+ EOC
456
+
457
+ if api_a
458
+ string << %Q{
459
+ def #{func}A(#{proto})
460
+ #{call_func}A.call(#{proto})
461
+ end
462
+ }
463
+ end
464
+
465
+ if api_w
466
+ string << %Q{
467
+ def #{func}W(#{proto})
468
+ #{call_func}W.call(#{proto})
469
+ end
470
+ }
471
+ end
472
+
473
+ # Create aliases for methods with an underscore that do not
474
+ # require an underscore, e.g. umask and _umask.
475
+ if func[0].chr == '_'
476
+ func_alias = func[1, func.length]
477
+ string << "alias #{func_alias} #{func}\n"
478
+ end
479
+
480
+ string << 'end'
481
+ end
482
+
483
+ eval(string)
484
+ end
485
+ end
486
+
487
+ # Calls the function name set in the constructor.
488
+ #
489
+ def call(*args)
490
+ @api.call(*args)
491
+ end
492
+
493
+ private
494
+
495
+ # Get a module's namespace. This is basically the equivalent of
496
+ # the rb_path2class() function from intern.h
497
+ #
498
+ def class_for(class_name)
499
+ names = class_name.split("::")
500
+ result = Object
501
+ names.each{ |n| result = result.const_get(n) }
502
+ result
503
+ end
504
+ end
505
+ end