windows-api 0.4.4 → 0.4.5

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/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