win_gui 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -14,6 +14,64 @@ elements (such as WinGui::Window class and its methods).
14
14
 
15
15
  It is still work in progress, only a small number of API functions wrapped so far...
16
16
 
17
+ == SUMMARY
18
+
19
+ So you want to write a simple program that makes some Win32 API function calls.
20
+ You searched MSDN and you know what functions you'll need. Now you just want to
21
+ put those calls into your Ruby code. You'd love it to be a natural extension of
22
+ Ruby code, preferably not turning your code base into a ugly C++ like spaghetty
23
+ of CamelCase calls, String/Array pack/unpack gymnastics, buffer allocations,
24
+ extracting return values from [in/out] parameters and checking return codes for 0.
25
+ You can definitely use excellent 'win32-api' gem by Daniel J. Berger and Park Heesob
26
+ that allows you to define Win32 API objects for any function you can find on MSDN,
27
+ execute calls on them and even define callback objects that some of those API functions expect.
28
+
29
+ However, this gem will only take you so far. You'll still have to handle (somewhat)
30
+ gory details of argument preparation, mimicking pointers with Strings and stuff.
31
+ For example, consider the amount of code needed to complete a task as simple as
32
+ getting unicode title text for the window that you already have handle for:
33
+
34
+ api = Win32::API.new( 'GetWindowTextW', ['L', 'P', 'I'], 'L', 'user32' )
35
+ buffer = "\x00" * 1024
36
+ num_chars = api.call( window_handle, buffer, buffer.size)
37
+ title = if num_chars == 0
38
+ nil
39
+ else
40
+ buffer.force_encoding('utf-16LE').encode('utf-8').rstrip
41
+ end
42
+
43
+ Ew, ugly. What about getting information about process id for a known window?
44
+
45
+ api = Win32::API.new( 'GetWindowThreadProcessId', ['L', 'P'], 'L' , 'user32' )
46
+ process_packed = [1].pack('L')
47
+ thread = api.call(window_handle, process_packed)
48
+ process = process.unpack('L').first
49
+
50
+ Wow, packing and unpacking arrays into String to get hold of a simple integer id.
51
+ Just great. Now, wouldn't it be MUCH better if you can just say something like this:
52
+
53
+ title = window_text( window_handle)
54
+ thread, process = window_thread_process_id( window_handle)
55
+
56
+ What about API functions expecting callbacks? Well, something like this may be nice:
57
+
58
+ enum_child_windows( parent_handle, message ){|child_handle, message| puts child_handle }
59
+
60
+ If you think about it, callbacks are not much than more than code blocks, so let's not
61
+ be afraid to treat them as such. It would be also good if test functions return true/false
62
+ instead of zero/nonzero, find functions return nil if nothing was found etc...
63
+
64
+ So this is an idea behind WinGui library - make Win32 API functions more fun to use
65
+ and feel more natural inside Ruby code. Following the principle of least surprise, we
66
+ define methods with Rubyesque names (minimized? instead of IsMinimized, etc), minimum
67
+ arguments with sensible defaults, explicit return values and generous use of attached blocks.
68
+
69
+ Well, we even keep a backup solution for those diehard Win32 API longtimers who would rather
70
+ allocate their buffer strings by hand and mess with obscure return codes. If you use original
71
+ CamelCase method name instead of Rubyesque snake_case one, it will expect those standard
72
+ parameters you know and love from MSDN, return your zeroes instead of nils and support no
73
+ other enhancements. Enjoy!
74
+
17
75
  == DOCUMENTATION:
18
76
 
19
77
  See WinGui and WinGui::Window for documentation
@@ -25,7 +83,7 @@ arguments given to block, etc...)
25
83
 
26
84
  == FEATURES/PROBLEMS:
27
85
 
28
- This project is quite new, so it's probably not ready for prime time just yet...
86
+ This project is quite new, so it's may be not suitable for production-quality systems
29
87
  Contributors always welcome!
30
88
 
31
89
  == INSTALL:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.1.3
@@ -1,71 +1,82 @@
1
1
  module WinGui
2
2
 
3
- # WinGui Module internal Constants:
4
-
5
- WG_KEY_DELAY = 0.00001
6
- WG_SLEEP_DELAY = 0.001
3
+ # Delay between key commands (events)
4
+ WG_KEY_DELAY = 0.00001
5
+ # Wait delay quant
6
+ WG_SLEEP_DELAY = 0.001
7
+ # Timeout waiting for Window to be closed
7
8
  WG_CLOSE_TIMEOUT = 1
