win 0.1.27 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/win/library.rb CHANGED
@@ -1,464 +1,464 @@
1
- require 'ffi'
2
- require 'win/extensions'
3
-
4
- # Related Windows API functions are grouped by topic and defined in separate namespaces (modules),
5
- # that also contain related constants and convenience methods. For example, Win::DDE module
6
- # contains only functions related to DDE protocol such as DdeInitialize() as well as constants
7
- # such as DMLERR_NO_ERROR, APPCLASS_STANDARD, etc. So if you need only DDE-related functions,
8
- # there is no need to load all the other modules, clogging your namespaces - just <b> require 'win/dde' </b>
9
- # and be done with it. Win is just a top level namespace (container) that holds all the other modules.
10
- #
11
- module Win
12
-
13
- module Errors # :nodoc:
14
- class NotFoundError < NameError # :nodoc:
15
- def initialize(name=nil, libs=nil)
16
- super %Q[Function #{name ? "'#{name}' ": ""}not found#{libs ? " in #{libs}" : ""}"]
17
- end
18
- end
19
- end
20
-
21
- # WIN::Library is a module that extends FFI::Library and is used to connect to Windows API functions
22
- # and wrap them into Ruby methods using 'function' declaration. If you do not see your favorite Windows
23
- # API functions among those already defined, you can easily 'include Win::Library’ into your module
24
- # and declare them using ‘function’ class method (macro) - it does a lot of heavy lifting for you and
25
- # can be customized with options and code blocks to give you reusable API wrapper methods with the exact
26
- # behavior you need.
27
- #
28
- module Library
29
-
30
- # Win::Library::API is a wrapper for callable function API object that mimics Win32::API
31
- class API
32
-
33
- # The name of the DLL(s) that export this API function
34
- attr_reader :dll_name
35
-
36
- # Ruby namespace (module) where this API function is attached
37
- attr_reader :namespace
38
-
39
- # The name of the function passed to the constructor
40
- attr_reader :function_name
41
-
42
- # The name of the actual Windows API function. For example, if you passed 'GetUserName' to the
43
- # constructor, then the effective function name would be either 'GetUserNameA' or 'GetUserNameW'.
44
- attr_accessor :effective_function_name
45
-
46
- # The prototype, returned as an array of FFI types
47
- attr_reader :prototype
48
-
49
- # The return type (:void for no return value)
50
- attr_reader :return_type
51
-
52
- def initialize( namespace, function_name, effective_function_name, prototype, return_type, dll_name )
53
- @namespace = namespace
54
- @function_name = function_name
55
- @effective_function_name = effective_function_name
56
- @prototype = prototype
57
- @return_type = return_type
58
- @dll_name = dll_name
59
- end
60
-
61
- # Calls underlying CamelCase Windows API function with supplied args
62
- def call( *args )
63
- @namespace.send(@function_name.to_sym, *args)
64
- end
65
-
66
- # alias_method :[], :call
67
- end
68
-
69
- # Contains class methods (macros) that can be used in any module mixing in Win::Library
70
- module ClassMethods
71
-
72
- # Mapping of Windows API types and one-letter shortcuts into FFI types.
73
- # Like :ATOM => :ushort, :LPARAM => :long, :c => :char, :i => :int
74
- TYPES = {
75
- # FFI type shortcuts
76
- C: :uchar, #– 8-bit unsigned character (byte)
77
- c: :char, # 8-bit character (byte)
78
- # :int8 – 8-bit signed integer
79
- # :uint8 – 8-bit unsigned integer
80
- S: :ushort, # – 16-bit unsigned integer (Win32/API: S used for string params)
81
- s: :short, # – 16-bit signed integer
82
- # :uint16 – 16-bit unsigned integer
83
- # :int16 – 16-bit signed integer
84
- I: :uint, # 32-bit unsigned integer
85
- i: :int, # 32-bit signed integer
86
- # :uint32 – 32-bit unsigned integer
87
- # :int32 – 32-bit signed integer
88
- L: :ulong, # unsigned long int – platform-specific size
89
- l: :long, # long int – platform-specific size. For discussion of platforms, see:
90
- # (http://groups.google.com/group/ruby-ffi/browse_thread/thread/4762fc77130339b1)
91
- # :int64 – 64-bit signed integer
92
- # :uint64 – 64-bit unsigned integer
93
- # :long_long – 64-bit signed integer
94
- # :ulong_long – 64-bit unsigned integer
95
- F: :float, # 32-bit floating point
96
- D: :double, # 64-bit floating point (double-precision)
97
- P: :pointer, # pointer – platform-specific size
98
- p: :string, # C-style (NULL-terminated) character string (Win32API: S)
99
- B: :bool, # (?? 1 byte in C++)
100
- V: :void, # For functions that return nothing (return type void).
101
- v: :void, # For functions that return nothing (return type void).
102
- # For function argument type only:
103
- # :buffer_in – Similar to :pointer, but optimized for Buffers that the function can only read (not write).
104
- # :buffer_out – Similar to :pointer, but optimized for Buffers that the function can only write (not read).
105
- # :buffer_inout – Similar to :pointer, but may be optimized for Buffers.
106
- # :varargs – Variable arguments
107
- # :enum - Enumerable type (should be defined)
108
- # :char_array - ??
109
-
110
- # Windows-specific type defs (ms-help://MS.MSDNQTR.v90.en/winprog/winprog/windows_data_types.htm):
111
- ATOM: :ushort, # Atom ~= Symbol: Atom table stores strings and corresponding identifiers. Application
112
- # places a string in an atom table and receives a 16-bit integer, called an atom, that
113
- # can be used to access the string. Placed string is called an atom name.
114
- # See: http://msdn.microsoft.com/en-us/library/ms648708%28VS.85%29.aspx
115
- BOOL: :bool,
116
- BOOLEAN: :bool,
117
- BYTE: :uchar, # Byte (8 bits). Declared as unsigned char
118
- #CALLBACK: K, # Win32.API gem-specific ?? MSDN: #define CALLBACK __stdcall
119
- CHAR: :char, # 8-bit Windows (ANSI) character. See http://msdn.microsoft.com/en-us/library/dd183415%28VS.85%29.aspx
120
- COLORREF: :uint32, # Red, green, blue (RGB) color value (32 bits). See COLORREF for more info.
121
- DWORD: :uint32, # 32-bit unsigned integer. The range is 0 through 4,294,967,295 decimal.
122
- DWORDLONG: :uint64, # 64-bit unsigned integer. The range is 0 through 18,446,744,073,709,551,615 decimal.
123
- DWORD_PTR: :ulong, # Unsigned long type for pointer precision. Use when casting a pointer to a long type
124
- # to perform pointer arithmetic. (Also commonly used for general 32-bit parameters that have
125
- # been extended to 64 bits in 64-bit Windows.) BaseTsd.h: #typedef ULONG_PTR DWORD_PTR;
126
- DWORD32: :uint32,
127
- DWORD64: :uint64,
128
- HALF_PTR: :int, # Half the size of a pointer. Use within a structure that contains a pointer and two small fields.
129
- # BaseTsd.h: #ifdef (_WIN64) typedef int HALF_PTR; #else typedef short HALF_PTR;
130
- HACCEL: :ulong, # (L) Handle to an accelerator table. WinDef.h: #typedef HANDLE HACCEL;
131
- # See http://msdn.microsoft.com/en-us/library/ms645526%28VS.85%29.aspx
132
- HANDLE: :ulong, # (L) Handle to an object. WinNT.h: #typedef PVOID HANDLE;
133
- # todo: Platform-dependent! Need to change to :uint64 for Win64
134
- HBITMAP: :ulong, # (L) Handle to a bitmap: http://msdn.microsoft.com/en-us/library/dd183377%28VS.85%29.aspx
135
- HBRUSH: :ulong, # (L) Handle to a brush. http://msdn.microsoft.com/en-us/library/dd183394%28VS.85%29.aspx
136
- HCOLORSPACE: :ulong, # (L) Handle to a color space. http://msdn.microsoft.com/en-us/library/ms536546%28VS.85%29.aspx
137
- HCURSOR: :ulong, # (L) Handle to a cursor. http://msdn.microsoft.com/en-us/library/ms646970%28VS.85%29.aspx
138
- HCONV: :ulong, # (L) Handle to a dynamic data exchange (DDE) conversation.
139
- HCONVLIST: :ulong, # (L) Handle to a DDE conversation list. HANDLE - L ?
140
- HDDEDATA: :ulong, # (L) Handle to DDE data (structure?)
141
- HDC: :ulong, # (L) Handle to a device context (DC). http://msdn.microsoft.com/en-us/library/dd183560%28VS.85%29.aspx
142
- HDESK: :ulong, # (L) Handle to a desktop. http://msdn.microsoft.com/en-us/library/ms682573%28VS.85%29.aspx
143
- HDROP: :ulong, # (L) Handle to an internal drop structure.
144
- HDWP: :ulong, # (L) Handle to a deferred window position structure.
145
- HENHMETAFILE: :ulong, #(L) Handle to an enhanced metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx
146
- HFILE: :uint, # (I) Special file handle to a file opened by OpenFile, not CreateFile.
147
- # WinDef.h: #typedef int HFILE;
148
- HFONT: :ulong, # (L) Handle to a font. http://msdn.microsoft.com/en-us/library/dd162470%28VS.85%29.aspx
149
- HGDIOBJ: :ulong, # (L) Handle to a GDI object.
150
- HGLOBAL: :ulong, # (L) Handle to a global memory block.
151
- HHOOK: :ulong, # (L) Handle to a hook. http://msdn.microsoft.com/en-us/library/ms632589%28VS.85%29.aspx
152
- HICON: :ulong, # (L) Handle to an icon. http://msdn.microsoft.com/en-us/library/ms646973%28VS.85%29.aspx
153
- HINSTANCE: :ulong, # (L) Handle to an instance. This is the base address of the module in memory.
154
- # HMODULE and HINSTANCE are the same today, but were different in 16-bit Windows.
155
- HKEY: :ulong, # (L) Handle to a registry key.
156
- HKL: :ulong, # (L) Input locale identifier.
157
- HLOCAL: :ulong, # (L) Handle to a local memory block.
158
- HMENU: :ulong, # (L) Handle to a menu. http://msdn.microsoft.com/en-us/library/ms646977%28VS.85%29.aspx
159
- HMETAFILE: :ulong, # (L) Handle to a metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx
160
- HMODULE: :ulong, # (L) Handle to an instance. Same as HINSTANCE today, but was different in 16-bit Windows.
161
- HMONITOR: :ulong, # (L) Рandle to a display monitor. WinDef.h: if(WINVER >= 0x0500) typedef HANDLE HMONITOR;
162
- HPALETTE: :ulong, # (L) Handle to a palette.
163
- HPEN: :ulong, # (L) Handle to a pen. http://msdn.microsoft.com/en-us/library/dd162786%28VS.85%29.aspx
164
- HRESULT: :long, # Return code used by COM interfaces. For more info, Structure of the COM Error Codes.
165
- # To test an HRESULT value, use the FAILED and SUCCEEDED macros.
166
- HRGN: :ulong, # (L) Handle to a region. http://msdn.microsoft.com/en-us/library/dd162913%28VS.85%29.aspx
167
- HRSRC: :ulong, # (L) Handle to a resource.
168
- HSZ: :ulong, # (L) Handle to a DDE string.
169
- HWINSTA: :ulong, # (L) Handle to a window station. http://msdn.microsoft.com/en-us/library/ms687096%28VS.85%29.aspx
170
- HWND: :ulong, # (L) Handle to a window. http://msdn.microsoft.com/en-us/library/ms632595%28VS.85%29.aspx
171
- INT: :int, # 32-bit signed integer. The range is -2147483648 through 2147483647 decimal.
172
- INT_PTR: :int, # Signed integer type for pointer precision. Use when casting a pointer to an integer
173
- # to perform pointer arithmetic. BaseTsd.h:
174
- #if defined(_WIN64) typedef __int64 INT_PTR; #else typedef int INT_PTR;
175
- INT32: :int32, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
176
- INT64: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
177
- LANGID: :ushort, # Language identifier. For more information, see Locales. WinNT.h: #typedef WORD LANGID;
178
- # See http://msdn.microsoft.com/en-us/library/dd318716%28VS.85%29.aspx
179
- LCID: :uint32, # Locale identifier. For more information, see Locales.
180
- LCTYPE: :uint32, # Locale information type. For a list, see Locale Information Constants.
181
- LGRPID: :uint32, # Language group identifier. For a list, see EnumLanguageGroupLocales.
182
- LONG: :long, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
183
- LONG32: :int32, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
184
- LONG64: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
185
- LONGLONG: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
186
- LONG_PTR: :long, # Signed long type for pointer precision. Use when casting a pointer to a long to
187
- # perform pointer arithmetic. BaseTsd.h:
188
- #if defined(_WIN64) typedef __int64 LONG_PTR; #else typedef long LONG_PTR;
189
- LPARAM: :long, # Message parameter. WinDef.h as follows: #typedef LONG_PTR LPARAM;
190
- LPBOOL: :pointer, # Pointer to a BOOL. WinDef.h as follows: #typedef BOOL far *LPBOOL;
191
- LPBYTE: :pointer, # Pointer to a BYTE. WinDef.h as follows: #typedef BYTE far *LPBYTE;
192
- LPCOLORREF: :pointer, # Pointer to a COLORREF value. WinDef.h as follows: #typedef DWORD *LPCOLORREF;
193
- LPCSTR: :pointer, # Pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters.
194
- # See Character Sets Used By Fonts. http://msdn.microsoft.com/en-us/library/dd183415%28VS.85%29.aspx
195
- LPCTSTR: :pointer, # An LPCWSTR if UNICODE is defined, an LPCSTR otherwise.
196
- LPCVOID: :pointer, # Pointer to a constant of any type. WinDef.h as follows: typedef CONST void *LPCVOID;
197
- LPCWSTR: :pointer, # Pointer to a constant null-terminated string of 16-bit Unicode characters.
198
- LPDWORD: :pointer, # Pointer to a DWORD. WinDef.h as follows: typedef DWORD *LPDWORD;
199
- LPHANDLE: :pointer, # Pointer to a HANDLE. WinDef.h as follows: typedef HANDLE *LPHANDLE;
200
- LPINT: :pointer, # Pointer to an INT.
201
- LPLONG: :pointer, # Pointer to an LONG.
202
- LPSTR: :pointer, # Pointer to a null-terminated string of 8-bit Windows (ANSI) characters.
203
- LPTSTR: :pointer, # An LPWSTR if UNICODE is defined, an LPSTR otherwise.
204
- LPVOID: :pointer, # Pointer to any type.
205
- LPWORD: :pointer, # Pointer to a WORD.
206
- LPWSTR: :pointer, # Pointer to a null-terminated string of 16-bit Unicode characters.
207
- LRESULT: :long, # Signed result of message processing. WinDef.h: typedef LONG_PTR LRESULT;
208
- PBOOL: :pointer, # Pointer to a BOOL.
209
- PBOOLEAN: :pointer, # Pointer to a BOOL.
210
- PBYTE: :pointer, # Pointer to a BYTE.
211
- PCHAR: :pointer, # Pointer to a CHAR.
212
- PCSTR: :pointer, # Pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters.
213
- PCTSTR: :pointer, # A PCWSTR if UNICODE is defined, a PCSTR otherwise.
214
- PCWSTR: :pointer, # Pointer to a constant null-terminated string of 16-bit Unicode characters.
215
- PDWORD: :pointer, # Pointer to a DWORD.
216
- PDWORDLONG: :pointer, # Pointer to a DWORDLONG.
217
- PDWORD_PTR: :pointer, # Pointer to a DWORD_PTR.
218
- PDWORD32: :pointer, # Pointer to a DWORD32.
219
- PDWORD64: :pointer, # Pointer to a DWORD64.
220
- PFLOAT: :pointer, # Pointer to a FLOAT.
221
- PHALF_PTR: :pointer, # Pointer to a HALF_PTR.
222
- PHANDLE: :pointer, # Pointer to a HANDLE.
223
- PHKEY: :pointer, # Pointer to an HKEY.
224
- PINT: :pointer, # Pointer to an INT.
225
- PINT_PTR: :pointer, # Pointer to an INT_PTR.
226
- PINT32: :pointer, # Pointer to an INT32.
227
- PINT64: :pointer, # Pointer to an INT64.
228
- PLCID: :pointer, # Pointer to an LCID.
229
- PLONG: :pointer, # Pointer to a LONG.
230
- PLONGLONG: :pointer, # Pointer to a LONGLONG.
231
- PLONG_PTR: :pointer, # Pointer to a LONG_PTR.
232
- PLONG32: :pointer, # Pointer to a LONG32.
233
- PLONG64: :pointer, # Pointer to a LONG64.
234
- 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.
235
- 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.
236
- POINTER_SIGNED: :pointer, # A signed pointer.
237
- POINTER_UNSIGNED: :pointer, # An unsigned pointer.
238
- PSHORT: :pointer, # Pointer to a SHORT.
239
- PSIZE_T: :pointer, # Pointer to a SIZE_T.
240
- PSSIZE_T: :pointer, # Pointer to a SSIZE_T.
241
- PSTR: :pointer, # Pointer to a null-terminated string of 8-bit Windows (ANSI) characters. For more information, see Character Sets Used By Fonts.
242
- PTBYTE: :pointer, # Pointer to a TBYTE.
243
- PTCHAR: :pointer, # Pointer to a TCHAR.
244
- PTSTR: :pointer, # A PWSTR if UNICODE is defined, a PSTR otherwise.
245
- PUCHAR: :pointer, # Pointer to a UCHAR.
246
- PUHALF_PTR: :pointer, # Pointer to a UHALF_PTR.
247
- PUINT: :pointer, # Pointer to a UINT.
248
- PUINT_PTR: :pointer, # Pointer to a UINT_PTR.
249
- PUINT32: :pointer, # Pointer to a UINT32.
250
- PUINT64: :pointer, # Pointer to a UINT64.
251
- PULONG: :pointer, # Pointer to a ULONG.
252
- PULONGLONG: :pointer, # Pointer to a ULONGLONG.
253
- PULONG_PTR: :pointer, # Pointer to a ULONG_PTR.
254
- PULONG32: :pointer, # Pointer to a ULONG32.
255
- PULONG64: :pointer, # Pointer to a ULONG64.
256
- PUSHORT: :pointer, # Pointer to a USHORT.
257
- PVOID: :pointer, # Pointer to any type.
258
- PWCHAR: :pointer, # Pointer to a WCHAR.
259
- PWORD: :pointer, # Pointer to a WORD.
260
- PWSTR: :pointer, # Pointer to a null- terminated string of 16-bit Unicode characters.
261
- # For more information, see Character Sets Used By Fonts.
262
- SC_HANDLE: :ulong, # (L) Handle to a service control manager database.
263
- # See SCM Handles http://msdn.microsoft.com/en-us/library/ms685104%28VS.85%29.aspx
264
- SC_LOCK: :pointer, # Lock to a service control manager database. For more information, see SCM Handles.
265
- SERVICE_STATUS_HANDLE: :ulong, # (L) Handle to a service status value. See SCM Handles.
266
- SHORT: :short, # A 16-bit integer. The range is –32768 through 32767 decimal.
267
- 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.
268
- SSIZE_T: :long, # Signed SIZE_T.
269
- TBYTE: :short, # A WCHAR if UNICODE is defined, a CHAR otherwise.TCHAR:
270
- TCHAR: :short, # A WCHAR if UNICODE is defined, a CHAR otherwise.TCHAR:
271
- UCHAR: :uchar, # Unsigned CHAR (8 bit)
272
- UHALF_PTR: :uint, # Unsigned HALF_PTR. Use within a structure that contains a pointer and two small fields.
273
- UINT: :uint, # Unsigned INT. The range is 0 through 4294967295 decimal.
274
- UINT_PTR: :uint, # Unsigned INT_PTR.
275
- UINT32: :uint32, # Unsigned INT32. The range is 0 through 4294967295 decimal.
276
- UINT64: :uint64, # Unsigned INT64. The range is 0 through 18446744073709551615 decimal.
277
- ULONG: :ulong, # Unsigned LONG. The range is 0 through 4294967295 decimal.
278
- ULONGLONG: :ulong_long, # 64-bit unsigned integer. The range is 0 through 18446744073709551615 decimal.
279
- ULONG_PTR: :ulong, # Unsigned LONG_PTR.
280
- ULONG32: :uint32, # Unsigned INT32. The range is 0 through 4294967295 decimal.
281
- ULONG64: :uint64, # Unsigned LONG64. The range is 0 through 18446744073709551615 decimal.
282
- UNICODE_STRING: :pointer, # Pointer to some string structure??
283
- USHORT: :ushort, # Unsigned SHORT. The range is 0 through 65535 decimal.
284
- USN: :ulong_long, # Update sequence number (USN).
285
- VOID: [], # Any type ? Only use it to indicate no arguments or no return value
286
- WCHAR: :ushort, # 16-bit Unicode character. For more information, see Character Sets Used By Fonts.
287
- # In WinNT.h: typedef wchar_t WCHAR;
288
- #WINAPI: K, # Calling convention for system functions. WinDef.h: define WINAPI __stdcall
289
- WORD: :ushort, # 16-bit unsigned integer. The range is 0 through 65535 decimal.
290
- WPARAM: :uint # Message parameter. WinDef.h as follows: typedef UINT_PTR WPARAM;
291
- }
292
-
293
- ##
294
- # Defines new method wrappers for Windows API function call:
295
- # - Defines method with original (CamelCase) API function name and original signature (matches MSDN description)
296
- # - Defines method with snake_case name (converted from CamelCase function name) with enhanced API signature
297
- # When defined snake_case method is called, it converts the arguments you provided into ones required by
298
- # original API (adding defaults, mute and transitory args as necessary), executes API function call
299
- # and (optionally) transforms the result before returning it. If a block is attached to
300
- # method invocation, raw result is yielded to this block before final transformation take place
301
- # - Defines aliases for enhanced method with more Rubyesque names for getters, setters and tests:
302
- # GetWindowText -> window_test, SetWindowText -> window_text=, IsZoomed -> zoomed?
303
- # ---
304
- # You may modify default behavior of defined method by providing optional *def_block* to function definition.
305
- # If you do so, snake_case method is defined based on your *def_block*. It receives callable API
306
- # object for function being defined, arguments and (optional) runtime block with which the method
307
- # will be called. Results coming from &def_block are then transformed and returned.
308
- # So, your *def_block* should specify all the behavior of the method being defined. You can use *def_block* to:
309
- # - Change original signature of API function, provide argument defaults, check argument types
310
- # - Pack arguments into strings/structs for [in] or [in/out] parameters that expect a pointer
311
- # - Allocate buffers/structs for pointers required by API functions [out] parameters
312
- # - Unpack [out] and [in/out] parameters returned as pointers
313
- # - Explicitly return results of API call that are returned in [out] and [in/out] parameters
314
- # - Convert attached runtime blocks into callback functions and stuff them into [in] callback parameters
315
- # - do other stuff that you think is appropriate to make Windows API function behavior more Ruby-like...
316
- # ---
317
- # Accepts following options:
318
- # :dll:: Use this dll instead of default 'user32'
319
- # :rename:: Use this name instead of standard (conventional) function name
320
- # :alias(es):: Provides additional alias(es) for defined method
321
- # :boolean:: Forces method to return true/false instead of nonzero/zero
322
- # :zeronil:: Forces method to return nil if function result is zero
323
- #
324
- def function(name, params, returns, options={}, &def_block)
325
- snake_name, effective_names, aliases = generate_names(name, options)
326
- params, returns = generate_signature(params, returns)
327
- libs = ffi_libraries.map(&:name)
328
- boolean = options[:boolean]
329
- zeronil = options[:zeronil]
330
-
331
- effective_name = effective_names.inject(nil) do |func, effective_name|
332
- func || begin
333
- # tries to attach basic CamelCase method via FFI
334
- attach_function(name, effective_name, params.dup, returns)
335
- effective_name
336
- rescue FFI::NotFoundError
337
- nil
338
- end
339
- end
340
-
341
- raise Win::Errors::NotFoundError.new(name, libs) unless effective_name
342
-
343
- # Create API object that holds information about function names, params, etc
344
- api = API.new(namespace, name, effective_name, params, returns, libs)
345
-
346
- # Only define enhanced API if snake_name is different from original name (e.g. keybd_event)
347
- unless snake_name.to_s == name.to_s
348
- method_body = if def_block
349
- if zeronil
350
- ->(*args, &block){ (res = def_block.(api, *args, &block)) != 0 ? res : nil }
351
- elsif boolean
352
- ->(*args, &block){ def_block.(api, *args, &block) != 0 }
353
- else
354
- ->(*args, &block){ def_block.(api, *args, &block) }
355
- end
356
- else
357
- if zeronil
358
- ->(*args, &block){ (res = block ? block[api.call(*args)] : api.call(*args)) != 0 ? res : nil }
359
- elsif boolean
360
- ->(*args, &block){ block ? block[api.call(*args)] : api.call(*args) != 0 }
361
- else
362
- ->(*args, &block){ block ? block[api.call(*args)] : api.call(*args) }
363
- end
364
- end
365
-
366
- define_method snake_name, &method_body # define snake_case instance method
367
-
368
- eigenklass = class << self; self; end # Extracting eigenclass
369
- eigenklass.class_eval do
370
- define_method snake_name, &method_body # define snake_case class method
371
- end
372
- end
373
-
374
- aliases.each {|ali| alias_method ali, snake_name } # define aliases
375
- api #return api object from function declaration
376
- end
377
-
378
- # Try to define platform-specific function, rescue error, return message
379
- #
380
- def try_function(name, params, returns, options={}, &def_block)
381
- begin
382
- function name, params, returns, options={}, &def_block
383
- rescue Win::Errors::NotFoundError
384
- "This platform does not support function #{name}"
385
- end
386
- end
387
-
388
- # Generates possible effective names for function in Win32 dll (name+A/W),
389
- # Rubyesque name and aliases for method(s) defined based on function name,
390
- #
391
- def generate_names(name, options={})
392
- name = name.to_s
393
- effective_names = [name]
394
- effective_names += ["#{name}A", "#{name}W"] unless name =~ /[WA]$/
395
- aliases = ([options[:alias]] + [options[:aliases]]).flatten.compact
396
- snake_name = options[:rename] || name.snake_case
397
- case snake_name
398
- when /^is_/
399
- aliases << snake_name.sub(/^is_/, '') + '?'
400
- when /^set_/
401
- aliases << snake_name.sub(/^set_/, '')+ '='
402
- when /^get_/
403
- aliases << snake_name.sub(/^get_/, '')
404
- end
405
- [snake_name, effective_names, aliases]
406
- end
407
-
408
- ##
409
- # Generates params and returns (signature) containing only FFI-compliant types
410
- #
411
- def generate_signature(params, returns)
412
- params = params.split(//) if params.respond_to?(:split) # Convert params string into array
413
- params.map! {|param| TYPES[param.to_sym] || param} # Convert chars into FFI type symbols
414
- returns = TYPES[returns.to_sym] || returns # Convert chars into FFI type symbols
415
- [params, returns]
416
- end
417
-
418
- ##
419
- # Wrapper for FFI::Library#callback() that converts Win32/shortcut argument types into FFI-compliant types.
420
- # This method overrides FFI.callback which must be aliased to FFI.attach_callback
421
- #
422
- def callback(name, params, returns)
423
- params, returns = generate_signature(params, returns)
424
- attach_callback name.to_sym, params, returns
425
- end
426
-
427
- ##
428
- # :method: namespace
429
- # This method is meta-generated when Win::Library module is included into other module/class.
430
- # It returns reference to including (host) class/module for use by Win::Library::API and class methods.
431
-
432
- ##
433
- # Ensures that args count is equal to params count plus diff
434
- #
435
- def enforce_count(args, params, diff = 0)
436
- num_args = args.size
437
- num_params = params.size + diff #params == 'V' ? 0 : params.size + diff
438
- if num_args != num_params
439
- raise ArgumentError, "wrong number of arguments (#{num_args} for #{num_params})"
440
- end
441
- end
442
-
443
- end
444
-
445
- ##
446
- # Hook executed when Win::Library is included into class or module. It extends host class/module
447
- # with both FFI::Library methods and Win::Library macro methods like 'function'.
448
- #
449
- def self.included(klass)
450
- klass.extend FFI::Library
451
-
452
- eigenklass = class << klass; self; end # Extracting host class's eigenclass
453
- eigenklass.class_eval do
454
- define_method(:namespace) {klass} # Defining new class method for host pointing to itself
455
- alias_method :attach_callback, :callback
456
-
457
- include ClassMethods
458
- end
459
-
460
- klass.ffi_lib 'user32', 'kernel32' # Default libraries
461
- klass.ffi_convention :stdcall
462
- end
463
- end
1
+ require 'ffi'
2
+ require 'win/extensions'
3
+
4
+ # Related Windows API functions are grouped by topic and defined in separate namespaces (modules),
5
+ # that also contain related constants and convenience methods. For example, Win::Dde module
6
+ # contains only functions related to DDE protocol such as DdeInitialize() as well as constants
7
+ # such as DMLERR_NO_ERROR, APPCLASS_STANDARD, etc. So if you need only DDE-related functions,
8
+ # there is no need to load all the other modules, clogging your namespaces - just <b> require 'win/dde' </b>
9
+ # and be done with it. Win is just a top level namespace (container) that holds all the other modules.
10
+ #
11
+ module Win
12
+
13
+ module Errors # :nodoc:
14
+ class NotFoundError < NameError # :nodoc:
15
+ def initialize(name=nil, libs=nil)
16
+ super %Q[Function #{name ? "'#{name}' ": ""}not found#{libs ? " in #{libs}" : ""}"]
17
+ end
18
+ end
19
+ end
20
+
21
+ # WIN::Library is a module that extends FFI::Library and is used to connect to Windows API functions
22
+ # and wrap them into Ruby methods using 'function' declaration. If you do not see your favorite Windows
23
+ # API functions among those already defined, you can easily 'include Win::Library’ into your module
24
+ # and declare them using ‘function’ class method (macro) - it does a lot of heavy lifting for you and
25
+ # can be customized with options and code blocks to give you reusable API wrapper methods with the exact
26
+ # behavior you need.
27
+ #
28
+ module Library
29
+
30
+ # Win::Library::API is a wrapper for callable function API object that mimics Win32::API
31
+ class API
32
+
33
+ # The name of the DLL(s) that export this API function
34
+ attr_reader :dll_name
35
+
36
+ # Ruby namespace (module) where this API function is attached
37
+ attr_reader :namespace
38
+
39
+ # The name of the function passed to the constructor
40
+ attr_reader :function_name
41
+
42
+ # The name of the actual Windows API function. For example, if you passed 'GetUserName' to the
43
+ # constructor, then the effective function name would be either 'GetUserNameA' or 'GetUserNameW'.
44
+ attr_accessor :effective_function_name
45
+
46
+ # The prototype, returned as an array of FFI types
47
+ attr_reader :prototype
48
+
49
+ # The return type (:void for no return value)
50
+ attr_reader :return_type
51
+
52
+ def initialize( namespace, function_name, effective_function_name, prototype, return_type, dll_name )
53
+ @namespace = namespace
54
+ @function_name = function_name
55
+ @effective_function_name = effective_function_name
56
+ @prototype = prototype
57
+ @return_type = return_type
58
+ @dll_name = dll_name
59
+ end
60
+
61
+ # Calls underlying CamelCase Windows API function with supplied args
62
+ def call( *args )
63
+ @namespace.send(@function_name.to_sym, *args)
64
+ end
65
+
66
+ # alias_method :[], :call
67
+ end
68
+
69
+ # Contains class methods (macros) that can be used in any module mixing in Win::Library
70
+ module ClassMethods
71
+
72
+ # Mapping of Windows API types and one-letter shortcuts into FFI types.
73
+ # Like :ATOM => :ushort, :LPARAM => :long, :c => :char, :i => :int
74
+ TYPES = {
75
+ # FFI type shortcuts
76
+ C: :uchar, #– 8-bit unsigned character (byte)
77
+ c: :char, # 8-bit character (byte)
78
+ # :int8 – 8-bit signed integer
79
+ # :uint8 – 8-bit unsigned integer
80
+ S: :ushort, # – 16-bit unsigned integer (Win32/API: S used for string params)
81
+ s: :short, # – 16-bit signed integer
82
+ # :uint16 – 16-bit unsigned integer
83
+ # :int16 – 16-bit signed integer
84
+ I: :uint, # 32-bit unsigned integer
85
+ i: :int, # 32-bit signed integer
86
+ # :uint32 – 32-bit unsigned integer
87
+ # :int32 – 32-bit signed integer
88
+ L: :ulong, # unsigned long int – platform-specific size
89
+ l: :long, # long int – platform-specific size. For discussion of platforms, see:
90
+ # (http://groups.google.com/group/ruby-ffi/browse_thread/thread/4762fc77130339b1)
91
+ # :int64 – 64-bit signed integer
92
+ # :uint64 – 64-bit unsigned integer
93
+ # :long_long – 64-bit signed integer
94
+ # :ulong_long – 64-bit unsigned integer
95
+ F: :float, # 32-bit floating point
96
+ D: :double, # 64-bit floating point (double-precision)
97
+ P: :pointer, # pointer – platform-specific size
98
+ p: :string, # C-style (NULL-terminated) character string (Win32API: S)
99
+ B: :bool, # (?? 1 byte in C++)
100
+ V: :void, # For functions that return nothing (return type void).
101
+ v: :void, # For functions that return nothing (return type void).
102
+ # For function argument type only:
103
+ # :buffer_in – Similar to :pointer, but optimized for Buffers that the function can only read (not write).
104
+ # :buffer_out – Similar to :pointer, but optimized for Buffers that the function can only write (not read).
105
+ # :buffer_inout – Similar to :pointer, but may be optimized for Buffers.
106
+ # :varargs – Variable arguments
107
+ # :enum - Enumerable type (should be defined)
108
+ # :char_array - ??
109
+
110
+ # Windows-specific type defs (ms-help://MS.MSDNQTR.v90.en/winprog/winprog/windows_data_types.htm):
111
+ ATOM: :ushort, # Atom ~= Symbol: Atom table stores strings and corresponding identifiers. Application
112
+ # places a string in an atom table and receives a 16-bit integer, called an atom, that
113
+ # can be used to access the string. Placed string is called an atom name.
114
+ # See: http://msdn.microsoft.com/en-us/library/ms648708%28VS.85%29.aspx
115
+ BOOL: :bool,
116
+ BOOLEAN: :bool,
117
+ BYTE: :uchar, # Byte (8 bits). Declared as unsigned char
118
+ #CALLBACK: K, # Win32.API gem-specific ?? MSDN: #define CALLBACK __stdcall
119
+ CHAR: :char, # 8-bit Windows (ANSI) character. See http://msdn.microsoft.com/en-us/library/dd183415%28VS.85%29.aspx
120
+ COLORREF: :uint32, # Red, green, blue (RGB) color value (32 bits). See COLORREF for more info.
121
+ DWORD: :uint32, # 32-bit unsigned integer. The range is 0 through 4,294,967,295 decimal.
122
+ DWORDLONG: :uint64, # 64-bit unsigned integer. The range is 0 through 18,446,744,073,709,551,615 decimal.
123
+ DWORD_PTR: :ulong, # Unsigned long type for pointer precision. Use when casting a pointer to a long type
124
+ # to perform pointer arithmetic. (Also commonly used for general 32-bit parameters that have
125
+ # been extended to 64 bits in 64-bit Windows.) BaseTsd.h: #typedef ULONG_PTR DWORD_PTR;
126
+ DWORD32: :uint32,
127
+ DWORD64: :uint64,
128
+ HALF_PTR: :int, # Half the size of a pointer. Use within a structure that contains a pointer and two small fields.
129
+ # BaseTsd.h: #ifdef (_WIN64) typedef int HALF_PTR; #else typedef short HALF_PTR;
130
+ HACCEL: :ulong, # (L) Handle to an accelerator table. WinDef.h: #typedef HANDLE HACCEL;
131
+ # See http://msdn.microsoft.com/en-us/library/ms645526%28VS.85%29.aspx
132
+ HANDLE: :ulong, # (L) Handle to an object. WinNT.h: #typedef PVOID HANDLE;
133
+ # todo: Platform-dependent! Need to change to :uint64 for Win64
134
+ HBITMAP: :ulong, # (L) Handle to a bitmap: http://msdn.microsoft.com/en-us/library/dd183377%28VS.85%29.aspx
135
+ HBRUSH: :ulong, # (L) Handle to a brush. http://msdn.microsoft.com/en-us/library/dd183394%28VS.85%29.aspx
136
+ HCOLORSPACE: :ulong, # (L) Handle to a color space. http://msdn.microsoft.com/en-us/library/ms536546%28VS.85%29.aspx
137
+ HCURSOR: :ulong, # (L) Handle to a cursor. http://msdn.microsoft.com/en-us/library/ms646970%28VS.85%29.aspx
138
+ HCONV: :ulong, # (L) Handle to a dynamic data exchange (DDE) conversation.
139
+ HCONVLIST: :ulong, # (L) Handle to a DDE conversation list. HANDLE - L ?
140
+ HDDEDATA: :ulong, # (L) Handle to DDE data (structure?)
141
+ HDC: :ulong, # (L) Handle to a device context (DC). http://msdn.microsoft.com/en-us/library/dd183560%28VS.85%29.aspx
142
+ HDESK: :ulong, # (L) Handle to a desktop. http://msdn.microsoft.com/en-us/library/ms682573%28VS.85%29.aspx
143
+ HDROP: :ulong, # (L) Handle to an internal drop structure.
144
+ HDWP: :ulong, # (L) Handle to a deferred window position structure.
145
+ HENHMETAFILE: :ulong, #(L) Handle to an enhanced metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx
146
+ HFILE: :uint, # (I) Special file handle to a file opened by OpenFile, not CreateFile.
147
+ # WinDef.h: #typedef int HFILE;
148
+ HFONT: :ulong, # (L) Handle to a font. http://msdn.microsoft.com/en-us/library/dd162470%28VS.85%29.aspx
149
+ HGDIOBJ: :ulong, # (L) Handle to a GDI object.
150
+ HGLOBAL: :ulong, # (L) Handle to a global memory block.
151
+ HHOOK: :ulong, # (L) Handle to a hook. http://msdn.microsoft.com/en-us/library/ms632589%28VS.85%29.aspx
152
+ HICON: :ulong, # (L) Handle to an icon. http://msdn.microsoft.com/en-us/library/ms646973%28VS.85%29.aspx
153
+ HINSTANCE: :ulong, # (L) Handle to an instance. This is the base address of the module in memory.
154
+ # HMODULE and HINSTANCE are the same today, but were different in 16-bit Windows.
155
+ HKEY: :ulong, # (L) Handle to a registry key.
156
+ HKL: :ulong, # (L) Input locale identifier.
157
+ HLOCAL: :ulong, # (L) Handle to a local memory block.
158
+ HMENU: :ulong, # (L) Handle to a menu. http://msdn.microsoft.com/en-us/library/ms646977%28VS.85%29.aspx
159
+ HMETAFILE: :ulong, # (L) Handle to a metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx
160
+ HMODULE: :ulong, # (L) Handle to an instance. Same as HINSTANCE today, but was different in 16-bit Windows.
161
+ HMONITOR: :ulong, # (L) Рandle to a display monitor. WinDef.h: if(WINVER >= 0x0500) typedef HANDLE HMONITOR;
162
+ HPALETTE: :ulong, # (L) Handle to a palette.
163
+ HPEN: :ulong, # (L) Handle to a pen. http://msdn.microsoft.com/en-us/library/dd162786%28VS.85%29.aspx
164
+ HRESULT: :long, # Return code used by COM interfaces. For more info, Structure of the COM Error Codes.
165
+ # To test an HRESULT value, use the FAILED and SUCCEEDED macros.
166
+ HRGN: :ulong, # (L) Handle to a region. http://msdn.microsoft.com/en-us/library/dd162913%28VS.85%29.aspx
167
+ HRSRC: :ulong, # (L) Handle to a resource.
168
+ HSZ: :ulong, # (L) Handle to a DDE string.
169
+ HWINSTA: :ulong, # (L) Handle to a window station. http://msdn.microsoft.com/en-us/library/ms687096%28VS.85%29.aspx
170
+ HWND: :ulong, # (L) Handle to a window. http://msdn.microsoft.com/en-us/library/ms632595%28VS.85%29.aspx
171
+ INT: :int, # 32-bit signed integer. The range is -2147483648 through 2147483647 decimal.
172
+ INT_PTR: :int, # Signed integer type for pointer precision. Use when casting a pointer to an integer
173
+ # to perform pointer arithmetic. BaseTsd.h:
174
+ #if defined(_WIN64) typedef __int64 INT_PTR; #else typedef int INT_PTR;
175
+ INT32: :int32, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
176
+ INT64: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
177
+ LANGID: :ushort, # Language identifier. For more information, see Locales. WinNT.h: #typedef WORD LANGID;
178
+ # See http://msdn.microsoft.com/en-us/library/dd318716%28VS.85%29.aspx
179
+ LCID: :uint32, # Locale identifier. For more information, see Locales.
180
+ LCTYPE: :uint32, # Locale information type. For a list, see Locale Information Constants.
181
+ LGRPID: :uint32, # Language group identifier. For a list, see EnumLanguageGroupLocales.
182
+ LONG: :long, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
183
+ LONG32: :int32, # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal.
184
+ LONG64: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
185
+ LONGLONG: :int64, # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807
186
+ LONG_PTR: :long, # Signed long type for pointer precision. Use when casting a pointer to a long to
187
+ # perform pointer arithmetic. BaseTsd.h:
188
+ #if defined(_WIN64) typedef __int64 LONG_PTR; #else typedef long LONG_PTR;
189
+ LPARAM: :long, # Message parameter. WinDef.h as follows: #typedef LONG_PTR LPARAM;
190
+ LPBOOL: :pointer, # Pointer to a BOOL. WinDef.h as follows: #typedef BOOL far *LPBOOL;
191
+ LPBYTE: :pointer, # Pointer to a BYTE. WinDef.h as follows: #typedef BYTE far *LPBYTE;
192
+ LPCOLORREF: :pointer, # Pointer to a COLORREF value. WinDef.h as follows: #typedef DWORD *LPCOLORREF;
193
+ LPCSTR: :pointer, # Pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters.
194
+ # See Character Sets Used By Fonts. http://msdn.microsoft.com/en-us/library/dd183415%28VS.85%29.aspx
195
+ LPCTSTR: :pointer, # An LPCWSTR if UNICODE is defined, an LPCSTR otherwise.
196
+ LPCVOID: :pointer, # Pointer to a constant of any type. WinDef.h as follows: typedef CONST void *LPCVOID;
197
+ LPCWSTR: :pointer, # Pointer to a constant null-terminated string of 16-bit Unicode characters.
198
+ LPDWORD: :pointer, # Pointer to a DWORD. WinDef.h as follows: typedef DWORD *LPDWORD;
199
+ LPHANDLE: :pointer, # Pointer to a HANDLE. WinDef.h as follows: typedef HANDLE *LPHANDLE;
200
+ LPINT: :pointer, # Pointer to an INT.
201
+ LPLONG: :pointer, # Pointer to an LONG.
202
+ LPSTR: :pointer, # Pointer to a null-terminated string of 8-bit Windows (ANSI) characters.
203
+ LPTSTR: :pointer, # An LPWSTR if UNICODE is defined, an LPSTR otherwise.
204
+ LPVOID: :pointer, # Pointer to any type.
205
+ LPWORD: :pointer, # Pointer to a WORD.
206
+ LPWSTR: :pointer, # Pointer to a null-terminated string of 16-bit Unicode characters.
207
+ LRESULT: :long, # Signed result of message processing. WinDef.h: typedef LONG_PTR LRESULT;
208
+ PBOOL: :pointer, # Pointer to a BOOL.
209
+ PBOOLEAN: :pointer, # Pointer to a BOOL.
210
+ PBYTE: :pointer, # Pointer to a BYTE.
211
+ PCHAR: :pointer, # Pointer to a CHAR.
212
+ PCSTR: :pointer, # Pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters.
213
+ PCTSTR: :pointer, # A PCWSTR if UNICODE is defined, a PCSTR otherwise.
214
+ PCWSTR: :pointer, # Pointer to a constant null-terminated string of 16-bit Unicode characters.
215
+ PDWORD: :pointer, # Pointer to a DWORD.
216
+ PDWORDLONG: :pointer, # Pointer to a DWORDLONG.
217
+ PDWORD_PTR: :pointer, # Pointer to a DWORD_PTR.
218
+ PDWORD32: :pointer, # Pointer to a DWORD32.
219
+ PDWORD64: :pointer, # Pointer to a DWORD64.
220
+ PFLOAT: :pointer, # Pointer to a FLOAT.
221
+ PHALF_PTR: :pointer, # Pointer to a HALF_PTR.
222
+ PHANDLE: :pointer, # Pointer to a HANDLE.
223
+ PHKEY: :pointer, # Pointer to an HKEY.
224
+ PINT: :pointer, # Pointer to an INT.
225
+ PINT_PTR: :pointer, # Pointer to an INT_PTR.
226
+ PINT32: :pointer, # Pointer to an INT32.
227
+ PINT64: :pointer, # Pointer to an INT64.
228
+ PLCID: :pointer, # Pointer to an LCID.
229
+ PLONG: :pointer, # Pointer to a LONG.
230
+ PLONGLONG: :pointer, # Pointer to a LONGLONG.
231
+ PLONG_PTR: :pointer, # Pointer to a LONG_PTR.
232
+ PLONG32: :pointer, # Pointer to a LONG32.
233
+ PLONG64: :pointer, # Pointer to a LONG64.
234
+ 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.
235
+ 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.
236
+ POINTER_SIGNED: :pointer, # A signed pointer.
237
+ POINTER_UNSIGNED: :pointer, # An unsigned pointer.
238
+ PSHORT: :pointer, # Pointer to a SHORT.
239
+ PSIZE_T: :pointer, # Pointer to a SIZE_T.
240
+ PSSIZE_T: :pointer, # Pointer to a SSIZE_T.
241
+ PSTR: :pointer, # Pointer to a null-terminated string of 8-bit Windows (ANSI) characters. For more information, see Character Sets Used By Fonts.
242
+ PTBYTE: :pointer, # Pointer to a TBYTE.
243
+ PTCHAR: :pointer, # Pointer to a TCHAR.
244
+ PTSTR: :pointer, # A PWSTR if UNICODE is defined, a PSTR otherwise.
245
+ PUCHAR: :pointer, # Pointer to a UCHAR.
246
+ PUHALF_PTR: :pointer, # Pointer to a UHALF_PTR.
247
+ PUINT: :pointer, # Pointer to a UINT.
248
+ PUINT_PTR: :pointer, # Pointer to a UINT_PTR.
249
+ PUINT32: :pointer, # Pointer to a UINT32.
250
+ PUINT64: :pointer, # Pointer to a UINT64.
251
+ PULONG: :pointer, # Pointer to a ULONG.
252
+ PULONGLONG: :pointer, # Pointer to a ULONGLONG.
253
+ PULONG_PTR: :pointer, # Pointer to a ULONG_PTR.
254
+ PULONG32: :pointer, # Pointer to a ULONG32.
255
+ PULONG64: :pointer, # Pointer to a ULONG64.
256
+ PUSHORT: :pointer, # Pointer to a USHORT.
257
+ PVOID: :pointer, # Pointer to any type.
258
+ PWCHAR: :pointer, # Pointer to a WCHAR.
259
+ PWORD: :pointer, # Pointer to a WORD.
260
+ PWSTR: :pointer, # Pointer to a null- terminated string of 16-bit Unicode characters.
261
+ # For more information, see Character Sets Used By Fonts.
262
+ SC_HANDLE: :ulong, # (L) Handle to a service control manager database.
263
+ # See SCM Handles http://msdn.microsoft.com/en-us/library/ms685104%28VS.85%29.aspx
264
+ SC_LOCK: :pointer, # Lock to a service control manager database. For more information, see SCM Handles.
265
+ SERVICE_STATUS_HANDLE: :ulong, # (L) Handle to a service status value. See SCM Handles.
266
+ SHORT: :short, # A 16-bit integer. The range is –32768 through 32767 decimal.
267
+ 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.
268
+ SSIZE_T: :long, # Signed SIZE_T.
269
+ TBYTE: :short, # A WCHAR if UNICODE is defined, a CHAR otherwise.TCHAR:
270
+ TCHAR: :short, # A WCHAR if UNICODE is defined, a CHAR otherwise.TCHAR:
271
+ UCHAR: :uchar, # Unsigned CHAR (8 bit)
272
+ UHALF_PTR: :uint, # Unsigned HALF_PTR. Use within a structure that contains a pointer and two small fields.
273
+ UINT: :uint, # Unsigned INT. The range is 0 through 4294967295 decimal.
274
+ UINT_PTR: :uint, # Unsigned INT_PTR.
275
+ UINT32: :uint32, # Unsigned INT32. The range is 0 through 4294967295 decimal.
276
+ UINT64: :uint64, # Unsigned INT64. The range is 0 through 18446744073709551615 decimal.
277
+ ULONG: :ulong, # Unsigned LONG. The range is 0 through 4294967295 decimal.
278
+ ULONGLONG: :ulong_long, # 64-bit unsigned integer. The range is 0 through 18446744073709551615 decimal.
279
+ ULONG_PTR: :ulong, # Unsigned LONG_PTR.
280
+ ULONG32: :uint32, # Unsigned INT32. The range is 0 through 4294967295 decimal.
281
+ ULONG64: :uint64, # Unsigned LONG64. The range is 0 through 18446744073709551615 decimal.
282
+ UNICODE_STRING: :pointer, # Pointer to some string structure??
283
+ USHORT: :ushort, # Unsigned SHORT. The range is 0 through 65535 decimal.
284
+ USN: :ulong_long, # Update sequence number (USN).
285
+ VOID: [], # Any type ? Only use it to indicate no arguments or no return value
286
+ WCHAR: :ushort, # 16-bit Unicode character. For more information, see Character Sets Used By Fonts.
287
+ # In WinNT.h: typedef wchar_t WCHAR;
288
+ #WINAPI: K, # Calling convention for system functions. WinDef.h: define WINAPI __stdcall
289
+ WORD: :ushort, # 16-bit unsigned integer. The range is 0 through 65535 decimal.
290
+ WPARAM: :uint # Message parameter. WinDef.h as follows: typedef UINT_PTR WPARAM;
291
+ }
292
+
293
+ ##
294
+ # Defines new method wrappers for Windows API function call:
295
+ # - Defines method with original (CamelCase) API function name and original signature (matches MSDN description)
296
+ # - Defines method with snake_case name (converted from CamelCase function name) with enhanced API signature
297
+ # When defined snake_case method is called, it converts the arguments you provided into ones required by
298
+ # original API (adding defaults, mute and transitory args as necessary), executes API function call
299
+ # and (optionally) transforms the result before returning it. If a block is attached to
300
+ # method invocation, raw result is yielded to this block before final transformation take place
301
+ # - Defines aliases for enhanced method with more Rubyesque names for getters, setters and tests:
302
+ # GetWindowText -> window_test, SetWindowText -> window_text=, IsZoomed -> zoomed?
303
+ # ---
304
+ # You may modify default behavior of defined method by providing optional *def_block* to function definition.
305
+ # If you do so, snake_case method is defined based on your *def_block*. It receives callable API
306
+ # object for function being defined, arguments and (optional) runtime block with which the method
307
+ # will be called. Results coming from &def_block are then transformed and returned.
308
+ # So, your *def_block* should specify all the behavior of the method being defined. You can use *def_block* to:
309
+ # - Change original signature of API function, provide argument defaults, check argument types
310
+ # - Pack arguments into strings/structs for [in] or [in/out] parameters that expect a pointer
311
+ # - Allocate buffers/structs for pointers required by API functions [out] parameters
312
+ # - Unpack [out] and [in/out] parameters returned as pointers
313
+ # - Explicitly return results of API call that are returned in [out] and [in/out] parameters
314
+ # - Convert attached runtime blocks into callback functions and stuff them into [in] callback parameters
315
+ # - do other stuff that you think is appropriate to make Windows API function behavior more Ruby-like...
316
+ # ---
317
+ # Accepts following options:
318
+ # :dll:: Use this dll instead of default 'user32'
319
+ # :rename:: Use this name instead of standard (conventional) function name
320
+ # :alias(es):: Provides additional alias(es) for defined method
321
+ # :boolean:: Forces method to return true/false instead of nonzero/zero
322
+ # :zeronil:: Forces method to return nil if function result is zero
323
+ #
324
+ def function(name, params, returns, options={}, &def_block)
325
+ snake_name, effective_names, aliases = generate_names(name, options)
326
+ params, returns = generate_signature(params, returns)
327
+ libs = ffi_libraries.map(&:name)
328
+ boolean = options[:boolean]
329
+ zeronil = options[:zeronil]
330
+
331
+ effective_name = effective_names.inject(nil) do |func, effective_name|
332
+ func || begin
333
+ # tries to attach basic CamelCase method via FFI
334
+ attach_function(name, effective_name, params.dup, returns)
335
+ effective_name
336
+ rescue FFI::NotFoundError
337
+ nil
338
+ end
339
+ end
340
+
341
+ raise Win::Errors::NotFoundError.new(name, libs) unless effective_name
342
+
343
+ # Create API object that holds information about function names, params, etc
344
+ api = API.new(namespace, name, effective_name, params, returns, libs)
345
+
346
+ # Only define enhanced API if snake_name is different from original name (e.g. keybd_event)
347
+ unless snake_name.to_s == name.to_s
348
+ method_body = if def_block
349
+ if zeronil
350
+ ->(*args, &block){ (res = def_block.(api, *args, &block)) != 0 ? res : nil }
351
+ elsif boolean
352
+ ->(*args, &block){ def_block.(api, *args, &block) != 0 }
353
+ else
354
+ ->(*args, &block){ def_block.(api, *args, &block) }
355
+ end
356
+ else
357
+ if zeronil
358
+ ->(*args, &block){ (res = block ? block[api.call(*args)] : api.call(*args)) != 0 ? res : nil }
359
+ elsif boolean
360
+ ->(*args, &block){ block ? block[api.call(*args)] : api.call(*args) != 0 }
361
+ else
362
+ ->(*args, &block){ block ? block[api.call(*args)] : api.call(*args) }
363
+ end
364
+ end
365
+
366
+ define_method snake_name, &method_body # define snake_case instance method
367
+
368
+ eigenklass = class << self; self; end # Extracting eigenclass
369
+ eigenklass.class_eval do
370
+ define_method snake_name, &method_body # define snake_case class method
371
+ end
372
+ end
373
+
374
+ aliases.each {|ali| alias_method ali, snake_name } # define aliases
375
+ api #return api object from function declaration
376
+ end
377
+
378
+ # Try to define platform-specific function, rescue error, return message
379
+ #
380
+ def try_function(name, params, returns, options={}, &def_block)
381
+ begin
382
+ function name, params, returns, options={}, &def_block
383
+ rescue Win::Errors::NotFoundError
384
+ "This platform does not support function #{name}"
385
+ end
386
+ end
387
+
388
+ # Generates possible effective names for function in Win32 dll (name+A/W),
389
+ # Rubyesque name and aliases for method(s) defined based on function name,
390
+ #
391
+ def generate_names(name, options={})
392
+ name = name.to_s
393
+ effective_names = [name]
394
+ effective_names += ["#{name}A", "#{name}W"] unless name =~ /[WA]$/
395
+ aliases = ([options[:alias]] + [options[:aliases]]).flatten.compact
396
+ snake_name = options[:rename] || name.snake_case
397
+ case snake_name
398
+ when /^is_/
399
+ aliases << snake_name.sub(/^is_/, '') + '?'
400
+ when /^set_/
401
+ aliases << snake_name.sub(/^set_/, '')+ '='
402
+ when /^get_/
403
+ aliases << snake_name.sub(/^get_/, '')
404
+ end
405
+ [snake_name, effective_names, aliases]
406
+ end
407
+
408
+ ##
409
+ # Generates params and returns (signature) containing only FFI-compliant types
410
+ #
411
+ def generate_signature(params, returns)
412
+ params = params.split(//) if params.respond_to?(:split) # Convert params string into array
413
+ params.map! {|param| TYPES[param.to_sym] || param} # Convert chars into FFI type symbols
414
+ returns = TYPES[returns.to_sym] || returns # Convert chars into FFI type symbols
415
+ [params, returns]
416
+ end
417
+
418
+ ##
419
+ # Wrapper for FFI::Library#callback() that converts Win32/shortcut argument types into FFI-compliant types.
420
+ # This method overrides FFI.callback which must be aliased to FFI.attach_callback
421
+ #
422
+ def callback(name, params, returns)
423
+ params, returns = generate_signature(params, returns)
424
+ attach_callback name.to_sym, params, returns
425
+ end
426
+
427
+ ##
428
+ # :method: namespace
429
+ # This method is meta-generated when Win::Library module is included into other module/class.
430
+ # It returns reference to including (host) class/module for use by Win::Library::API and class methods.
431
+
432
+ ##
433
+ # Ensures that args count is equal to params count plus diff
434
+ #
435
+ def enforce_count(args, params, diff = 0)
436
+ num_args = args.size
437
+ num_params = params.size + diff #params == 'V' ? 0 : params.size + diff
438
+ if num_args != num_params
439
+ raise ArgumentError, "wrong number of arguments (#{num_args} for #{num_params})"
440
+ end
441
+ end
442
+
443
+ end
444
+
445
+ ##
446
+ # Hook executed when Win::Library is included into class or module. It extends host class/module
447
+ # with both FFI::Library methods and Win::Library macro methods like 'function'.
448
+ #
449
+ def self.included(klass)
450
+ klass.extend FFI::Library
451
+
452
+ eigenklass = class << klass; self; end # Extracting host class's eigenclass
453
+ eigenklass.class_eval do
454
+ define_method(:namespace) {klass} # Defining new class method for host pointing to itself
455
+ alias_method :attach_callback, :callback
456
+
457
+ include ClassMethods
458
+ end
459
+
460
+ klass.ffi_lib 'user32', 'kernel32' # Default libraries
461
+ klass.ffi_convention :stdcall
462
+ end
463
+ end
464
464
  end