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.4'

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).



315
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
# File 'lib/windows/api.rb', line 315

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.



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

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.



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

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.



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

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’.



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

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.



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

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.



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

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.



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

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’.



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

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

Instance Method Details

#call(*args) ⇒ Object

Calls the function name set in the constructor.



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

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