windows-api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES ADDED
@@ -0,0 +1,2 @@
1
+ = 0.1.0 - 24-May-2007
2
+ * Initial release
data/MANIFEST ADDED
@@ -0,0 +1,9 @@
1
+ * CHANGES
2
+ * MANIFEST
3
+ * README
4
+ * Rakefile
5
+ * windows-api.gemspec
6
+ * ext/extconf.rb
7
+ * ext/windows/api.c
8
+ * lib/windows/api.rb
9
+ * test/tc_windows_api.rb
data/README ADDED
@@ -0,0 +1,82 @@
1
+ = Description
2
+ This is a wrapper for Win32API that simplifies various idioms typically
3
+ used by people who use Win32API.
4
+
5
+ = Synopsis
6
+ require 'windows/api'
7
+ include Windows
8
+
9
+ # Defaults to 'V' prototype, 'L' return type and 'kernel32' library
10
+ GetVersion = API.new('GetVersion')
11
+
12
+ # Defaults to 'L' return type and 'kernel32' library
13
+ CloseHandle = API.new('CloseHandle', 'L')
14
+
15
+ # Defaults to 'kernel32' library
16
+ GetWindowsDirectory = API.new('GetWindowsDirectory', 'LI', 'I')
17
+
18
+ # Explicitly state every argument
19
+ GetComputerNameEx = API.new('GetComputerNameEx', 'PPP', 'I', 'kernel32')
20
+
21
+ # Attributes for possible inspection
22
+ puts GetVersion.dll_name # 'kernel32'
23
+ puts GetVersion.function_name # 'GetVersion'
24
+ puts GetVersion.prototype # ['V']
25
+ puts GetVersion.return_type # 'L'
26
+
27
+ # Automatic method generation
28
+
29
+ # This code....
30
+ module Windows
31
+ module Foo
32
+ API.auto_namespace = 'Windows::Foo'
33
+ API.new('GetComputerName', 'PP', 'B')
34
+ end
35
+ end
36
+
37
+ # Is the same as this code...
38
+ module Windows
39
+ module Foo
40
+ GetComputerName = Win32API.new('kernel32', 'GetComputerName', 'PP', 'I')
41
+ GetComputerNameA = Win32API.new('kernel32', GetComputerNameA', 'PP', 'I')
42
+ GetComputerNameW = Win32API.new('kernel32', 'GetComputerNameW', 'PP', 'I')
43
+
44
+ def GetComputerName(p1, p2)
45
+ GetComputerName.call(p1, p2) != 0
46
+ end
47
+
48
+ def GetComputerNameA(p1, p2)
49
+ GetComputerName.call(p1, p2) != 0
50
+ end
51
+
52
+ def GetComputerNameW(p1, p2)
53
+ GetComputerName.call(p1, p2) != 0
54
+ end
55
+ end
56
+ end
57
+
58
+ = Advantages over plain Win32API
59
+ * Automatic constant generation.
60
+ * Automatic definition of ANSI and Unicode method wrappers, including
61
+ special handling for boolean methods.
62
+ * Most arguments to the constructor are optional, with sensible defaults.
63
+ * Four attributes added to API objects - dll_name, function_name, prototype
64
+ and return_type.
65
+
66
+ = More documentation
67
+ See the RDoc documentation, which should have been automatically generated
68
+ if you installed this as a gem.
69
+
70
+ = Bugs
71
+ None that I'm aware of. Please submit any bugs to the project page at
72
+ http://www.rubyforge.org/projects/win32utils.
73
+
74
+ = Copyright
75
+ (C) 2007, Daniel J. Berger
76
+
77
+ = License
78
+ Ruby's
79
+
80
+ = Author
81
+ Daniel Berger
82
+ djberg96 at gmail dot com
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+ require 'rbconfig'
5
+ include Config
6
+
7
+ desc 'Install the windows-api package (non-gem)'
8
+ task :install do
9
+ sitelibdir = CONFIG["sitelibdir"]
10
+ installdir = File.join(sitelibdir, 'windows')
11
+ Dir.mkdir(installdir) unless File.exists?(installdir)
12
+ file = "lib/windows/api.rb"
13
+ FileUtils.cp(file, installdir, :verbose => true)
14
+ end
15
+
16
+ task :install_gem do
17
+ ruby 'windows-api.gemspec'
18
+ file = Dir["*.gem"].first
19
+ sh "gem install #{file}"
20
+ end
21
+
22
+ desc "Clean any build files for Windows::API"
23
+ task :clean do
24
+ Dir.chdir('ext') do
25
+ if File.exists?("api.so") || File.exists?("windows/api.so")
26
+ sh "nmake distclean"
27
+ rm "api.c" if File.exists?("api.c")
28
+ rm "windows/api.so" if File.exists?("windows/api.so")
29
+ end
30
+ end
31
+ end
32
+
33
+ desc "Build Windows::API (but don't install it)"
34
+ task :build_c => [:clean] do
35
+ Dir.chdir('ext') do
36
+ cp "windows/api.c", "."
37
+ sh "ruby extconf.rb"
38
+ sh "nmake"
39
+ mv "api.so", "windows"
40
+ end
41
+ end
42
+
43
+ Rake::TestTask.new('test_c') do |test|
44
+ task :test_c => [:build_c]
45
+ test.libs << 'ext'
46
+ test.test_files = FileList['test/tc*']
47
+ end
48
+
49
+ Rake::TestTask.new do |test|
50
+ test.libs << 'lib'
51
+ test.warning = true
52
+ test.verbose = true
53
+ test.test_files = FileList['test/tc*']
54
+ end
@@ -0,0 +1,358 @@
1
+ require 'Win32API'
2
+
3
+ module Windows
4
+ class API
5
+ VERSION = '0.1.0'
6
+
7
+ class Error < RuntimeError; end
8
+
9
+ @@auto_constant = false
10
+ @@auto_method = false
11
+ @@auto_unicode = false
12
+ @@auto_namespace = nil
13
+
14
+ # Returns the value of the @@auto_constant class variable. The default
15
+ # is nil, i.e. none. See the Windows::API.auto_constant= documentation
16
+ # for more information.
17
+ #
18
+ def self.auto_constant
19
+ @@auto_constant
20
+ end
21
+
22
+ # Automatically sets a constant to match the function name.
23
+ #
24
+ # The standard practice for defining Windows::API objects is to use
25
+ # a constant that matches the function name. For example, this is a
26
+ # typical idiom:
27
+ #
28
+ # module Windows
29
+ # module File
30
+ # GetFileAttributes = API.new('GetFileAttributes', 'P','L')
31
+ # end
32
+ # end
33
+ #
34
+ # With the API.auto_constant value set to true you can avoid the
35
+ # assignment step and the matching constant name will be automatically
36
+ # set for you in the namespace defined in API.auto_namespace. In other
37
+ # words, this example is identical to the one above:
38
+ #
39
+ # module Windows
40
+ # module File
41
+ # API.auto_constant = true
42
+ # API.auto_namespace = 'Windows::File'
43
+ # API.new('GetFileAttributes', 'P', 'L')
44
+ # end
45
+ # end
46
+ #
47
+ # If the auto_constant class variable is set to true, but no
48
+ # auto_namespace is set, an error will be raised. Note that the
49
+ # namespace must refer to an existing module (not a class).
50
+ #--
51
+ # TODO: If there's a way to automatically grab the namespace internally,
52
+ # nesting and all, I'd love to know the solution.
53
+ #
54
+ def self.auto_constant=(bool)
55
+ @@auto_constant = bool
56
+ end
57
+
58
+ # Returns the value of the auto_namespace class variable. Used in
59
+ # conjunction with API.auto_constant and/or API.auto_method.
60
+ #
61
+ def self.auto_namespace
62
+ @@auto_namespace
63
+ end
64
+
65
+ # Sets the value of the auto_namespace class variable. The default is
66
+ # nil, i.e. none. Use in conjunction with the auto_constant and/or
67
+ # auto_method class variables, this method will automatically set a
68
+ # constant and/or method in +namespace+ equal to the function name set
69
+ # in the constructor.
70
+ #
71
+ # The +namespace+ must refer to an existing module, not a class.
72
+ #
73
+ def self.auto_namespace=(namespace)
74
+ @@auto_namespace = namespace
75
+ end
76
+
77
+ # Returns the value of the auto_method class variable. Used in
78
+ # conjunction with auto_unicode. See API.auto_method= for more
79
+ # information.
80
+ #
81
+ def self.auto_method
82
+ @@auto_method
83
+ end
84
+
85
+ # If this option is set to true then a corresponding method is
86
+ # automatically generated when you create a new Windows::API object.
87
+ #
88
+ # For example, instead of doing this:
89
+ #
90
+ # module Windows
91
+ # module File
92
+ # GetFileAttributes = API.new('GetFileAttributes', 'P', 'L')
93
+ #
94
+ # def GetFileAttributes(x)
95
+ # GetFileAttributes.call(x)
96
+ # end
97
+ # end
98
+ # end
99
+ #
100
+ # You can do this, and have the method autogenerated for you.
101
+ #
102
+ # module Windows
103
+ # module File
104
+ # API.auto_namespace = 'Windows::File'
105
+ # API.auto_constant = true
106
+ # API.auto_method = true
107
+ # GetFileAttributes = API.new('GetFileAttributes', 'P', 'L')
108
+ # end
109
+ # end
110
+ #
111
+ # If the Windows::API object is declared to be a boolean in the
112
+ # constructor, then the method definition automatically includes a
113
+ # '!= 0' clause at the end of the call. That way, you can do
114
+ # 'if SomeMethod(x)' instead of having to do 'if SomeMethod(x) != 0',
115
+ # and it will do the right thing.
116
+ #
117
+ # If the API.auto_unicode option is also set to true, then you will
118
+ # get three method definitions. The standard function name, the explicit
119
+ # ANSI ('A') version and the explicit Unicode/wide version ('W'). The
120
+ # exception to this rule is that the explicit ANSI and Unicode methods
121
+ # will NOT be generated if the function passed to the constructor
122
+ # already ends with 'A' or 'W'.
123
+ #
124
+ def self.auto_method=(bool)
125
+ @@auto_method = bool
126
+ end
127
+
128
+ # Returns the value of the auto_unicode class variable. This is used
129
+ # in conjunction with the auto_method and/or auto_constant class
130
+ # variables. Not significant if neither of those variables are set.
131
+ #
132
+ def self.auto_unicode
133
+ @@auto_unicode
134
+ end
135
+
136
+ # If set to true, and the auto_constant variable is set, then the
137
+ # automatic constant generation will generate the explicit ANSI ('A')
138
+ # and Unicode/wide ('W') versions of the function passed to the
139
+ # constructor, if such versions exist. Likewise, if the the
140
+ # auto_method variable is set, then explicit ANSI and Unicode methods
141
+ # are generated.
142
+ #
143
+ # Here's a typical idiom:
144
+ #
145
+ # module Windows
146
+ # module Path
147
+ # API.auto_namespace = Windows::Path
148
+ # API.auto_constant = true
149
+ # API.new('shlwapi', 'PathAddBackslash', 'P', 'P')
150
+ # API.new('shlwapi', 'PathAddBackslashA', 'P', 'P')
151
+ # API.new('shlwapi', 'PathAddBackslashW', 'P', 'P')
152
+ # end
153
+ # end
154
+ #
155
+ # That can be reduced to this:
156
+ #
157
+ # module Windows
158
+ # module Path
159
+ # API.auto_namespace = Windows::Path
160
+ # API.auto_constant = true
161
+ # API.auto_unicode = true
162
+ # API.new('shlwapi', 'PathAddBackslash', 'P', 'P')
163
+ # end
164
+ # end
165
+ #
166
+ # This value is ignored if the function passed to the constructor
167
+ # already ends with an 'A' or 'W'.
168
+ #
169
+ def self.auto_unicode=(bool)
170
+ @@auto_unicode = bool
171
+ end
172
+
173
+ attr_reader :function_name
174
+ attr_reader :dll_name
175
+ attr_reader :prototype
176
+ attr_reader :return_type
177
+
178
+ # call-seq:
179
+ # API.new(func, proto='V', rtype='L', dll='kernel32')
180
+ #
181
+ # Creates and returns a new Windows::API object. The +func+ is the
182
+ # name of the Windows function.
183
+ #
184
+ # The +proto+ is the function prototype for +func+. This can be a
185
+ # string or an array of characters. The possible valid characters
186
+ # are 'I' (integer), 'B' (BOOL), 'L' (long), 'V' (void), or 'P' (pointer).
187
+ # The default is void ('V').
188
+ #
189
+ # The +rtype+ argument is the return type for the function. The valid
190
+ # characters are the same as for the +proto+. The default is long ('L').
191
+ #
192
+ # The 'B' (BOOL) return type is the same as 'I' (Integer). This is
193
+ # significant only if the API.auto_method option is set to true, in which
194
+ # case it alters the generated method definition slightly. See the
195
+ # API.auto_method= class method for more information.
196
+ #
197
+ # The +dll+ is the name of the DLL file that the function is exported
198
+ # from. The default is 'kernel32'.
199
+ #
200
+ # If the function cannot be found then an API::Error is raised (a subclass
201
+ # of RuntimeError).
202
+ #
203
+ def initialize(func, proto='V', rtype='L', dll='kernel32')
204
+ @function_name = func
205
+ @prototype = proto
206
+ @return_type = rtype == 'B' ? 'I' : rtype
207
+ @dll_name = dll
208
+ @boolean = rtype == 'B' ? true : false
209
+
210
+ begin
211
+ @api = Win32API.new(
212
+ @dll_name,
213
+ @function_name,
214
+ @prototype,
215
+ @return_type
216
+ )
217
+ rescue RuntimeError => err
218
+ raise Error, err
219
+ end
220
+
221
+ api_a = nil
222
+ api_w = nil
223
+
224
+ # If the auto_unicode option is set, and the func is not already
225
+ # an explicit ANSI or Unicode function name, generate Win32API
226
+ # objects for those functions as well. Ignore errors because not
227
+ # all functions have explicit ANSI or Unicode equivalents.
228
+ #
229
+ if @@auto_unicode
230
+ begin
231
+ if func[-1].chr != 'A'
232
+ api_a = Win32API.new(dll, "#{func}A", proto, rtype)
233
+ end
234
+ rescue RuntimeError
235
+ end
236
+
237
+ begin
238
+ if func[-1].chr != 'W'
239
+ api_w = Win32API.new(dll, "#{func}W", proto, rtype)
240
+ end
241
+ rescue RuntimeError
242
+ end
243
+ end
244
+
245
+ # Automatically define a constant matching the function name if the
246
+ # auto_constant option is set.
247
+ #
248
+ if @@auto_constant && @@auto_namespace
249
+ if @@auto_namespace != 'Windows'
250
+ namespace = class_for(@@auto_namespace)
251
+ else
252
+ namespace = @@auto_namespace
253
+ end
254
+
255
+ namespace.const_set(func, @api)
256
+
257
+ # Automatically define the explicit ANSI and Unicode functions
258
+ # as constants as well, if appropriate.
259
+ #
260
+ if @@auto_unicode
261
+ namespace.const_set("#{func}A", api_a) if api_a
262
+ namespace.const_set("#{func}W", api_w) if api_w
263
+ end
264
+ end
265
+
266
+ # Automatically define a method in the auto_namespace if the
267
+ # auto_method option is set. The explicit ANSI and Unicode methods
268
+ # are added as well if the auto_unicode option is set to true.
269
+ #
270
+ if @@auto_method && @@auto_namespace
271
+ n = 0
272
+ if proto.is_a?(String)
273
+ proto = proto.split('').map{ |e|
274
+ n += 1
275
+ e.downcase + n.to_s
276
+ }.join(', ')
277
+ else
278
+ proto = proto.map{ |e|
279
+ n += 1
280
+ e.downcase + n.to_s
281
+ }.join(', ')
282
+ end
283
+
284
+ if @boolean
285
+ string = <<-EOC
286
+ module #{@@auto_namespace}
287
+ def #{func}(#{proto})
288
+ #{func}.call(#{proto}) != 0
289
+ end
290
+ EOC
291
+
292
+ if api_a
293
+ string << %Q{
294
+ def #{func}A(#{proto})
295
+ #{func}A.call(#{proto}) != 0
296
+ end
297
+ }
298
+ end
299
+
300
+ if api_w
301
+ string << %Q{
302
+ def #{func}W(#{proto})
303
+ #{func}W.call(#{proto}) != 0
304
+ end
305
+ }
306
+ end
307
+
308
+ string << 'end'
309
+ else
310
+ string = <<-EOC
311
+ module #{@@auto_namespace}
312
+ def #{func}(#{proto})
313
+ #{func}.call(#{proto})
314
+ end
315
+ EOC
316
+
317
+ if api_a
318
+ string << %Q{
319
+ def #{func}A(#{proto})
320
+ #{func}A.call(#{proto})
321
+ end
322
+ }
323
+ end
324
+
325
+ if api_w
326
+ string << %Q{
327
+ def #{func}W(#{proto})
328
+ #{func}W.call(#{proto})
329
+ end
330
+ }
331
+ end
332
+
333
+ string << 'end'
334
+ end
335
+
336
+ eval(string)
337
+ end
338
+ end
339
+
340
+ # Calls the function name set in the constructor.
341
+ #
342
+ def call(*args)
343
+ @api.call(*args)
344
+ end
345
+
346
+ private
347
+
348
+ # Get a module's namespace. This is basically the equivalent of
349
+ # the rb_path2class() function from intern.h
350
+ #
351
+ def class_for(class_name)
352
+ names = class_name.split("::")
353
+ result = Object
354
+ names.each{ |n| result = result.const_get(n) }
355
+ result
356
+ end
357
+ end
358
+ end
@@ -0,0 +1,104 @@
1
+ ############################################################################
2
+ # tc_windows_api.rb
3
+ #
4
+ # Test case for the Windows::API class. You should run this as Rake task,
5
+ # i.e. 'rake test', instead of running it directly.
6
+ ############################################################################
7
+ require 'windows/api'
8
+ require 'test/unit'
9
+ include Windows
10
+
11
+ module Windows
12
+ module Test
13
+ API.auto_namespace = 'Windows::Test'
14
+ API.auto_unicode = true
15
+ API.auto_method = true
16
+ API.auto_constant = true
17
+ $test_method = API.new('GetCurrentDirectory', 'PP', 'L')
18
+ end
19
+
20
+ module Foo
21
+ API.auto_namespace = 'Windows::Foo'
22
+ API.auto_unicode = false
23
+ API.auto_method = false
24
+ API.auto_constant = false
25
+ $foo_method = API.new('GetSystemDirectory', 'PL', 'L')
26
+ end
27
+
28
+ module Bar
29
+ API.auto_namespace = 'Windows::Bar'
30
+ API.auto_constant = true
31
+ API.auto_method = true
32
+ $bar_method = API.new('GetUserName', 'PP', 'I', 'advapi32')
33
+ end
34
+ end
35
+
36
+ class TC_Windows_API < Test::Unit::TestCase
37
+ include Windows::Test
38
+ include Windows::Foo
39
+ include Windows::Bar
40
+
41
+ def setup
42
+ @buf = 0.chr * 256
43
+ end
44
+
45
+ def test_version
46
+ assert_equal('0.1.0', API::VERSION)
47
+ end
48
+
49
+ def test_auto_unicode
50
+ assert_not_nil(Windows::Bar::GetUserName)
51
+ assert_equal(true, self.respond_to?(:GetUserName))
52
+ assert_equal(false, self.respond_to?(:GetUserNameA))
53
+ assert_equal(false, self.respond_to?(:GetUserNameW))
54
+ end
55
+
56
+ def test_auto_constant
57
+ assert_not_nil(Windows::Test::GetCurrentDirectory)
58
+ assert_not_nil(Windows::Bar::GetUserName)
59
+
60
+ assert_kind_of(Win32API, Windows::Test::GetCurrentDirectory)
61
+ assert_respond_to(Windows::Test::GetCurrentDirectory, :call)
62
+ end
63
+
64
+ def test_auto_method
65
+ assert_respond_to(self, :GetCurrentDirectory)
66
+ assert_respond_to(self, :GetCurrentDirectoryA)
67
+ assert_respond_to(self, :GetCurrentDirectoryW)
68
+
69
+ assert_equal(false, self.respond_to?(:GetSystemDirectory))
70
+ assert_equal(false, self.respond_to?(:GetSystemDirectoryA))
71
+ assert_equal(false, self.respond_to?(:GetSystemDirectoryW))
72
+ end
73
+
74
+ def test_call
75
+ assert_respond_to($test_method, :call)
76
+ assert_respond_to($foo_method, :call)
77
+ assert_nothing_raised{ $test_method.call(@buf.length, @buf) }
78
+ assert_nothing_raised{ $foo_method.call(@buf, @buf.length) }
79
+ end
80
+
81
+ def test_dll_name
82
+ assert_respond_to($test_method, :dll_name)
83
+ assert_equal('kernel32', $test_method.dll_name)
84
+ end
85
+
86
+ def test_function_name
87
+ assert_respond_to($test_method, :function_name)
88
+ assert_equal('GetCurrentDirectory', $test_method.function_name)
89
+ end
90
+
91
+ def test_prototype
92
+ assert_respond_to($test_method, :prototype)
93
+ assert_equal('PP', $test_method.prototype)
94
+ end
95
+
96
+ def test_return_type
97
+ assert_respond_to($test_method, :return_type)
98
+ assert_equal('L', $test_method.return_type)
99
+ end
100
+
101
+ def teardown
102
+ @buf = nil
103
+ end
104
+ end
@@ -0,0 +1,23 @@
1
+ require "rubygems"
2
+
3
+ spec = Gem::Specification.new do |gem|
4
+ gem.name = "windows-api"
5
+ gem.version = "0.1.0"
6
+ gem.author = "Daniel J. Berger"
7
+ gem.email = "djberg96@gmail.com"
8
+ gem.homepage = "http://www.rubyforge.org/projects/win32utils"
9
+ gem.platform = Gem::Platform::RUBY
10
+ gem.summary = "An easier way to create methods using Win32API"
11
+ gem.description = "An easier way to create methods using Win32API"
12
+ gem.test_file = "test/tc_windows_api.rb"
13
+ gem.has_rdoc = true
14
+ gem.files = Dir["lib/windows/*.rb"] + Dir["test/*"] + Dir["[A-Z]*"]
15
+ gem.files.reject! { |fn| fn.include? "CVS" }
16
+ gem.require_path = "lib"
17
+ gem.extra_rdoc_files = ["README", "CHANGES", "MANIFEST"]
18
+ end
19
+
20
+ if $0 == __FILE__
21
+ Gem.manage_gems
22
+ Gem::Builder.new(spec).build
23
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: windows-api
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2007-05-24 00:00:00 -06:00
8
+ summary: An easier way to create methods using Win32API
9
+ require_paths:
10
+ - lib
11
+ email: djberg96@gmail.com
12
+ homepage: http://www.rubyforge.org/projects/win32utils
13
+ rubyforge_project:
14
+ description: An easier way to create methods using Win32API
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Daniel J. Berger
31
+ files:
32
+ - lib/windows/api.rb
33
+ - test/CVS
34
+ - test/tc_windows_api.rb
35
+ - CHANGES
36
+ - CVS
37
+ - ext
38
+ - lib
39
+ - MANIFEST
40
+ - Rakefile
41
+ - README
42
+ - test
43
+ - windows-api.gemspec
44
+ test_files:
45
+ - test/tc_windows_api.rb
46
+ rdoc_options: []
47
+
48
+ extra_rdoc_files:
49
+ - README
50
+ - CHANGES
51
+ - MANIFEST
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ requirements: []
57
+
58
+ dependencies: []
59
+