lib/win/library.rb in win-0.1.27 vs lib/win/library.rb in win-0.3.1
- old
+ new
@@ -1,464 +1,464 @@
-require 'ffi'
-require 'win/extensions'
-
-# Related Windows API functions are grouped by topic and defined in separate namespaces (modules),
-# that also contain related constants and convenience methods. For example, Win::DDE module
-# contains only functions related to DDE protocol such as DdeInitialize() as well as constants
-# such as DMLERR_NO_ERROR, APPCLASS_STANDARD, etc. So if you need only DDE-related functions,
-# there is no need to load all the other modules, clogging your namespaces - just <b> require 'win/dde' </b>
-# and be done with it. Win is just a top level namespace (container) that holds all the other modules.
-#
-module Win
-
- module Errors # :nodoc:
- class NotFoundError < NameError # :nodoc:
- def initialize(name=nil, libs=nil)
- super %Q[Function #{name ? "'#{name}' ": ""}not found#{libs ? " in #{libs}" : ""}"]
- end
- end
- end
-
- # WIN::Library is a module that extends FFI::Library and is used to connect to Windows API functions
- # and wrap them into Ruby methods using 'function' declaration. If you do not see your favorite Windows
- # API functions among those already defined, you can easily 'include Win::Library’ into your module
- # and declare them using ‘function’ class method (macro) - it does a lot of heavy lifting for you and
- # can be customized with options and code blocks to give you reusable API wrapper methods with the exact
- # behavior you need.
- #
- module Library
-
- # Win::Library::API is a wrapper for callable function API object that mimics Win32::API
- class API
-
- # The name of the DLL(s) that export this API function
- attr_reader :dll_name
-
- # Ruby namespace (module) where this API function is attached
- attr_reader :namespace
-
- # The name of the function passed to the constructor
- attr_reader :function_name
-
- # The name of the actual Windows API function. For example, if you passed 'GetUserName' to the
- # constructor, then the effective function name would be either 'GetUserNameA' or 'GetUserNameW'.
- attr_accessor :effective_function_name
-
- # The prototype, returned as an array of FFI types
- attr_reader :prototype
-
- # The return type (:void for no return value)
- attr_reader :return_type
-
- def initialize( namespace, function_name, effective_function_name, prototype, return_type, dll_name )
- @namespace = namespace
- @function_name = function_name
- @effective_function_name = effective_function_name
- @prototype = prototype
- @return_type = return_type
- @dll_name = dll_name
- end
-
- # Calls underlying CamelCase Windows API function with supplied args
- def call( *args )
- @namespace.send(@function_name.to_sym, *args)
- end
-
- # alias_method :[], :call
- end
-
- # Contains class methods (macros) that can be used in any module mixing in Win::Library
- module ClassMethods
-
- # Mapping of Windows API types and one-letter shortcuts into FFI types.
- # Like :ATOM => :ushort, :LPARAM => :long, :c => :char, :i => :int
- TYPES = {
- # FFI type shortcuts
- C: :uchar, #– 8-bit unsigned character (byte)
- c: :char, # 8-bit character (byte)
- # :int8 – 8-bit signed integer
- # :uint8 – 8-bit unsigned integer
- S: :ushort, # – 16-bit unsigned integer (Win32/API: S used for string params)
- s: :short, # – 16-bit signed integer
- # :uint16 – 16-bit unsigned integer
- # :int16 – 16-bit signed integer
- I: :uint, # 32-bit unsigned integer
- i: :int, # 32-bit signed integer
- # :uint32 – 32-bit unsigned integer
- # :int32 – 32-bit signed integer
- L: :ulong, # unsigned long int – platform-specific size
- l: :long, # long int – platform-specific size. For discussion of platforms, see:
- # (http://groups.google.com/group/ruby-ffi/browse_thread/thread/4762fc77130339b1)
- # :int64 – 64-bit signed integer
- # :uint64 – 64-bit unsigned integer
- # :long_long – 64-bit signed integer
- # :ulong_long – 64-bit unsigned integer
- F: :float, # 32-bit floating point
- D: :double, # 64-bit floating point (double-precision)
- P: :pointer, # pointer – platform-specific size
- p: :string, # C-style (NULL-terminated) character string (Win32API: S)
- B: :bool, # (?? 1 byte in C++)
- V: :void, # For functions that return nothing (return type void).
- v: :void, # For functions that return nothing (return type void).
- # For function argument type only:
- # :buffer_in – Similar to :pointer, but optimized for Buffers that the function can only read (not write).
- # :buffer_out – Similar to :pointer, but optimized for Buffers that the function can only write (not read).
- # :buffer_inout – Similar to :pointer, but may be optimized for Buffers.
- # :varargs – Variable arguments
- # :enum - Enumerable type (should be defined)
- # :char_array - ??
-
- # Windows-specific type defs (ms-help://MS.MSDNQTR.v90.en/winprog/winprog/windows_data_types.htm):
- ATOM: :ushort, # Atom ~= Symbol: Atom table stores strings and corresponding identifiers. Application
- # places a string in an atom table and receives a 16-bit integer, called an atom, that
- # can be used to access the string. Placed string is called an atom name.
- # See: http://msdn.microsoft.com/en-us/library/ms648708%28VS.85%29.aspx
- BOOL: :bool,
- BOOLEAN: :bool,
- BYTE: :uchar, # Byte (8 bits). Declared as unsigned char
- #CALLBACK: K, # Win32.API gem-specific ?? MSDN: #define CALLBACK __stdcall
- CHAR: :char, # 8-bit Windows (ANSI) character. See http://msdn.microsoft.com/en-us/library/dd183415%28VS.85%29.aspx
- COLORREF: :uint32, # Red, green, blue (RGB) color value (32 bits). See COLORREF for more info.
- DWORD: :uint32, # 32-bit unsigned integer. The range is 0 through 4,294,967,295 decimal.
- DWORDLONG: :uint64, # 64-bit unsigned integer. The range is 0 through 18,446,744,073,709,551,615 decimal.
- DWORD_PTR: :ulong, # Unsigned long type for pointer precision. Use when casting a pointer to a long type
- # to perform pointer arithmetic. (Also commonly used for general 32-bit parameters that have
- # been extended to 64 bits in 64-bit Windows.) BaseTsd.h: #typedef ULONG_PTR DWORD_PTR;
- DWORD32: :uint32,
- DWORD64: :uint64,
- HALF_PTR: :int, # Half the size of a pointer. Use within a structure that contains a pointer and two small fields.
- # BaseTsd.h: #ifdef (_WIN64) typedef int HALF_PTR; #else typedef short HALF_PTR;
- HACCEL: :ulong, # (L) Handle to an accelerator table. WinDef.h: #typedef HANDLE HACCEL;
- # See http://msdn.microsoft.com/en-us/library/ms645526%28VS.85%29.aspx
- HANDLE: :ulong, # (L) Handle to an object. WinNT.h: #typedef PVOID HANDLE;
- # todo: Platform-dependent! Need to change to :uint64 for Win64
- HBITMAP: :ulong, # (L) Handle to a bitmap: http://msdn.microsoft.com/en-us/library/dd183377%28VS.85%29.aspx
- HBRUSH: :ulong, # (L) Handle to a brush. http://msdn.microsoft.com/en-us/library/dd183394%28VS.85%29.aspx
- HCOLORSPACE: :ulong, # (L) Handle to a color space. http://msdn.microsoft.com/en-us/library/ms536546%28VS.85%29.aspx
- HCURSOR: :ulong, # (L) Handle to a cursor. http://msdn.microsoft.com/en-us/library/ms646970%28VS.85%29.aspx
- HCONV: :ulong, # (L) Handle to a dynamic data exchange (DDE) conversation.
- HCONVLIST: :ulong, # (L) Handle to a DDE conversation list. HANDLE - L ?
- HDDEDATA: :ulong, # (L) Handle to DDE data (structure?)
- HDC: :ulong, # (L) Handle to a device context (DC). http://msdn.microsoft.com/en-us/library/dd183560%28VS.85%29.aspx
- HDESK: :ulong, # (L) Handle to a desktop. http://msdn.microsoft.com/en-us/library/ms682573%28VS.85%29.aspx
- HDROP: :ulong, # (L) Handle to an internal drop structure.
- HDWP: :ulong, # (L) Handle to a deferred window position structure.
- HENHMETAFILE: :ulong, #(L) Handle to an enhanced metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx
- HFILE: :uint, # (I) Special file handle to a file opened by OpenFile, not CreateFile.
- # WinDef.h: #typedef int HFILE;
- HFONT: :ulong, # (L) Handle to a font. http://msdn.microsoft.com/en-us/library/dd162470%28VS.85%29.aspx
- HGDIOBJ: :ulong, # (L) Handle to a GDI object.
- HGLOBAL: :ulong, # (L) Handle to a global memory block.
- HHOOK: :ulong, # (L) Handle to a hook. http://msdn.microsoft.com/en-us/library/ms632589%28VS.85%29.aspx
- HICON: :ulong, # (L) Handle to an icon. http://msdn.microsoft.com/en-us/library/ms646973%28VS.85%29.aspx
- HINSTANCE: :ulong, # (L) Handle to an instance. This is the base address of the module in memory.
- # HMODULE and HINSTANCE are the same today, but were different in 16-bit Windows.
- HKEY: :ulong, # (L) Handle to a registry key.
- HKL: :ulong, # (L) Input locale identifier.
- HLOCAL: :ulong, # (L) Handle to a local memory block.
- HMENU: :ulong, # (L) Handle to a menu. http://msdn.microsoft.com/en-us/library/ms646977%28VS.85%29.aspx
- HMETAFILE: :ulong, # (L) Handle to a metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx
- HMODULE: :ulong, # (L) Handle to an instance. Same as HINSTANCE today, but was different in 16-bit Windows.
- HMONITOR: :ulong, # (L) Рandle to a display monitor. WinDef.h: if(WINVER >= 0x0500) typedef HANDLE HMONITOR;
- HPALETTE: :ulong, # (L) Handle to a palette.
- HPEN: :ulong, # (L) Handle to a pen. http://msdn.microsoft.com/en-us/library/dd162786%28VS.85%29.aspx
- HRESULT: :long, # Return code used by COM interfaces. For more info, Structure of the COM Error Codes.
- # To test an HRESULT value, use the FAILED and SUCCEEDED macros.
- HRGN: :ulong, # (L) Handle to a region. http://msdn.microsoft.com/en-us/library/dd162913%28VS.85%29.aspx
- HRSRC: :ulong, # (L) Handle to a resource.
- HSZ: :ulong, # (L) Handle to a DDE string.
- HWINSTA: :ulong, # (L) Handle to a window station. http://msdn.microsoft.com/en-us/library/ms687096%28VS.85%29.aspx
- HWND: :ulong, # (L) Handle to a window. http://msdn.microsoft.com/en-us/library/ms632595%28VS.85%29.aspx
- INT: :int, # 32-bit signed integer. The range is -2147483648 through 2147483647 decimal.
- INT_PTR: :int, # Signed integer type for pointer precision. Use when casting a pointer to an integer
- # to perform pointer arithmetic. BaseTsd.h:
- #if defined(_WIN64) typedef __int64 INT_PTR; #else typedef int INT_PTR;
- INT32: :int32, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
- INT64: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
- LANGID: :ushort, # Language identifier. For more information, see Locales. WinNT.h: #typedef WORD LANGID;
- # See http://msdn.microsoft.com/en-us/library/dd318716%28VS.85%29.aspx
- LCID: :uint32, # Locale identifier. For more information, see Locales.
- LCTYPE: :uint32, # Locale information type. For a list, see Locale Information Constants.
- LGRPID: :uint32, # Language group identifier. For a list, see EnumLanguageGroupLocales.
- LONG: :long, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
- LONG32: :int32, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
- LONG64: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
- LONGLONG: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
- LONG_PTR: :long, # Signed long type for pointer precision. Use when casting a pointer to a long to
- # perform pointer arithmetic. BaseTsd.h:
- #if defined(_WIN64) typedef __int64 LONG_PTR; #else typedef long LONG_PTR;
- LPARAM: :long, # Message parameter. WinDef.h as follows: #typedef LONG_PTR LPARAM;
- LPBOOL: :pointer, # Pointer to a BOOL. WinDef.h as follows: #typedef BOOL far *LPBOOL;
- LPBYTE: :pointer, # Pointer to a BYTE. WinDef.h as follows: #typedef BYTE far *LPBYTE;
- LPCOLORREF: :pointer, # Pointer to a COLORREF value. WinDef.h as follows: #typedef DWORD *LPCOLORREF;
- LPCSTR: :pointer, # Pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters.
- # See Character Sets Used By Fonts. http://msdn.microsoft.com/en-us/library/dd183415%28VS.85%29.aspx
- LPCTSTR: :pointer, # An LPCWSTR if UNICODE is defined, an LPCSTR otherwise.
- LPCVOID: :pointer, # Pointer to a constant of any type. WinDef.h as follows: typedef CONST void *LPCVOID;
- LPCWSTR: :pointer, # Pointer to a constant null-terminated string of 16-bit Unicode characters.
- LPDWORD: :pointer, # Pointer to a DWORD. WinDef.h as follows: typedef DWORD *LPDWORD;
- LPHANDLE: :pointer, # Pointer to a HANDLE. WinDef.h as follows: typedef HANDLE *LPHANDLE;
- LPINT: :pointer, # Pointer to an INT.
- LPLONG: :pointer, # Pointer to an LONG.
- LPSTR: :pointer, # Pointer to a null-terminated string of 8-bit Windows (ANSI) characters.
- LPTSTR: :pointer, # An LPWSTR if UNICODE is defined, an LPSTR otherwise.
- LPVOID: :pointer, # Pointer to any type.
- LPWORD: :pointer, # Pointer to a WORD.
- LPWSTR: :pointer, # Pointer to a null-terminated string of 16-bit Unicode characters.
- LRESULT: :long, # Signed result of message processing. WinDef.h: typedef LONG_PTR LRESULT;
- PBOOL: :pointer, # Pointer to a BOOL.
- PBOOLEAN: :pointer, # Pointer to a BOOL.
- PBYTE: :pointer, # Pointer to a BYTE.
- PCHAR: :pointer, # Pointer to a CHAR.
- PCSTR: :pointer, # Pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters.
- PCTSTR: :pointer, # A PCWSTR if UNICODE is defined, a PCSTR otherwise.
- PCWSTR: :pointer, # Pointer to a constant null-terminated string of 16-bit Unicode characters.
- PDWORD: :pointer, # Pointer to a DWORD.
- PDWORDLONG: :pointer, # Pointer to a DWORDLONG.
- PDWORD_PTR: :pointer, # Pointer to a DWORD_PTR.
- PDWORD32: :pointer, # Pointer to a DWORD32.
- PDWORD64: :pointer, # Pointer to a DWORD64.
- PFLOAT: :pointer, # Pointer to a FLOAT.
- PHALF_PTR: :pointer, # Pointer to a HALF_PTR.
- PHANDLE: :pointer, # Pointer to a HANDLE.
- PHKEY: :pointer, # Pointer to an HKEY.
- PINT: :pointer, # Pointer to an INT.
- PINT_PTR: :pointer, # Pointer to an INT_PTR.
- PINT32: :pointer, # Pointer to an INT32.
- PINT64: :pointer, # Pointer to an INT64.
- PLCID: :pointer, # Pointer to an LCID.
- PLONG: :pointer, # Pointer to a LONG.
- PLONGLONG: :pointer, # Pointer to a LONGLONG.
- PLONG_PTR: :pointer, # Pointer to a LONG_PTR.
- PLONG32: :pointer, # Pointer to a LONG32.
- PLONG64: :pointer, # Pointer to a LONG64.
- POINTER_32: :pointer, # 32-bit pointer. On a 32-bit system, this is a native pointer. On a 64-bit system, this is a truncated 64-bit pointer.
- POINTER_64: :pointer, # 64-bit pointer. On a 64-bit system, this is a native pointer. On a 32-bit system, this is a sign-extended 32-bit pointer.
- POINTER_SIGNED: :pointer, # A signed pointer.
- POINTER_UNSIGNED: :pointer, # An unsigned pointer.
- PSHORT: :pointer, # Pointer to a SHORT.
- PSIZE_T: :pointer, # Pointer to a SIZE_T.
- PSSIZE_T: :pointer, # Pointer to a SSIZE_T.
- PSTR: :pointer, # Pointer to a null-terminated string of 8-bit Windows (ANSI) characters. For more information, see Character Sets Used By Fonts.
- PTBYTE: :pointer, # Pointer to a TBYTE.
- PTCHAR: :pointer, # Pointer to a TCHAR.
- PTSTR: :pointer, # A PWSTR if UNICODE is defined, a PSTR otherwise.
- PUCHAR: :pointer, # Pointer to a UCHAR.
- PUHALF_PTR: :pointer, # Pointer to a UHALF_PTR.
- PUINT: :pointer, # Pointer to a UINT.
- PUINT_PTR: :pointer, # Pointer to a UINT_PTR.
- PUINT32: :pointer, # Pointer to a UINT32.
- PUINT64: :pointer, # Pointer to a UINT64.
- PULONG: :pointer, # Pointer to a ULONG.
- PULONGLONG: :pointer, # Pointer to a ULONGLONG.
- PULONG_PTR: :pointer, # Pointer to a ULONG_PTR.
- PULONG32: :pointer, # Pointer to a ULONG32.
- PULONG64: :pointer, # Pointer to a ULONG64.
- PUSHORT: :pointer, # Pointer to a USHORT.
- PVOID: :pointer, # Pointer to any type.
- PWCHAR: :pointer, # Pointer to a WCHAR.
- PWORD: :pointer, # Pointer to a WORD.
- PWSTR: :pointer, # Pointer to a null- terminated string of 16-bit Unicode characters.
- # For more information, see Character Sets Used By Fonts.
- SC_HANDLE: :ulong, # (L) Handle to a service control manager database.
- # See SCM Handles http://msdn.microsoft.com/en-us/library/ms685104%28VS.85%29.aspx
- SC_LOCK: :pointer, # Lock to a service control manager database. For more information, see SCM Handles.
- SERVICE_STATUS_HANDLE: :ulong, # (L) Handle to a service status value. See SCM Handles.
- SHORT: :short, # A 16-bit integer. The range is –32768 through 32767 decimal.
- SIZE_T: :ulong, # The maximum number of bytes to which a pointer can point. Use for a count that must span the full range of a pointer.
- SSIZE_T: :long, # Signed SIZE_T.
- TBYTE: :short, # A WCHAR if UNICODE is defined, a CHAR otherwise.TCHAR:
- TCHAR: :short, # A WCHAR if UNICODE is defined, a CHAR otherwise.TCHAR:
- UCHAR: :uchar, # Unsigned CHAR (8 bit)
- UHALF_PTR: :uint, # Unsigned HALF_PTR. Use within a structure that contains a pointer and two small fields.
- UINT: :uint, # Unsigned INT. The range is 0 through 4294967295 decimal.
- UINT_PTR: :uint, # Unsigned INT_PTR.
- UINT32: :uint32, # Unsigned INT32. The range is 0 through 4294967295 decimal.
- UINT64: :uint64, # Unsigned INT64. The range is 0 through 18446744073709551615 decimal.
- ULONG: :ulong, # Unsigned LONG. The range is 0 through 4294967295 decimal.
- ULONGLONG: :ulong_long, # 64-bit unsigned integer. The range is 0 through 18446744073709551615 decimal.
- ULONG_PTR: :ulong, # Unsigned LONG_PTR.
- ULONG32: :uint32, # Unsigned INT32. The range is 0 through 4294967295 decimal.
- ULONG64: :uint64, # Unsigned LONG64. The range is 0 through 18446744073709551615 decimal.
- UNICODE_STRING: :pointer, # Pointer to some string structure??
- USHORT: :ushort, # Unsigned SHORT. The range is 0 through 65535 decimal.
- USN: :ulong_long, # Update sequence number (USN).
- VOID: [], # Any type ? Only use it to indicate no arguments or no return value
- WCHAR: :ushort, # 16-bit Unicode character. For more information, see Character Sets Used By Fonts.
- # In WinNT.h: typedef wchar_t WCHAR;
- #WINAPI: K, # Calling convention for system functions. WinDef.h: define WINAPI __stdcall
- WORD: :ushort, # 16-bit unsigned integer. The range is 0 through 65535 decimal.
- WPARAM: :uint # Message parameter. WinDef.h as follows: typedef UINT_PTR WPARAM;
- }
-
- ##
- # Defines new method wrappers for Windows API function call:
- # - Defines method with original (CamelCase) API function name and original signature (matches MSDN description)
- # - Defines method with snake_case name (converted from CamelCase function name) with enhanced API signature
- # When defined snake_case method is called, it converts the arguments you provided into ones required by
- # original API (adding defaults, mute and transitory args as necessary), executes API function call
- # and (optionally) transforms the result before returning it. If a block is attached to
- # method invocation, raw result is yielded to this block before final transformation take place
- # - Defines aliases for enhanced method with more Rubyesque names for getters, setters and tests:
- # GetWindowText -> window_test, SetWindowText -> window_text=, IsZoomed -> zoomed?
- # ---
- # You may modify default behavior of defined method by providing optional *def_block* to function definition.
- # If you do so, snake_case method is defined based on your *def_block*. It receives callable API
- # object for function being defined, arguments and (optional) runtime block with which the method
- # will be called. Results coming from &def_block are then transformed and returned.
- # So, your *def_block* should specify all the behavior of the method being defined. You can use *def_block* to:
- # - Change original signature of API function, provide argument defaults, check argument types
- # - Pack arguments into strings/structs for [in] or [in/out] parameters that expect a pointer
- # - Allocate buffers/structs for pointers required by API functions [out] parameters
- # - Unpack [out] and [in/out] parameters returned as pointers
- # - Explicitly return results of API call that are returned in [out] and [in/out] parameters
- # - Convert attached runtime blocks into callback functions and stuff them into [in] callback parameters
- # - do other stuff that you think is appropriate to make Windows API function behavior more Ruby-like...
- # ---
- # Accepts following options:
- # :dll:: Use this dll instead of default 'user32'
- # :rename:: Use this name instead of standard (conventional) function name
- # :alias(es):: Provides additional alias(es) for defined method
- # :boolean:: Forces method to return true/false instead of nonzero/zero
- # :zeronil:: Forces method to return nil if function result is zero
- #
- def function(name, params, returns, options={}, &def_block)
- snake_name, effective_names, aliases = generate_names(name, options)
- params, returns = generate_signature(params, returns)
- libs = ffi_libraries.map(&:name)
- boolean = options[:boolean]
- zeronil = options[:zeronil]
-
- effective_name = effective_names.inject(nil) do |func, effective_name|
- func || begin
- # tries to attach basic CamelCase method via FFI
- attach_function(name, effective_name, params.dup, returns)
- effective_name
- rescue FFI::NotFoundError
- nil
- end
- end
-
- raise Win::Errors::NotFoundError.new(name, libs) unless effective_name
-
- # Create API object that holds information about function names, params, etc
- api = API.new(namespace, name, effective_name, params, returns, libs)
-
- # Only define enhanced API if snake_name is different from original name (e.g. keybd_event)
- unless snake_name.to_s == name.to_s
- method_body = if def_block
- if zeronil
- ->(*args, &block){ (res = def_block.(api, *args, &block)) != 0 ? res : nil }
- elsif boolean
- ->(*args, &block){ def_block.(api, *args, &block) != 0 }
- else
- ->(*args, &block){ def_block.(api, *args, &block) }
- end
- else
- if zeronil
- ->(*args, &block){ (res = block ? block[api.call(*args)] : api.call(*args)) != 0 ? res : nil }
- elsif boolean
- ->(*args, &block){ block ? block[api.call(*args)] : api.call(*args) != 0 }
- else
- ->(*args, &block){ block ? block[api.call(*args)] : api.call(*args) }
- end
- end
-
- define_method snake_name, &method_body # define snake_case instance method
-
- eigenklass = class << self; self; end # Extracting eigenclass
- eigenklass.class_eval do
- define_method snake_name, &method_body # define snake_case class method
- end
- end
-
- aliases.each {|ali| alias_method ali, snake_name } # define aliases
- api #return api object from function declaration
- end
-
- # Try to define platform-specific function, rescue error, return message
- #
- def try_function(name, params, returns, options={}, &def_block)
- begin
- function name, params, returns, options={}, &def_block
- rescue Win::Errors::NotFoundError
- "This platform does not support function #{name}"
- end
- end
-
- # Generates possible effective names for function in Win32 dll (name+A/W),
- # Rubyesque name and aliases for method(s) defined based on function name,
- #
- def generate_names(name, options={})
- name = name.to_s
- effective_names = [name]
- effective_names += ["#{name}A", "#{name}W"] unless name =~ /[WA]$/
- aliases = ([options[:alias]] + [options[:aliases]]).flatten.compact
- snake_name = options[:rename] || name.snake_case
- case snake_name
- when /^is_/
- aliases << snake_name.sub(/^is_/, '') + '?'
- when /^set_/
- aliases << snake_name.sub(/^set_/, '')+ '='
- when /^get_/
- aliases << snake_name.sub(/^get_/, '')
- end
- [snake_name, effective_names, aliases]
- end
-
- ##
- # Generates params and returns (signature) containing only FFI-compliant types
- #
- def generate_signature(params, returns)
- params = params.split(//) if params.respond_to?(:split) # Convert params string into array
- params.map! {|param| TYPES[param.to_sym] || param} # Convert chars into FFI type symbols
- returns = TYPES[returns.to_sym] || returns # Convert chars into FFI type symbols
- [params, returns]
- end
-
- ##
- # Wrapper for FFI::Library#callback() that converts Win32/shortcut argument types into FFI-compliant types.
- # This method overrides FFI.callback which must be aliased to FFI.attach_callback
- #
- def callback(name, params, returns)
- params, returns = generate_signature(params, returns)
- attach_callback name.to_sym, params, returns
- end
-
- ##
- # :method: namespace
- # This method is meta-generated when Win::Library module is included into other module/class.
- # It returns reference to including (host) class/module for use by Win::Library::API and class methods.
-
- ##
- # Ensures that args count is equal to params count plus diff
- #
- def enforce_count(args, params, diff = 0)
- num_args = args.size
- num_params = params.size + diff #params == 'V' ? 0 : params.size + diff
- if num_args != num_params
- raise ArgumentError, "wrong number of arguments (#{num_args} for #{num_params})"
- end
- end
-
- end
-
- ##
- # Hook executed when Win::Library is included into class or module. It extends host class/module
- # with both FFI::Library methods and Win::Library macro methods like 'function'.
- #
- def self.included(klass)
- klass.extend FFI::Library
-
- eigenklass = class << klass; self; end # Extracting host class's eigenclass
- eigenklass.class_eval do
- define_method(:namespace) {klass} # Defining new class method for host pointing to itself
- alias_method :attach_callback, :callback
-
- include ClassMethods
- end
-
- klass.ffi_lib 'user32', 'kernel32' # Default libraries
- klass.ffi_convention :stdcall
- end
- end
+require 'ffi'
+require 'win/extensions'
+
+# Related Windows API functions are grouped by topic and defined in separate namespaces (modules),
+# that also contain related constants and convenience methods. For example, Win::Dde module
+# contains only functions related to DDE protocol such as DdeInitialize() as well as constants
+# such as DMLERR_NO_ERROR, APPCLASS_STANDARD, etc. So if you need only DDE-related functions,
+# there is no need to load all the other modules, clogging your namespaces - just <b> require 'win/dde' </b>
+# and be done with it. Win is just a top level namespace (container) that holds all the other modules.
+#
+module Win
+
+ module Errors # :nodoc:
+ class NotFoundError < NameError # :nodoc:
+ def initialize(name=nil, libs=nil)
+ super %Q[Function #{name ? "'#{name}' ": ""}not found#{libs ? " in #{libs}" : ""}"]
+ end
+ end
+ end
+
+ # WIN::Library is a module that extends FFI::Library and is used to connect to Windows API functions
+ # and wrap them into Ruby methods using 'function' declaration. If you do not see your favorite Windows
+ # API functions among those already defined, you can easily 'include Win::Library’ into your module
+ # and declare them using ‘function’ class method (macro) - it does a lot of heavy lifting for you and
+ # can be customized with options and code blocks to give you reusable API wrapper methods with the exact
+ # behavior you need.
+ #
+ module Library
+
+ # Win::Library::API is a wrapper for callable function API object that mimics Win32::API
+ class API
+
+ # The name of the DLL(s) that export this API function
+ attr_reader :dll_name
+
+ # Ruby namespace (module) where this API function is attached
+ attr_reader :namespace
+
+ # The name of the function passed to the constructor
+ attr_reader :function_name
+
+ # The name of the actual Windows API function. For example, if you passed 'GetUserName' to the
+ # constructor, then the effective function name would be either 'GetUserNameA' or 'GetUserNameW'.
+ attr_accessor :effective_function_name
+
+ # The prototype, returned as an array of FFI types
+ attr_reader :prototype
+
+ # The return type (:void for no return value)
+ attr_reader :return_type
+
+ def initialize( namespace, function_name, effective_function_name, prototype, return_type, dll_name )
+ @namespace = namespace
+ @function_name = function_name
+ @effective_function_name = effective_function_name
+ @prototype = prototype
+ @return_type = return_type
+ @dll_name = dll_name
+ end
+
+ # Calls underlying CamelCase Windows API function with supplied args
+ def call( *args )
+ @namespace.send(@function_name.to_sym, *args)
+ end
+
+ # alias_method :[], :call
+ end
+
+ # Contains class methods (macros) that can be used in any module mixing in Win::Library
+ module ClassMethods
+
+ # Mapping of Windows API types and one-letter shortcuts into FFI types.
+ # Like :ATOM => :ushort, :LPARAM => :long, :c => :char, :i => :int
+ TYPES = {
+ # FFI type shortcuts
+ C: :uchar, #– 8-bit unsigned character (byte)
+ c: :char, # 8-bit character (byte)
+ # :int8 – 8-bit signed integer
+ # :uint8 – 8-bit unsigned integer
+ S: :ushort, # – 16-bit unsigned integer (Win32/API: S used for string params)
+ s: :short, # – 16-bit signed integer
+ # :uint16 – 16-bit unsigned integer
+ # :int16 – 16-bit signed integer
+ I: :uint, # 32-bit unsigned integer
+ i: :int, # 32-bit signed integer
+ # :uint32 – 32-bit unsigned integer
+ # :int32 – 32-bit signed integer
+ L: :ulong, # unsigned long int – platform-specific size
+ l: :long, # long int – platform-specific size. For discussion of platforms, see:
+ # (http://groups.google.com/group/ruby-ffi/browse_thread/thread/4762fc77130339b1)
+ # :int64 – 64-bit signed integer
+ # :uint64 – 64-bit unsigned integer
+ # :long_long – 64-bit signed integer
+ # :ulong_long – 64-bit unsigned integer
+ F: :float, # 32-bit floating point
+ D: :double, # 64-bit floating point (double-precision)
+ P: :pointer, # pointer – platform-specific size
+ p: :string, # C-style (NULL-terminated) character string (Win32API: S)
+ B: :bool, # (?? 1 byte in C++)
+ V: :void, # For functions that return nothing (return type void).
+ v: :void, # For functions that return nothing (return type void).
+ # For function argument type only:
+ # :buffer_in – Similar to :pointer, but optimized for Buffers that the function can only read (not write).
+ # :buffer_out – Similar to :pointer, but optimized for Buffers that the function can only write (not read).
+ # :buffer_inout – Similar to :pointer, but may be optimized for Buffers.
+ # :varargs – Variable arguments
+ # :enum - Enumerable type (should be defined)
+ # :char_array - ??
+
+ # Windows-specific type defs (ms-help://MS.MSDNQTR.v90.en/winprog/winprog/windows_data_types.htm):
+ ATOM: :ushort, # Atom ~= Symbol: Atom table stores strings and corresponding identifiers. Application
+ # places a string in an atom table and receives a 16-bit integer, called an atom, that
+ # can be used to access the string. Placed string is called an atom name.
+ # See: http://msdn.microsoft.com/en-us/library/ms648708%28VS.85%29.aspx
+ BOOL: :bool,
+ BOOLEAN: :bool,
+ BYTE: :uchar, # Byte (8 bits). Declared as unsigned char
+ #CALLBACK: K, # Win32.API gem-specific ?? MSDN: #define CALLBACK __stdcall
+ CHAR: :char, # 8-bit Windows (ANSI) character. See http://msdn.microsoft.com/en-us/library/dd183415%28VS.85%29.aspx
+ COLORREF: :uint32, # Red, green, blue (RGB) color value (32 bits). See COLORREF for more info.
+ DWORD: :uint32, # 32-bit unsigned integer. The range is 0 through 4,294,967,295 decimal.
+ DWORDLONG: :uint64, # 64-bit unsigned integer. The range is 0 through 18,446,744,073,709,551,615 decimal.
+ DWORD_PTR: :ulong, # Unsigned long type for pointer precision. Use when casting a pointer to a long type
+ # to perform pointer arithmetic. (Also commonly used for general 32-bit parameters that have
+ # been extended to 64 bits in 64-bit Windows.) BaseTsd.h: #typedef ULONG_PTR DWORD_PTR;
+ DWORD32: :uint32,
+ DWORD64: :uint64,
+ HALF_PTR: :int, # Half the size of a pointer. Use within a structure that contains a pointer and two small fields.
+ # BaseTsd.h: #ifdef (_WIN64) typedef int HALF_PTR; #else typedef short HALF_PTR;
+ HACCEL: :ulong, # (L) Handle to an accelerator table. WinDef.h: #typedef HANDLE HACCEL;
+ # See http://msdn.microsoft.com/en-us/library/ms645526%28VS.85%29.aspx
+ HANDLE: :ulong, # (L) Handle to an object. WinNT.h: #typedef PVOID HANDLE;
+ # todo: Platform-dependent! Need to change to :uint64 for Win64
+ HBITMAP: :ulong, # (L) Handle to a bitmap: http://msdn.microsoft.com/en-us/library/dd183377%28VS.85%29.aspx
+ HBRUSH: :ulong, # (L) Handle to a brush. http://msdn.microsoft.com/en-us/library/dd183394%28VS.85%29.aspx
+ HCOLORSPACE: :ulong, # (L) Handle to a color space. http://msdn.microsoft.com/en-us/library/ms536546%28VS.85%29.aspx
+ HCURSOR: :ulong, # (L) Handle to a cursor. http://msdn.microsoft.com/en-us/library/ms646970%28VS.85%29.aspx
+ HCONV: :ulong, # (L) Handle to a dynamic data exchange (DDE) conversation.
+ HCONVLIST: :ulong, # (L) Handle to a DDE conversation list. HANDLE - L ?
+ HDDEDATA: :ulong, # (L) Handle to DDE data (structure?)
+ HDC: :ulong, # (L) Handle to a device context (DC). http://msdn.microsoft.com/en-us/library/dd183560%28VS.85%29.aspx
+ HDESK: :ulong, # (L) Handle to a desktop. http://msdn.microsoft.com/en-us/library/ms682573%28VS.85%29.aspx
+ HDROP: :ulong, # (L) Handle to an internal drop structure.
+ HDWP: :ulong, # (L) Handle to a deferred window position structure.
+ HENHMETAFILE: :ulong, #(L) Handle to an enhanced metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx
+ HFILE: :uint, # (I) Special file handle to a file opened by OpenFile, not CreateFile.
+ # WinDef.h: #typedef int HFILE;
+ HFONT: :ulong, # (L) Handle to a font. http://msdn.microsoft.com/en-us/library/dd162470%28VS.85%29.aspx
+ HGDIOBJ: :ulong, # (L) Handle to a GDI object.
+ HGLOBAL: :ulong, # (L) Handle to a global memory block.
+ HHOOK: :ulong, # (L) Handle to a hook. http://msdn.microsoft.com/en-us/library/ms632589%28VS.85%29.aspx
+ HICON: :ulong, # (L) Handle to an icon. http://msdn.microsoft.com/en-us/library/ms646973%28VS.85%29.aspx
+ HINSTANCE: :ulong, # (L) Handle to an instance. This is the base address of the module in memory.
+ # HMODULE and HINSTANCE are the same today, but were different in 16-bit Windows.
+ HKEY: :ulong, # (L) Handle to a registry key.
+ HKL: :ulong, # (L) Input locale identifier.
+ HLOCAL: :ulong, # (L) Handle to a local memory block.
+ HMENU: :ulong, # (L) Handle to a menu. http://msdn.microsoft.com/en-us/library/ms646977%28VS.85%29.aspx
+ HMETAFILE: :ulong, # (L) Handle to a metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx
+ HMODULE: :ulong, # (L) Handle to an instance. Same as HINSTANCE today, but was different in 16-bit Windows.
+ HMONITOR: :ulong, # (L) Рandle to a display monitor. WinDef.h: if(WINVER >= 0x0500) typedef HANDLE HMONITOR;
+ HPALETTE: :ulong, # (L) Handle to a palette.
+ HPEN: :ulong, # (L) Handle to a pen. http://msdn.microsoft.com/en-us/library/dd162786%28VS.85%29.aspx
+ HRESULT: :long, # Return code used by COM interfaces. For more info, Structure of the COM Error Codes.
+ # To test an HRESULT value, use the FAILED and SUCCEEDED macros.
+ HRGN: :ulong, # (L) Handle to a region. http://msdn.microsoft.com/en-us/library/dd162913%28VS.85%29.aspx
+ HRSRC: :ulong, # (L) Handle to a resource.
+ HSZ: :ulong, # (L) Handle to a DDE string.
+ HWINSTA: :ulong, # (L) Handle to a window station. http://msdn.microsoft.com/en-us/library/ms687096%28VS.85%29.aspx
+ HWND: :ulong, # (L) Handle to a window. http://msdn.microsoft.com/en-us/library/ms632595%28VS.85%29.aspx
+ INT: :int, # 32-bit signed integer. The range is -2147483648 through 2147483647 decimal.
+ INT_PTR: :int, # Signed integer type for pointer precision. Use when casting a pointer to an integer
+ # to perform pointer arithmetic. BaseTsd.h:
+ #if defined(_WIN64) typedef __int64 INT_PTR; #else typedef int INT_PTR;
+ INT32: :int32, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
+ INT64: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
+ LANGID: :ushort, # Language identifier. For more information, see Locales. WinNT.h: #typedef WORD LANGID;
+ # See http://msdn.microsoft.com/en-us/library/dd318716%28VS.85%29.aspx
+ LCID: :uint32, # Locale identifier. For more information, see Locales.
+ LCTYPE: :uint32, # Locale information type. For a list, see Locale Information Constants.
+ LGRPID: :uint32, # Language group identifier. For a list, see EnumLanguageGroupLocales.
+ LONG: :long, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
+ LONG32: :int32, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
+ LONG64: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
+ LONGLONG: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
+ LONG_PTR: :long, # Signed long type for pointer precision. Use when casting a pointer to a long to
+ # perform pointer arithmetic. BaseTsd.h:
+ #if defined(_WIN64) typedef __int64 LONG_PTR; #else typedef long LONG_PTR;
+ LPARAM: :long, # Message parameter. WinDef.h as follows: #typedef LONG_PTR LPARAM;
+ LPBOOL: :pointer, # Pointer to a BOOL. WinDef.h as follows: #typedef BOOL far *LPBOOL;
+ LPBYTE: :pointer, # Pointer to a BYTE. WinDef.h as follows: #typedef BYTE far *LPBYTE;
+ LPCOLORREF: :pointer, # Pointer to a COLORREF value. WinDef.h as follows: #typedef DWORD *LPCOLORREF;
+ LPCSTR: :pointer, # Pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters.
+ # See Character Sets Used By Fonts. http://msdn.microsoft.com/en-us/library/dd183415%28VS.85%29.aspx
+ LPCTSTR: :pointer, # An LPCWSTR if UNICODE is defined, an LPCSTR otherwise.
+ LPCVOID: :pointer, # Pointer to a constant of any type. WinDef.h as follows: typedef CONST void *LPCVOID;
+ LPCWSTR: :pointer, # Pointer to a constant null-terminated string of 16-bit Unicode characters.
+ LPDWORD: :pointer, # Pointer to a DWORD. WinDef.h as follows: typedef DWORD *LPDWORD;
+ LPHANDLE: :pointer, # Pointer to a HANDLE. WinDef.h as follows: typedef HANDLE *LPHANDLE;
+ LPINT: :pointer, # Pointer to an INT.
+ LPLONG: :pointer, # Pointer to an LONG.
+ LPSTR: :pointer, # Pointer to a null-terminated string of 8-bit Windows (ANSI) characters.
+ LPTSTR: :pointer, # An LPWSTR if UNICODE is defined, an LPSTR otherwise.
+ LPVOID: :pointer, # Pointer to any type.
+ LPWORD: :pointer, # Pointer to a WORD.
+ LPWSTR: :pointer, # Pointer to a null-terminated string of 16-bit Unicode characters.
+ LRESULT: :long, # Signed result of message processing. WinDef.h: typedef LONG_PTR LRESULT;
+ PBOOL: :pointer, # Pointer to a BOOL.
+ PBOOLEAN: :pointer, # Pointer to a BOOL.
+ PBYTE: :pointer, # Pointer to a BYTE.
+ PCHAR: :pointer, # Pointer to a CHAR.
+ PCSTR: :pointer, # Pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters.
+ PCTSTR: :pointer, # A PCWSTR if UNICODE is defined, a PCSTR otherwise.
+ PCWSTR: :pointer, # Pointer to a constant null-terminated string of 16-bit Unicode characters.
+ PDWORD: :pointer, # Pointer to a DWORD.
+ PDWORDLONG: :pointer, # Pointer to a DWORDLONG.
+ PDWORD_PTR: :pointer, # Pointer to a DWORD_PTR.
+ PDWORD32: :pointer, # Pointer to a DWORD32.
+ PDWORD64: :pointer, # Pointer to a DWORD64.
+ PFLOAT: :pointer, # Pointer to a FLOAT.
+ PHALF_PTR: :pointer, # Pointer to a HALF_PTR.
+ PHANDLE: :pointer, # Pointer to a HANDLE.
+ PHKEY: :pointer, # Pointer to an HKEY.
+ PINT: :pointer, # Pointer to an INT.
+ PINT_PTR: :pointer, # Pointer to an INT_PTR.
+ PINT32: :pointer, # Pointer to an INT32.
+ PINT64: :pointer, # Pointer to an INT64.
+ PLCID: :pointer, # Pointer to an LCID.
+ PLONG: :pointer, # Pointer to a LONG.
+ PLONGLONG: :pointer, # Pointer to a LONGLONG.
+ PLONG_PTR: :pointer, # Pointer to a LONG_PTR.
+ PLONG32: :pointer, # Pointer to a LONG32.
+ PLONG64: :pointer, # Pointer to a LONG64.
+ POINTER_32: :pointer, # 32-bit pointer. On a 32-bit system, this is a native pointer. On a 64-bit system, this is a truncated 64-bit pointer.
+ POINTER_64: :pointer, # 64-bit pointer. On a 64-bit system, this is a native pointer. On a 32-bit system, this is a sign-extended 32-bit pointer.
+ POINTER_SIGNED: :pointer, # A signed pointer.
+ POINTER_UNSIGNED: :pointer, # An unsigned pointer.
+ PSHORT: :pointer, # Pointer to a SHORT.
+ PSIZE_T: :pointer, # Pointer to a SIZE_T.
+ PSSIZE_T: :pointer, # Pointer to a SSIZE_T.
+ PSTR: :pointer, # Pointer to a null-terminated string of 8-bit Windows (ANSI) characters. For more information, see Character Sets Used By Fonts.
+ PTBYTE: :pointer, # Pointer to a TBYTE.
+ PTCHAR: :pointer, # Pointer to a TCHAR.
+ PTSTR: :pointer, # A PWSTR if UNICODE is defined, a PSTR otherwise.
+ PUCHAR: :pointer, # Pointer to a UCHAR.
+ PUHALF_PTR: :pointer, # Pointer to a UHALF_PTR.
+ PUINT: :pointer, # Pointer to a UINT.
+ PUINT_PTR: :pointer, # Pointer to a UINT_PTR.
+ PUINT32: :pointer, # Pointer to a UINT32.
+ PUINT64: :pointer, # Pointer to a UINT64.
+ PULONG: :pointer, # Pointer to a ULONG.
+ PULONGLONG: :pointer, # Pointer to a ULONGLONG.
+ PULONG_PTR: :pointer, # Pointer to a ULONG_PTR.
+ PULONG32: :pointer, # Pointer to a ULONG32.
+ PULONG64: :pointer, # Pointer to a ULONG64.
+ PUSHORT: :pointer, # Pointer to a USHORT.
+ PVOID: :pointer, # Pointer to any type.
+ PWCHAR: :pointer, # Pointer to a WCHAR.
+ PWORD: :pointer, # Pointer to a WORD.
+ PWSTR: :pointer, # Pointer to a null- terminated string of 16-bit Unicode characters.
+ # For more information, see Character Sets Used By Fonts.
+ SC_HANDLE: :ulong, # (L) Handle to a service control manager database.
+ # See SCM Handles http://msdn.microsoft.com/en-us/library/ms685104%28VS.85%29.aspx
+ SC_LOCK: :pointer, # Lock to a service control manager database. For more information, see SCM Handles.
+ SERVICE_STATUS_HANDLE: :ulong, # (L) Handle to a service status value. See SCM Handles.
+ SHORT: :short, # A 16-bit integer. The range is –32768 through 32767 decimal.
+ SIZE_T: :ulong, # The maximum number of bytes to which a pointer can point. Use for a count that must span the full range of a pointer.
+ SSIZE_T: :long, # Signed SIZE_T.
+ TBYTE: :short, # A WCHAR if UNICODE is defined, a CHAR otherwise.TCHAR:
+ TCHAR: :short, # A WCHAR if UNICODE is defined, a CHAR otherwise.TCHAR:
+ UCHAR: :uchar, # Unsigned CHAR (8 bit)
+ UHALF_PTR: :uint, # Unsigned HALF_PTR. Use within a structure that contains a pointer and two small fields.
+ UINT: :uint, # Unsigned INT. The range is 0 through 4294967295 decimal.
+ UINT_PTR: :uint, # Unsigned INT_PTR.
+ UINT32: :uint32, # Unsigned INT32. The range is 0 through 4294967295 decimal.
+ UINT64: :uint64, # Unsigned INT64. The range is 0 through 18446744073709551615 decimal.
+ ULONG: :ulong, # Unsigned LONG. The range is 0 through 4294967295 decimal.
+ ULONGLONG: :ulong_long, # 64-bit unsigned integer. The range is 0 through 18446744073709551615 decimal.
+ ULONG_PTR: :ulong, # Unsigned LONG_PTR.
+ ULONG32: :uint32, # Unsigned INT32. The range is 0 through 4294967295 decimal.
+ ULONG64: :uint64, # Unsigned LONG64. The range is 0 through 18446744073709551615 decimal.
+ UNICODE_STRING: :pointer, # Pointer to some string structure??
+ USHORT: :ushort, # Unsigned SHORT. The range is 0 through 65535 decimal.
+ USN: :ulong_long, # Update sequence number (USN).
+ VOID: [], # Any type ? Only use it to indicate no arguments or no return value
+ WCHAR: :ushort, # 16-bit Unicode character. For more information, see Character Sets Used By Fonts.
+ # In WinNT.h: typedef wchar_t WCHAR;
+ #WINAPI: K, # Calling convention for system functions. WinDef.h: define WINAPI __stdcall
+ WORD: :ushort, # 16-bit unsigned integer. The range is 0 through 65535 decimal.
+ WPARAM: :uint # Message parameter. WinDef.h as follows: typedef UINT_PTR WPARAM;
+ }
+
+ ##
+ # Defines new method wrappers for Windows API function call:
+ # - Defines method with original (CamelCase) API function name and original signature (matches MSDN description)
+ # - Defines method with snake_case name (converted from CamelCase function name) with enhanced API signature
+ # When defined snake_case method is called, it converts the arguments you provided into ones required by
+ # original API (adding defaults, mute and transitory args as necessary), executes API function call
+ # and (optionally) transforms the result before returning it. If a block is attached to
+ # method invocation, raw result is yielded to this block before final transformation take place
+ # - Defines aliases for enhanced method with more Rubyesque names for getters, setters and tests:
+ # GetWindowText -> window_test, SetWindowText -> window_text=, IsZoomed -> zoomed?
+ # ---
+ # You may modify default behavior of defined method by providing optional *def_block* to function definition.
+ # If you do so, snake_case method is defined based on your *def_block*. It receives callable API
+ # object for function being defined, arguments and (optional) runtime block with which the method
+ # will be called. Results coming from &def_block are then transformed and returned.
+ # So, your *def_block* should specify all the behavior of the method being defined. You can use *def_block* to:
+ # - Change original signature of API function, provide argument defaults, check argument types
+ # - Pack arguments into strings/structs for [in] or [in/out] parameters that expect a pointer
+ # - Allocate buffers/structs for pointers required by API functions [out] parameters
+ # - Unpack [out] and [in/out] parameters returned as pointers
+ # - Explicitly return results of API call that are returned in [out] and [in/out] parameters
+ # - Convert attached runtime blocks into callback functions and stuff them into [in] callback parameters
+ # - do other stuff that you think is appropriate to make Windows API function behavior more Ruby-like...
+ # ---
+ # Accepts following options:
+ # :dll:: Use this dll instead of default 'user32'
+ # :rename:: Use this name instead of standard (conventional) function name
+ # :alias(es):: Provides additional alias(es) for defined method
+ # :boolean:: Forces method to return true/false instead of nonzero/zero
+ # :zeronil:: Forces method to return nil if function result is zero
+ #
+ def function(name, params, returns, options={}, &def_block)
+ snake_name, effective_names, aliases = generate_names(name, options)
+ params, returns = generate_signature(params, returns)
+ libs = ffi_libraries.map(&:name)
+ boolean = options[:boolean]
+ zeronil = options[:zeronil]
+
+ effective_name = effective_names.inject(nil) do |func, effective_name|
+ func || begin
+ # tries to attach basic CamelCase method via FFI
+ attach_function(name, effective_name, params.dup, returns)
+ effective_name
+ rescue FFI::NotFoundError
+ nil
+ end
+ end
+
+ raise Win::Errors::NotFoundError.new(name, libs) unless effective_name
+
+ # Create API object that holds information about function names, params, etc
+ api = API.new(namespace, name, effective_name, params, returns, libs)
+
+ # Only define enhanced API if snake_name is different from original name (e.g. keybd_event)
+ unless snake_name.to_s == name.to_s
+ method_body = if def_block
+ if zeronil
+ ->(*args, &block){ (res = def_block.(api, *args, &block)) != 0 ? res : nil }
+ elsif boolean
+ ->(*args, &block){ def_block.(api, *args, &block) != 0 }
+ else
+ ->(*args, &block){ def_block.(api, *args, &block) }
+ end
+ else
+ if zeronil
+ ->(*args, &block){ (res = block ? block[api.call(*args)] : api.call(*args)) != 0 ? res : nil }
+ elsif boolean
+ ->(*args, &block){ block ? block[api.call(*args)] : api.call(*args) != 0 }
+ else
+ ->(*args, &block){ block ? block[api.call(*args)] : api.call(*args) }
+ end
+ end
+
+ define_method snake_name, &method_body # define snake_case instance method
+
+ eigenklass = class << self; self; end # Extracting eigenclass
+ eigenklass.class_eval do
+ define_method snake_name, &method_body # define snake_case class method
+ end
+ end
+
+ aliases.each {|ali| alias_method ali, snake_name } # define aliases
+ api #return api object from function declaration
+ end
+
+ # Try to define platform-specific function, rescue error, return message
+ #
+ def try_function(name, params, returns, options={}, &def_block)
+ begin
+ function name, params, returns, options={}, &def_block
+ rescue Win::Errors::NotFoundError
+ "This platform does not support function #{name}"
+ end
+ end
+
+ # Generates possible effective names for function in Win32 dll (name+A/W),
+ # Rubyesque name and aliases for method(s) defined based on function name,
+ #
+ def generate_names(name, options={})
+ name = name.to_s
+ effective_names = [name]
+ effective_names += ["#{name}A", "#{name}W"] unless name =~ /[WA]$/
+ aliases = ([options[:alias]] + [options[:aliases]]).flatten.compact
+ snake_name = options[:rename] || name.snake_case
+ case snake_name
+ when /^is_/
+ aliases << snake_name.sub(/^is_/, '') + '?'
+ when /^set_/
+ aliases << snake_name.sub(/^set_/, '')+ '='
+ when /^get_/
+ aliases << snake_name.sub(/^get_/, '')
+ end
+ [snake_name, effective_names, aliases]
+ end
+
+ ##
+ # Generates params and returns (signature) containing only FFI-compliant types
+ #
+ def generate_signature(params, returns)
+ params = params.split(//) if params.respond_to?(:split) # Convert params string into array
+ params.map! {|param| TYPES[param.to_sym] || param} # Convert chars into FFI type symbols
+ returns = TYPES[returns.to_sym] || returns # Convert chars into FFI type symbols
+ [params, returns]
+ end
+
+ ##
+ # Wrapper for FFI::Library#callback() that converts Win32/shortcut argument types into FFI-compliant types.
+ # This method overrides FFI.callback which must be aliased to FFI.attach_callback
+ #
+ def callback(name, params, returns)
+ params, returns = generate_signature(params, returns)
+ attach_callback name.to_sym, params, returns
+ end
+
+ ##
+ # :method: namespace
+ # This method is meta-generated when Win::Library module is included into other module/class.
+ # It returns reference to including (host) class/module for use by Win::Library::API and class methods.
+
+ ##
+ # Ensures that args count is equal to params count plus diff
+ #
+ def enforce_count(args, params, diff = 0)
+ num_args = args.size
+ num_params = params.size + diff #params == 'V' ? 0 : params.size + diff
+ if num_args != num_params
+ raise ArgumentError, "wrong number of arguments (#{num_args} for #{num_params})"
+ end
+ end
+
+ end
+
+ ##
+ # Hook executed when Win::Library is included into class or module. It extends host class/module
+ # with both FFI::Library methods and Win::Library macro methods like 'function'.
+ #
+ def self.included(klass)
+ klass.extend FFI::Library
+
+ eigenklass = class << klass; self; end # Extracting host class's eigenclass
+ eigenklass.class_eval do
+ define_method(:namespace) {klass} # Defining new class method for host pointing to itself
+ alias_method :attach_callback, :callback
+
+ include ClassMethods
+ end
+
+ klass.ffi_lib 'user32', 'kernel32' # Default libraries
+ klass.ffi_convention :stdcall
+ end
+ end
end
\ No newline at end of file