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