windows-api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+