Class: Windows::API

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/windows/api.rb

Overview

Wrapper around the Win32::API class

Constant Summary collapse

VERSION =

The version of the windows-api library

'0.4.0'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(func, proto = 'V', rtype = 'L', dll = 'kernel32') ⇒ API

call-seq:

API.new(func, proto='V', rtype='L', dll='kernel32')

Creates and returns a new Windows::API object. The func is the name of the Windows function.

The proto is the function prototype for func. This can be a string or an array of characters. The possible valid characters are ‘I’ (integer), ‘B’ (BOOL), ‘L’ (long), ‘V’ (void), or ‘P’ (pointer). The default is void (‘V’).

The rtype argument is the return type for the function. The valid characters are the same as for the proto. The default is long (‘L’).

The ‘B’ (BOOL) return type is the same as ‘I’ (Integer). This is significant only if the API.auto_method option is set to true, in which case it alters the generated method definition slightly. See the API.auto_method= class method for more information.

The dll is the name of the DLL file that the function is exported from. The default is ‘kernel32’.

If the function cannot be found then an API::Error is raised (a subclass of RuntimeError).



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/windows/api.rb', line 316

def initialize(func, proto='V', rtype='L', dll='kernel32') 
   # Convert literal data types to values that win32-api understands
   if proto.is_a?(Array)
      proto.each_with_index{ |pt, index|
         if pt.length > 1
            proto[index].replace(DATA_TYPES[pt])
         end
      }
   end
   
   if rtype.length > 1
      rtype.replace(DATA_TYPES[rtype])
   end
   
   @function_name = func
   @prototype     = proto
   @return_type   = rtype == 'B' ? 'I' : rtype
   @dll_name      = dll
   @boolean       = rtype == 'B' ? true : false         
   
   @api = Win32::API.new(func, proto, rtype, dll)
   
   api_a = nil
   api_w = nil
   
   # If the auto_unicode option is set, and the func is not already
   # an explicit ANSI or Wide function name, generate Win32::API
   # objects for those functions as well. Ignore errors because not
   # all functions have explicit ANSI or Wide character implementations.
   #
   # This entire bit of logic is skipped if the DLL is msvcrt, since
   # msvcrt functions never have explicit ANSI or Wide character
   # versions.
   # 
   if Windows::API.auto_unicode && dll !~ /msvcr/i
      begin
         unless ['A', 'W'].include?(func[-1].chr)
            api_a = Win32::API.new("#{func}A", proto, rtype, dll)
         end
      rescue RuntimeError
      end

      begin
         unless ['W', 'A'].include?(func[-1].chr)
            api_w = Win32::API.new("#{func}W", proto, rtype, dll)
         end
      rescue RuntimeError
      end
   end

   func_upper = nil

   # Automatically define a constant matching the function name if the
   # auto_constant option is set. Lower case method names will have a
   # capitalized equivalent created, e.g. Memcpy for memcpy, etc.
   # 
   if Windows::API.auto_constant && Windows::API.auto_namespace
      if Windows::API.auto_namespace != 'Windows'
         namespace = class_for(Windows::API.auto_namespace)
      else
         namespace = Windows::API.auto_namespace
      end

      # Convert e.g. 'strstr' to 'Strstr' as an equivalent constant
      if ('A'..'Z').include?(func[0].chr)
         namespace.const_set(func, @api)
      else
         func_upper = func.dup
         if func_upper[0].chr == '_'
            func_upper = func_upper[1, func_upper.length]
         end
         func_upper[0, 1] = func_upper[0].chr.capitalize
         namespace.const_set(func_upper, @api)
      end
      
      # Automatically define the explicit ANSI and Unicode functions
      # as constants as well, if appropriate.
      # 
      if Windows::API.auto_unicode
         namespace.const_set("#{func}A", api_a) if api_a
         namespace.const_set("#{func}W", api_w) if api_w
      end
   end
   
   # Automatically define a method in the auto_namespace if the
   # auto_method option is set. The explicit ANSI and Unicode methods
   # are added as well if the auto_unicode option is set to true.
   # 
   if Windows::API.auto_method && Windows::API.auto_namespace
      if proto == 'V'
         proto = ''
      else
         n = 0
         if proto.is_a?(String)
            proto = proto.split('').map{ |e|
               n += 1
               e.downcase + n.to_s
            }.join(', ')
         else
            proto = proto.map{ |e|
               n += 1
               e.downcase + n.to_s
            }.join(', ')
         end
      end

      # Use the upper case function equivalent if defined
      call_func = func_upper || func

      if @boolean
         string = <<-EOC
            module #{Windows::API.auto_namespace} 
               def #{func}(#{proto})
                  #{call_func}.call(#{proto}) != 0
               end
            EOC
            
         if api_a
            string << %Q{
               def #{func}A(#{proto})
                  #{call_func}A.call(#{proto}) != 0
               end
            }
         end
         
         if api_w
            string << %Q{
               def #{func}W(#{proto})
                  #{call_func}W.call(#{proto}) != 0
               end
            }
         end
         
         string << 'end'
      else
         string = <<-EOC
            module #{Windows::API.auto_namespace}
               def #{func}(#{proto})
                  #{call_func}.call(#{proto})
               end
            EOC

         if api_a
            string << %Q{
               def #{func}A(#{proto})
                  #{call_func}A.call(#{proto})
               end
            }
         end
         
         if api_w
            string << %Q{
               def #{func}W(#{proto})
                  #{call_func}W.call(#{proto})
               end
            }
         end

         # Create aliases for methods with an underscore that do not
         # require an underscore, e.g. umask and _umask.
         if func[0].chr == '_'
            func_alias = func[1, func.length]
            string << "alias #{func_alias} #{func}\n"
         end
         
         string << 'end'
      end
      
      eval(string)
   end