8
- #WG_TEXT_BUFFER = '\0' * 2048
9
-
9
+
10
10
  # Windows keyboard-related Constants:
11
11
  # Virtual key codes:
12
12
 
13
- VK_CANCEL = 0x03 # Control-break processing
13
+
14
+ # Control-break processing
15
+ VK_CANCEL = 0x03
16
+ # Backspace? key
14
17
  VK_BACK = 0x08
18
+ # Tab key
15
19
  VK_TAB = 0x09
20
+ # Shift key
16
21
  VK_SHIFT = 0x10
22
+ # Ctrl key
17
23
  VK_CONTROL = 0x11
18
- VK_RETURN = 0x0D # ENTER key
19
- VK_ALT = 0x12 # ALT key
20
- VK_MENU = 0x12 # ALT key alias
21
- VK_PAUSE = 0x13 # PAUSE key
22
- VK_CAPITAL = 0x14 # CAPS LOCK key
23
- VK_ESCAPE = 0x1B # ESC key
24
- VK_SPACE = 0x20 # SPACEBAR
25
- VK_PRIOR = 0x21 # PAGE UP key
26
- VK_NEXT = 0x22 # PAGE DOWN key
27
- VK_END = 0x23 # END key
28
- VK_HOME = 0x24 # HOME key
29
- VK_LEFT = 0x25 # LEFT ARROW key
30
- VK_UP = 0x26 # UP ARROW key
31
- VK_RIGHT = 0x27 # RIGHT ARROW key
32
- VK_DOWN = 0x28 # DOWN ARROW key
33
- VK_SELECT = 0x29 # SELECT key
34
- VK_PRINT = 0x2A # PRINT key
35
- VK_EXECUTE = 0x2B # EXECUTE key
36
- VK_SNAPSHOT = 0x2C # PRINT SCREEN key
37
- VK_INSERT = 0x2D # INS key
38
- VK_DELETE = 0x2E # DEL key
39
- VK_HELP = 0x2F # HELP key
40
-
41
- # Key events:
24
+ # ENTER key
25
+ VK_RETURN = 0x0D
26
+ # ALT key
27
+ VK_ALT = 0x12
28
+ # ALT key alias
29
+ VK_MENU = 0x12
30
+ # PAUSE key
31
+ VK_PAUSE = 0x13
32
+ # CAPS LOCK key
33
+ VK_CAPITAL = 0x14
34
+ # ESC key
35
+ VK_ESCAPE = 0x1B
36
+ # SPACEBAR
37
+ VK_SPACE = 0x20
38
+ # PAGE UP key
39
+ VK_PRIOR = 0x21
40
+ # PAGE DOWN key
41
+ VK_NEXT = 0x22
42
+ # END key
43
+ VK_END = 0x23
44
+ # HOME key
45
+ VK_HOME = 0x24
46
+ # LEFT ARROW key
47
+ VK_LEFT = 0x25
48
+ # UP ARROW key
49
+ VK_UP = 0x26
50
+ # RIGHT ARROW key
51
+ VK_RIGHT = 0x27
52
+ # DOWN ARROW key
53
+ VK_DOWN = 0x28
54
+ # SELECT key
55
+ VK_SELECT = 0x29
56
+ # PRINT key
57
+ VK_PRINT = 0x2A
58
+ # EXECUTE key
59
+ VK_EXECUTE = 0x2B
60
+ # PRINT SCREEN key
61
+ VK_SNAPSHOT = 0x2C
62
+ # INS key
63
+ VK_INSERT = 0x2D
64
+ # DEL key
65
+ VK_DELETE = 0x2E
66
+ # HELP key
67
+ VK_HELP = 0x2F
42
68
 
69
+ # Key down keyboard event
43
70
  KEYEVENTF_KEYDOWN = 0
44
- KEYEVENTF_KEYUP = 2
45
-
46
- # Show Window Commands:
47
-
48
- SW_HIDE = 0
49
- SW_NORMAL = 1
50
- SW_SHOWNORMAL = 1
51
- SW_SHOWMINIMIZED = 2
52
- SW_SHOWMAXIMIZED = 3
53
- SW_MAXIMIZE = 3
54
- SW_SHOWNOACTIVATE = 4
55
- SW_SHOW = 5
56
- SW_MINIMIZE = 6
57
- SW_SHOWMINNOACTIVE= 7
58
- SW_SHOWNA = 8
59
- SW_RESTORE = 9
60
- SW_SHOWDEFAULT = 10
61
- SW_FORCEMINIMIZE = 11
71
+ # Key up keyboard event
72
+ KEYEVENTF_KEYUP = 2
62
73
 