end

Class Method Details

.auto_constantObject

Returns the value of the @auto_constant class instance variable. The default is nil, i.e. none. See the Windows::API.auto_constant= documentation for more information.



133
134
135
# File 'lib/windows/api.rb', line 133

def self.auto_constant
   @auto_constant
end

.auto_constant=(bool) ⇒ Object

Automatically sets a constant to match the function name.

The standard practice for defining Windows::API objects is to use a constant that matches the function name. For example, this is a typical idiom:

module Windows
   module File
      GetFileAttributes = API.new('GetFileAttributes', 'P','L')
   end
end

With the API.auto_constant value set to true you can avoid the assignment step and the matching constant name will be automatically set for you in the namespace defined in API.auto_namespace. In other words, this example is identical to the one above:

module Windows
   module File
      API.auto_constant  = true
      API.auto_namespace = 'Windows::File'
      API.new('GetFileAttributes', 'P', 'L')
   end
end

If the auto_constant class variable is set to true, but no auto_namespace is set, an error will be raised. Note that the namespace must refer to an existing module (not a class). – TODO: If there’s a way to automatically grab the namespace internally, nesting and all, I’d love to know the solution.



169
170
171
# File 'lib/windows/api.rb', line 169

def self.auto_constant=(bool)
   @auto_constant = bool
end

.auto_methodObject

Returns the value of the auto_method class instance variable. Used in conjunction with auto_unicode. See API.auto_method= for more information.



196
197
198
# File 'lib/windows/api.rb', line 196

def self.auto_method
   @auto_method
end

.auto_method=(bool) ⇒ Object

If this option is set to true then a corresponding method is automatically generated when you create a new Windows::API object.

For example, instead of doing this:

module Windows
   module File
      GetFileAttributes = API.new('GetFileAttributes', 'P', 'L')

      def GetFileAttributes(x)
         GetFileAttributes.call(x)
      end
   end
end

You can do this, and have the method autogenerated for you.

module Windows
   module File
      API.auto_namespace = 'Windows::File'
      API.auto_constant  = true
      API.auto_method    = true
      API.new('GetFileAttributes', 'P', 'L')
   end
end

include Windows::File
GetFileAttributes('C:/test.txt') # vs. GetFileAttributes.call

If the Windows::API object is declared to be a boolean in the constructor, then the method definition automatically includes a ‘!= 0’ clause at the end of the call. That way, you can do ‘if SomeMethod(x)’ instead of having to do ‘if SomeMethod(x) != 0’, and it will do the right thing.

If the API.auto_unicode option is also set to true, then you will get three method definitions. The standard function name, the explicit ANSI (‘A’) version and the explicit Unicode/wide version (‘W’). The exception to this rule is that the explicit ANSI and Unicode methods will NOT be generated if the function passed to the constructor already ends with ‘A’ or ‘W’.



242
243
244
# File 'lib/windows/api.rb', line 242

def self.auto_method=(bool)
   @auto_method = bool
end

.auto_namespaceObject

Returns the value of the auto_namespace class instance variable. Used in conjunction with API.auto_constant and/or API.auto_method.



176
177
178
# File 'lib/windows/api.rb', line 176

def self.auto_namespace
   @auto_namespace
end

.auto_namespace=(namespace) ⇒ Object

Sets the value of the auto_namespace class nstance variable. The default is nil, i.e. none. Use in conjunction with the auto_constant and/or auto_method class variables, this method will automatically set a constant and/or method in namespace equal to the function name set in the constructor.

The namespace must refer to an existing module, not a class.



188
189
190
# File 'lib/windows/api.rb', line 188

def self.auto_namespace=(namespace)
   @auto_namespace = namespace
end

.auto_unicodeObject

Returns the value of the auto_unicode class instance variable. This is used in conjunction with the auto_method and/or auto_constant class variables. Not significant if neither of those variables are set.



250
251
252
# File 'lib/windows/api.rb', line 250

def self.auto_unicode
   @auto_unicode
end

.auto_unicode=(bool) ⇒ Object

If set to true, and the auto_constant variable is set, then the automatic constant generation will generate the explicit ANSI (‘A’) and Unicode/wide (‘W’) versions of the function passed to the constructor, if such versions exist. Likewise, if the the auto_method variable is set, then explicit ANSI and Unicode methods are generated.

Here’s a typical idiom:

module Windows

module Path
   API.auto_namespace = Windows::Path
   API.auto_constant = true
   API.new('shlwapi', 'PathAddBackslash', 'P', 'P')
   API.new('shlwapi', 'PathAddBackslashA', 'P', 'P')
   API.new('shlwapi', 'PathAddBackslashW', 'P', 'P')
end

end

That can be reduced to this:

module Windows

module Path
   API.auto_namespace = Windows::Path
   API.auto_constant = true
   API.auto_unicode  = true
   API.new('shlwapi', 'PathAddBackslash', 'P', 'P')
end

end

This value is ignored if the function passed to the constructor already ends with an ‘A’ or ‘W’.



287
288
289
# File 'lib/windows/api.rb', line 287

def self.auto_unicode=(bool)
   @auto_unicode = bool
end

Instance Method Details

#call(*args) ⇒ Object

Calls the function name set in the constructor.



490
491
492
# File 'lib/windows/api.rb', line 490

def call(*args)
   @api.call(*args)
end