63
- # Windows Messages Constants:
64
-
74
+ # Windows Message Get Text
65
75
  WM_GETTEXT = 0x000D
76
+ # Windows Message Sys Command
66
77
  WM_SYSCOMMAND = 0x0112
78
+ # Sys Command Close
67
79
  SC_CLOSE = 0xF060
68
80
 
69
- # Other Windows Constants:
70
81
  end
71
82
 
@@ -1,66 +1,97 @@
1
+ require 'Win32/api'
2
+
1
3
  module WinGui
2
4
  module DefApi
5
+ # DLL to use with API decarations by default ('user32')
3
6
  DEFAULT_DLL = 'user32'
4
7
 
5
- # Defines new instance method wrapper for Windows API function call. Converts CamelCase function name
6
- # into snake_case method name, renames test functions according to Ruby convention (IsWindow -> window?)
7
- # When the defined wrapper method is called, it executes underlying API function call, yields the result
8
- # to attached block (if any) and (optionally) transforms the result before returning it.
8
+ # Defines new method wrappers for Windows API function call:
9
+ # - Defines method with original (CamelCase) API function name and original signature (matches MSDN description)
10
+ # - Defines method with snake_case name (converted from CamelCase function name) with enhanced API signature
11
+ # When the defined wrapper method is called, it checks the argument count, executes underlying API
12
+ # function call and (optionally) transforms the result before returning it. If block is attached to
13
+ # method invocation, raw result is yielded to this block before final transformations
14
+ # - Defines aliases for enhanced method with more Rubyesque names for getters, setters and tests:
15
+ # GetWindowText -> window_test, SetWindowText -> window_text=, IsZoomed -> zoomed?
9
16
  #
10
- # You may modify default defined method behavior by providing optional &define_block to def_api.
11
- # If you do so, instead of directly calling API function, defined method yields callable api object, arguments
12
- # and (optional) runtime block to &define_block that should define method content and return result.
17
+ # You may modify default behavior of defined method by providing optional &define_block to def_api.
18
+ # If you do so, instead of directly calling API function, defined method just yields callable api
19
+ # object, arguments and (optional) runtime block to your &define_block and returns result coming out of it.
20
+ # So, &define_block should define all the behavior of defined method. You can use define_block to:
21
+ # - Change original signature of API function, provide argument defaults, check argument types
22
+ # - Pack arguments into strings for [in] or [in/out] parameters that expect a pointer
23
+ # - Allocate string buffers for pointers required by API functions [out] parameters
24
+ # - Unpack [out] and [in/out] parameters returned as pointers
25
+ # - Explicitly return results of API call that are returned in [out] and [in/out] parameters
26
+ # - Convert attached runtime blocks into callback functions and stuff them into [in] callback parameters
13
27
  #
14
28
  # Accepts following options:
15
- # :dll:: Use this dll instead of default 'user32'
16
- # :rename:: Use this name instead of standard (conventional) function name
17
- # :alias(es):: Provides additional alias(es) for defined method
18
- # :boolean:: Forces method to return true/false instead of nonzero/zero
19
- # :zeronil:: Forces method to return nil if function result is zero
29
+ # :dll:: Use this dll instead of default 'user32'
30
+ # :rename:: Use this name instead of standard (conventional) function name
31
+ # :alias(es):: Provides additional alias(es) for defined method
32
+ # :boolean:: Forces method to return true/false instead of nonzero/zero
33
+ # :zeronil:: Forces method to return nil if function result is zero
20
34
  #
21
35
  def def_api(function, params, returns, options={}, &define_block)
36
+ aliases = ([options[:alias]] + [options[:aliases]]).flatten.compact
22
37
  name = options[:rename] || function.snake_case
23
- if name.sub!(/^is_/, '')
24
- name << '?'
25
- boolean = true
38
+ case name
39
+ when /^is_/
40
+ aliases << name.sub(/^is_/, '') + '?'
41
+ boolean = true
42
+ when /^set_/
43
+ aliases << name.sub(/^set_/, '')+ '='
44
+ when /^get_/
45
+ aliases << name.sub(/^get_/, '')
26
46
  end
27
47
  boolean ||= options[:boolean]
28
48
  zeronil = options[:zeronil]
29
- aliases = ([options[:alias]] + [options[:aliases]]).flatten.compact
30
49
  proto = params.respond_to?(:join) ? params.join : params # Converts params into prototype string
31
50
  api = Win32::API.new(function, proto.upcase, returns.upcase, options[:dll] || DEFAULT_DLL)
32
51
 
33
- define_method(name) do |*args, &runtime_block|
52
+ define_method(function) {|*args| api.call(*args)} # defines CamelCase method wrapper for api call
53
+
54
+ define_method(name) do |*args, &runtime_block| # defines snake_case method with enhanced api
34
55
  return api if args == [:api]
35
56
  return define_block.call(api, *args, &runtime_block) if define_block
36
- raise 'Invalid args count' unless args.size == params.size
57
+ unless args.size == params.size
58
+ raise ArgumentError, "wrong number of parameters: expected #{params.size}, got #{args.size}"
59
+ end
37
60
  result = api.call(*args)
38
- yield result if runtime_block
61
+ result = runtime_block[result] if runtime_block
39
62
  return result != 0 if boolean # Boolean function returns true/false instead of nonzero/zero
40
63
  return nil if zeronil && result == 0
41
64
  result
42
65
  end
43
- aliases.each {|aliass| alias_method aliass, name } unless aliases == []
66
+ aliases.each {|ali| alias_method ali, name } unless aliases == []
44
67
  end
45
68
 
46
- # Helper methods:
47
-
48
69
  # Converts block into API::Callback object that can be used as API callback argument
70
+ #
49
71
  def callback(params, returns, &block)
50
72
  Win32::API::Callback.new(params, returns, &block)
51
73
  end
52
74
 
75
+ private # Helper methods:
76
+
53
77
  # Returns string buffer - used to supply string pointer reference to API functions
78
+ #
54
79
  def buffer(size = 1024, code = "\x00")
55
80
  code * size
56
81
  end
57
82
 
58
83
  # Procedure that returns (possibly encoded) string as a result of api function call
84
+ # or nil if zero characters was returned by api call
85
+ #
59
86
  def return_string( encode = nil )
60
87
  lambda do |api, *args|
61
- raise 'Invalid args count' unless args.size == api.prototype.size-2
88
+ num_params = api.prototype.size-2
89
+ unless args.size == num_params
90
+ raise ArgumentError, "wrong number of parameters: expected #{num_params}, got #{args.size}"
91
+ end
62
92
  args += [string = buffer, string.length]
63
- num_chars = api.call(*args) # num_chars not used
93
+ num_chars = api.call(*args)
94
+ return nil if num_chars == 0
64
95
  string = string.force_encoding('utf-16LE').encode(encode) if encode
65
96
  string.rstrip
66
97
  end
@@ -69,9 +100,13 @@ module WinGui
69
100
  # Procedure that calls api function expecting a callback. If runtime block is given
70
101
  # it is converted into callback, otherwise procedure returns an array of all handles
71
102
  # pushed into callback by api enumeration
103
+ #
72
104
  def return_enum
73
105
  lambda do |api, *args, &block|
74
- raise 'Invalid args count' unless args.size == api.prototype.size-1
106
+ num_params = api.prototype.size-1
107
+ unless args.size == num_params
108
+ raise ArgumentError, "wrong number of parameters: expected #{num_params}, got #{args.size}"
109
+ end
75
110
  handles = []
76
111
  cb = if block
77
112
  callback('LP', 'I', &block)
@@ -87,5 +122,21 @@ module WinGui
87
122
  end
88
123
  end
89
124
 
125
+ # Procedure that calls (DdeInitialize) function expecting a DdeCallback. Runtime block is converted
126
+ # into Dde callback and registered with DdeInitialize. Returns DDE init status and DDE instance id.
127
+ #
128
+ # TODO: Pushed into this module since RubyMine (wrongly) reports error on lambda args
129
+ #
130
+ def return_id_status
131
+ lambda do |api, id=0, cmd, &block|
132
+ raise ArgumentError, 'No callback block' unless block
133
+ callback = callback 'IIPPPPPP', 'L', &block
134
+ id = [id].pack('L')
135
+
136
+ status = api.call(id, callback, cmd, 0)
137
+ [*id.unpack('L'), status]
138
+ end
139
+ end
140
+
90
141
  end
91
142
  end