win32-api 1.4.5-x86-mswin32-60 → 1.4.6-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,120 +1,105 @@
1
- require 'rake'
2
- require 'rake/clean'
3
- require 'rake/testtask'
4
- require 'rbconfig'
5
- include Config
6
-
7
- desc 'Build the ruby.exe.manifest if it does not already exist'
8
- task :build_manifest do
9
- version = CONFIG['host_os'].split('_')[1]
10
-
11
- if version && version.to_i >= 80
12
- unless File.exist?(File.join(CONFIG['bindir'], 'ruby.exe.manifest'))
13
- Dir.chdir(CONFIG['bindir']) do
14
- sh "mt -inputresource:ruby.exe;2 -out:ruby.exe.manifest"
15
- end
16
- end
17
- end
18
- end
19
-
20
- desc 'Install the win32-api library (non-gem)'
21
- task :install => [:build] do
22
- Dir.chdir('ext'){
23
- sh 'nmake install'
24
- }
25
- end
26
-
27
- task :install_gem do
28
- ruby 'win32-api.gemspec'
29
- file = Dir["*.gem"].first
30
- sh "gem install #{file}"
31
- end
32
-
33
- desc 'Clean any build files for Win32::API'
34
- task :clean do
35
- Dir.chdir('ext') do
36
- if File.exists?('api.so') || File.exists?('win32/api.so') ||
37
- File.exists?('api.obj')
38
- then
39
- sh 'nmake distclean'
40
- rm 'win32/api.so' if File.exists?('win32/api.so')
41
- rm 'api.so' if File.exists?('api.so')
42
- end
43
- end
44
-
45
- # Cleanup any files generated by the build_binary_gem task
46
- rm_rf('lib') if File.exists?('lib')
47
- end
48
-
49
- desc "Build Win32::API (but don't install it)"
50
- task :build => [:clean, :build_manifest] do
51
- Dir.chdir('ext') do
52
- ruby "extconf.rb"
53
- sh "nmake"
54
- mv "api.so", "win32" # For the test suite
55
- end
56
- end
57
-
58
- desc 'Build a standard gem'
59
- task :build_gem => [:clean] do
60
- eval(IO.read('win32-api.gemspec'))
61
- end
62
-
63
- desc 'Build a binary gem'
64
- task :build_binary_gem => [:build] do
65
- mkdir_p 'lib/win32'
66
- mv 'ext/win32/api.so', 'lib/win32'
67
-
68
- task :build_binary_gem => [:clean]
69
-
70
- spec = Gem::Specification.new do |gem|
71
- gem.name = 'win32-api'
72
- gem.version = '1.4.5'
73
- gem.authors = ['Daniel J. Berger', 'Park Heesob']
74
- gem.license = 'Artistic 2.0'
75
- gem.email = 'djberg96@gmail.com'
76
- gem.homepage = 'http://www.rubyforge.org/projects/win32utils'
77
- gem.platform = Gem::Platform::CURRENT
78
- gem.summary = 'A superior replacement for Win32API'
79
- gem.has_rdoc = true
80
- gem.test_files = Dir['test/test*']
81
-
82
- gem.files = [
83
- Dir['*'],
84
- Dir['test/*'],
85
- 'ext/extconf.rb',
86
- 'ext/win32/api.c',
87
- 'lib/win32/api.so'
88
- ].flatten.reject{ |f| f.include?('CVS') }
89
-
90
- gem.rubyforge_project = 'win32utils'
91
- gem.required_ruby_version = '>= 1.8.2'
92
-
93
- gem.extra_rdoc_files = [
94
- 'README',
95
- 'CHANGES',
96
- 'MANIFEST',
97
- 'ext/win32/api.c'
98
- ]
99
-
100
- gem.description = <<-EOF
101
- The Win32::API library is meant as a replacement for the Win32API
102
- library that ships as part of the standard library. It contains several
103
- advantages over Win32API, including callback support, raw function
104
- pointers, an additional string type, and more.
105
- EOF
106
- end
107
-
108
- Gem::Builder.new(spec).build
109
- end
110
-
111
- Rake::TestTask.new(:test) do |test|
112
- task :test => [:build]
113
- test.libs << 'ext'
114
- test.warning = true
115
- test.verbose = true
116
- end
117
-
118
- task :test do
119
- Rake.application[:clean].execute
120
- end
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+ require 'rbconfig'
5
+ include Config
6
+
7
+ make = CONFIG['host_os'] =~ /mingw|cygwin/i ? 'make' : 'nmake'
8
+
9
+ desc 'Build the ruby.exe.manifest if it does not already exist'
10
+ task :build_manifest do
11
+ version = CONFIG['host_os'].split('_')[1]
12
+
13
+ if version && version.to_i >= 80
14
+ unless File.exist?(File.join(CONFIG['bindir'], 'ruby.exe.manifest'))
15
+ Dir.chdir(CONFIG['bindir']) do
16
+ sh "mt -inputresource:ruby.exe;2 -out:ruby.exe.manifest"
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ desc 'Install the win32-api library (non-gem)'
23
+ task :install => [:build] do
24
+ Dir.chdir('ext'){
25
+ sh "#{make} install"
26
+ }
27
+ end
28
+
29
+ desc 'Clean any build files for Win32::API'
30
+ task :clean do
31
+ Dir.chdir('ext') do
32
+ if File.exists?('api.so') || File.exists?('win32/api.so') ||
33
+ File.exists?('api.obj') || File.exists?('Makefile')
34
+ then
35
+ sh "#{make} distclean"
36
+ rm 'win32/api.so' if File.exists?('win32/api.so')
37
+ rm 'api.so' if File.exists?('api.so')
38
+ end
39
+ end
40
+
41
+ # Cleanup any files generated by the build_binary_gem task
42
+ rm_rf('lib') if File.exists?('lib')
43
+ Dir["*.gem"].each{ |f| File.delete(f) }
44
+ end
45
+
46
+ desc "Build Win32::API (but don't install it)"
47
+ task :build => [:clean, :build_manifest] do
48
+ Dir.chdir('ext') do
49
+ ruby "extconf.rb"
50
+ sh make
51
+ cp "api.so", "win32" # For the test suite
52
+ end
53
+ end
54
+
55
+ namespace 'gem' do
56
+ desc 'Build a standard gem'
57
+ task :create => [:clean] do
58
+ spec = eval(IO.read('win32-api.gemspec'))
59
+ Gem::Builder.new(spec).build
60
+ end
61
+
62
+ desc 'Build a binary gem'
63
+ task :binary => [:build] do
64
+ mkdir_p 'lib/win32'
65
+ cp 'ext/api.so', 'lib/win32'
66
+
67
+ spec = eval(IO.read('win32-api.gemspec'))
68
+ spec.platform = Gem::Platform::CURRENT
69
+ spec.extensions = nil
70
+ spec.files = spec.files.reject{ |f| f.include?('ext') }
71
+
72
+ Gem::Builder.new(spec).build
73
+ end
74
+
75
+ desc 'Install the gem'
76
+ task :install => [:create] do
77
+ file = Dir["*.gem"].first
78
+ sh "gem install #{file}"
79
+ end
80
+ end
81
+
82
+ namespace 'test' do
83
+ Rake::TestTask.new(:all) do |test|
84
+ task :all => [:build]
85
+ test.libs << 'ext'
86
+ test.warning = true
87
+ test.verbose = true
88
+ end
89
+
90
+ Rake::TestTask.new(:callback) do |test|
91
+ task :callback => [:build]
92
+ test.test_files = FileList['test/test_win32_api_callback.rb']
93
+ test.libs << 'ext'
94
+ test.warning = true
95
+ test.verbose = true
96
+ end
97
+
98
+ Rake::TestTask.new(:function) do |test|
99
+ task :function => [:build]
100
+ test.test_files = FileList['test/test_win32_api_function.rb']
101
+ test.libs << 'ext'
102
+ test.warning = true
103
+ test.verbose = true
104
+ end
105
+ end
data/ext/win32/api.c CHANGED
@@ -1,948 +1,946 @@
1
- #include <ruby.h>
2
- #include <windows.h>
3
-
4
- // Ruby 1.9.x
5
- #ifndef RSTRING_PTR
6
- #define RSTRING_PTR(s) (RSTRING(s)->ptr)
7
- #endif
8
- #ifndef RSTRING_LEN
9
- #define RSTRING_LEN(s) (RSTRING(s)->len)
10
- #endif
11
-
12
- #ifndef RARRAY_PTR
13
- #define RARRAY_PTR(a) (RARRAY(a)->ptr)
14
- #endif
15
- #ifndef RARRAY_LEN
16
- #define RARRAY_LEN(a) (RARRAY(a)->len)
17
- #endif
18
-
19
- #define MAX_BUF 1024
20
- #define WINDOWS_API_VERSION "1.4.5"
21
-
22
- #define _T_VOID 0
23
- #define _T_LONG 1
24
- #define _T_POINTER 2
25
- #define _T_INTEGER 3
26
- #define _T_CALLBACK 4
27
- #define _T_STRING 5
28
-
29
- VALUE cAPIError, cAPIProtoError, cAPILoadError;
30
- static VALUE ActiveCallback = Qnil;
31
-
32
- typedef struct {
33
- HANDLE library;
34
- FARPROC function;
35
- int return_type;
36
- int prototype[20];
37
- } Win32API;
38
-
39
- static void api_free(Win32API* ptr){
40
- if(ptr->library)
41
- FreeLibrary(ptr->library);
42
-
43
- if(ptr)
44
- free(ptr);
45
- }
46
-
47
- static VALUE api_allocate(VALUE klass){
48
- Win32API* ptr = malloc(sizeof(Win32API));
49
- return Data_Wrap_Struct(klass, 0, api_free, ptr);
50
- }
51
-
52
- /* Helper function that converts the error number returned by GetLastError()
53
- * into a human readable string. Note that we always use English for error
54
- * output because that's what Ruby itself does.
55
- *
56
- * Internal use only.
57
- */
58
- char* StringError(DWORD dwError){
59
- LPVOID lpMsgBuf;
60
- static char buf[MAX_PATH];
61
- DWORD dwLen;
62
-
63
- // Assume ASCII error messages from the Windows API
64
- dwLen = FormatMessageA(
65
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
66
- FORMAT_MESSAGE_FROM_SYSTEM |
67
- FORMAT_MESSAGE_IGNORE_INSERTS,
68
- NULL,
69
- dwError,
70
- MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
71
- (LPSTR)&lpMsgBuf,
72
- 0,
73
- NULL
74
- );
75
-
76
- /* It appears that Windows doesn't necessarily ship with the DLL
77
- * required to always use English error messages. Check for error
78
- * ERROR_MUI_FILE_NOT_FOUND (15100), and try again if necessary.
79
- */
80
- if(!dwLen && GetLastError() == 15100){
81
- dwLen = FormatMessageA(
82
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
83
- FORMAT_MESSAGE_FROM_SYSTEM |
84
- FORMAT_MESSAGE_IGNORE_INSERTS,
85
- NULL,
86
- dwError,
87
- 0,
88
- (LPSTR)&lpMsgBuf,
89
- 0,
90
- NULL
91
- );
92
- }
93
-
94
- if(!dwLen){
95
- rb_raise(
96
- cAPIError,
97
- "Attempt to format message failed (error = '%d')",
98
- GetLastError()
99
- );
100
- }
101
-
102
- memset(buf, 0, MAX_PATH);
103
-
104
- /* remove \r\n */
105
- #ifdef HAVE_STRNCPY_S
106
- strncpy_s(buf, MAX_PATH, lpMsgBuf, dwLen - 2);
107
- #else
108
- strncpy(buf, lpMsgBuf, dwLen - 2);
109
- #endif
110
-
111
- LocalFree(lpMsgBuf);
112
-
113
- return buf;
114
- }
115
-
116
- /*
117
- * call-seq:
118
- * Win32::API::Callback.new(prototype, return='L'){ |proto| ... }
119
- *
120
- * Creates and returns a new Win32::API::Callback object. The prototype
121
- * arguments are yielded back to the block in the same order they were
122
- * declared.
123
- *
124
- * The +prototype+ is the function prototype for the callback function. This
125
- * is a string. The possible valid characters are 'I' (integer), 'L' (long),
126
- * 'V' (void), 'P' (pointer) or 'S' (string). Unlike API objects, API::Callback
127
- * objects do not have a default prototype.
128
- *
129
- * The +return+ argument is the return type for the callback function. The
130
- * valid characters are the same as for the +prototype+. The default is
131
- * 'L' (long).
132
- *
133
- * Example:
134
- * require 'win32/api'
135
- * include Win32
136
- *
137
- * EnumWindows = API.new('EnumWindows', 'KP', 'L', 'user32')
138
- * GetWindowText = API.new('GetWindowText', 'LPI', 'I', 'user32')
139
- *
140
- * EnumWindowsProc = API::Callback.new('LP', 'I'){ |handle, param|
141
- * buf = "\0" * 200
142
- * GetWindowText.call(handle, buf, 200);
143
- * puts buf.strip unless buf.strip.empty?
144
- * buf.index(param).nil? ? true : false
145
- * }
146
- *
147
- * EnumWindows.call(EnumWindowsProc, 'UEDIT32')
148
- */
149
- static VALUE callback_init(int argc, VALUE* argv, VALUE self)
150
- {
151
- void *find_callback(VALUE,int);
152
- VALUE v_proto, v_return, v_proc;
153
- int i;
154
-
155
- rb_scan_args(argc, argv, "11&", &v_proto, &v_return, &v_proc);
156
-
157
- /* Validate prototype characters */
158
- for(i = 0; i < RSTRING_LEN(v_proto); i++){
159
- switch(RSTRING_PTR(v_proto)[i]){
160
- case 'I': case 'L': case 'P': case 'V': case 'S':
161
- break;
162
- default:
163
- rb_raise(cAPIProtoError, "Illegal prototype '%c'",
164
- RSTRING_PTR(v_proto)[i]
165
- );
166
- }
167
- }
168
-
169
- if(NIL_P(v_return) || RARRAY_LEN(v_return) == 0){
170
- v_return = rb_str_new2("L");
171
- }
172
- else{
173
- switch(*(char*)RSTRING_PTR(v_return)){
174
- case 'I': case 'L': case 'P': case 'V': case 'S':
175
- break;
176
- default:
177
- rb_raise(cAPIProtoError, "Illegal return type '%s'",
178
- RSTRING_PTR(v_return)
179
- );
180
- }
181
- }
182
-
183
- rb_iv_set(self, "@function", v_proc);
184
- rb_iv_set(self, "@prototype", v_proto);
185
- rb_iv_set(self, "@return_type", v_return);
186
- rb_iv_set(self, "@address", ULONG2NUM((LPARAM)find_callback(self,RSTRING_LEN(v_proto))));
187
- ActiveCallback = self;
188
-
189
- return self;
190
- }
191
-
192
- /*
193
- * call-seq:
194
- * Win32::API.new(function, prototype='V', return='L', dll='kernel32')
195
- *
196
- * Creates and returns a new Win32::API object. The +function+ is the name
197
- * of the Windows function.
198
- *
199
- * The +prototype+ is the function prototype for +function+. This can be a
200
- * string or an array of characters. The possible valid characters are 'I'
201
- * (integer), 'L' (long), 'V' (void), 'P' (pointer), 'K' (callback) or 'S'
202
- * (string).
203
- *
204
- * The default is void ('V').
205
- *
206
- * Constant (const char*) strings should use 'S'. Pass by reference string
207
- * buffers should use 'P'. The former is faster, but cannot be modified.
208
- *
209
- * The +return+ argument is the return type for the function. The valid
210
- * characters are the same as for the +prototype+. The default is 'L' (long).
211
- *
212
- * The +dll+ is the name of the DLL file that the function is exported from.
213
- * The default is 'kernel32'.
214
- *
215
- * If the function cannot be found then an API::Error is raised (a subclass
216
- * of RuntimeError).
217
- *
218
- * Example:
219
- *
220
- * require 'win32/api'
221
- * include Win32
222
- *
223
- * buf = 0.chr * 260
224
- * len = [buf.length].pack('L')
225
- *
226
- * GetUserName = API.new('GetUserName', 'PP', 'I', 'advapi32')
227
- * GetUserName.call(buf, len)
228
- *
229
- * puts buf.strip
230
- */
231
- static VALUE api_init(int argc, VALUE* argv, VALUE self)
232
- {
233
- HMODULE hLibrary;
234
- FARPROC fProc;
235
- Win32API* ptr;
236
- int i;
237
- char* first = "A";
238
- char* second = "W";
239
- VALUE v_proc, v_proto, v_return, v_dll;
240
-
241
- rb_scan_args(argc, argv, "13", &v_proc, &v_proto, &v_return, &v_dll);
242
-
243
- Data_Get_Struct(self, Win32API, ptr);
244
-
245
- // Convert a string prototype to an array of characters
246
- if(rb_respond_to(v_proto, rb_intern("split")))
247
- v_proto = rb_str_split(v_proto, "");
248
-
249
- // Convert a nil or empty prototype to 'V' (void) automatically
250
- if(NIL_P(v_proto) || RARRAY_LEN(v_proto) == 0){
251
- v_proto = rb_ary_new();
252
- rb_ary_push(v_proto, rb_str_new2("V"));
253
- }
254
-
255
- // Set an arbitrary limit of 20 parameters
256
- if(20 < RARRAY_LEN(v_proto))
257
- rb_raise(rb_eArgError, "too many parameters: %d\n", RARRAY_LEN(v_proto));
258
-
259
- // Set the default dll to 'kernel32'
260
- if(NIL_P(v_dll))
261
- v_dll = rb_str_new2("kernel32");
262
-
263
- // Set the default return type to 'L' (DWORD)
264
- if(NIL_P(v_return))
265
- v_return = rb_str_new2("L");
266
-
267
- SafeStringValue(v_dll);
268
- SafeStringValue(v_proc);
269
-
270
- hLibrary = LoadLibrary(TEXT(RSTRING_PTR(v_dll)));
271
-
272
- // The most likely cause of failure is a bad DLL load path
273
- if(!hLibrary){
274
- rb_raise(cAPILoadError, "LoadLibrary() function failed for '%s': %s",
275
- RSTRING_PTR(v_dll),
276
- StringError(GetLastError())
277
- );
278
- }
279
-
280
- ptr->library = hLibrary;
281
-
282
- /* Attempt to get the function. If it fails, try again with an 'A'
283
- * appended. If that fails, try again with a 'W' appended. If that
284
- * still fails, raise an API::LoadLibraryError.
285
- */
286
-
287
- fProc = GetProcAddress(hLibrary, TEXT(RSTRING_PTR(v_proc)));
288
-
289
- // Skip the ANSI and Wide function checks for MSVCRT functions.
290
- if(!fProc){
291
- if(strstr(RSTRING_PTR(v_dll), "msvcr")){
292
- rb_raise(
293
- cAPILoadError,
294
- "Unable to load function '%s'",
295
- RSTRING_PTR(v_proc)
296
- );
297
- }
298
- else{
299
- VALUE v_ascii = rb_str_new3(v_proc);
300
- v_ascii = rb_str_cat(v_ascii, first, 1);
301
- fProc = GetProcAddress(hLibrary, TEXT(RSTRING_PTR(v_ascii)));
302
-
303
- if(!fProc){
304
- VALUE v_unicode = rb_str_new3(v_proc);
305
- v_unicode = rb_str_cat(v_unicode, second, 1);
306
- fProc = GetProcAddress(hLibrary, TEXT(RSTRING_PTR(v_unicode)));
307
-
308
- if(!fProc){
309
- rb_raise(
310
- cAPILoadError,
311
- "Unable to load function '%s', '%s', or '%s'",
312
- RSTRING_PTR(v_proc),
313
- RSTRING_PTR(v_ascii),
314
- RSTRING_PTR(v_unicode)
315
- );
316
- }
317
- else{
318
- rb_iv_set(self, "@effective_function_name", v_unicode);
319
- }
320
- }
321
- else{
322
- rb_iv_set(self, "@effective_function_name", v_ascii);
323
- }
324
- }
325
- }
326
- else{
327
- rb_iv_set(self, "@effective_function_name", v_proc);
328
- }
329
-
330
- ptr->function = fProc;
331
-
332
- // Push the numeric prototypes onto our int array for later use.
333
-
334
- for(i = 0; i < RARRAY_LEN(v_proto); i++){
335
- SafeStringValue(RARRAY_PTR(v_proto)[i]);
336
- switch(*(char*)StringValuePtr(RARRAY_PTR(v_proto)[i])){
337
- case 'L':
338
- ptr->prototype[i] = _T_LONG;
339
- break;
340
- case 'P':
341
- ptr->prototype[i] = _T_POINTER;
342
- break;
343
- case 'I': case 'B':
344
- ptr->prototype[i] = _T_INTEGER;
345
- break;
346
- case 'V':
347
- ptr->prototype[i] = _T_VOID;
348
- break;
349
- case 'K':
350
- ptr->prototype[i] = _T_CALLBACK;
351
- break;
352
- case 'S':
353
- ptr->prototype[i] = _T_STRING;
354
- break;
355
- default:
356
- rb_raise(cAPIProtoError, "Illegal prototype '%s'",
357
- StringValuePtr(RARRAY_PTR(v_proto)[i])
358
- );
359
- }
360
- }
361
-
362
- // Store the return type for later use.
363
-
364
- // Automatically convert empty strings or nil to type void.
365
- if(NIL_P(v_return) || RSTRING_LEN(v_return) == 0){
366
- v_return = rb_str_new2("V");
367
- ptr->return_type = _T_VOID;
368
- }
369
- else{
370
- SafeStringValue(v_return);
371
- switch(*RSTRING_PTR(v_return)){
372
- case 'L':
373
- ptr->return_type = _T_LONG;
374
- break;
375
- case 'P':
376
- ptr->return_type = _T_POINTER;
377
- break;
378
- case 'I': case 'B':
379
- ptr->return_type = _T_INTEGER;
380
- break;
381
- case 'V':
382
- ptr->return_type = _T_VOID;
383
- break;
384
- case 'S':
385
- ptr->return_type = _T_STRING;
386
- break;
387
- default:
388
- rb_raise(cAPIProtoError, "Illegal return type '%s'",
389
- RSTRING_PTR(v_return)
390
- );
391
- }
392
- }
393
-
394
- rb_iv_set(self, "@dll_name", v_dll);
395
- rb_iv_set(self, "@function_name", v_proc);
396
- rb_iv_set(self, "@prototype", v_proto);
397
- rb_iv_set(self, "@return_type", v_return);
398
-
399
- return self;
400
- }
401
-
402
- /*
403
- * call-seq:
404
- *
405
- * API::Function.new(address, prototype = 'V', return_type = 'L')
406
- *
407
- * Creates and returns an API::Function object. This object is similar to an
408
- * API object, except that instead of a character function name you pass a
409
- * function pointer address as the first argument, and there's no associated
410
- * DLL file.
411
- *
412
- * Once you have your API::Function object you can then call it the same way
413
- * you would an API object.
414
- *
415
- * Example:
416
- *
417
- * require 'win32/api'
418
- * include Win32
419
- *
420
- * LoadLibrary = API.new('LoadLibrary', 'P', 'L')
421
- * GetProcAddress = API.new('GetProcAddress', 'LP', 'L')
422
- *
423
- * # Play a system beep
424
- * hlib = LoadLibrary.call('user32')
425
- * addr = GetProcAddress.call(hlib, 'MessageBeep')
426
- * func = Win32::API::Function.new(addr, 'L', 'L')
427
- * func.call(0)
428
- */
429
- static VALUE func_init(int argc, VALUE* argv, VALUE self){
430
- Win32API* ptr;
431
- int i;
432
- VALUE v_address, v_proto, v_return;
433
-
434
- rb_scan_args(argc, argv, "12", &v_address, &v_proto, &v_return);
435
-
436
- Data_Get_Struct(self, Win32API, ptr);
437
-
438
- // Convert a string prototype to an array of characters
439
- if(rb_respond_to(v_proto, rb_intern("split")))
440
- v_proto = rb_str_split(v_proto, "");
441
-
442
- // Convert a nil or empty prototype to 'V' (void) automatically
443
- if(NIL_P(v_proto) || RARRAY_LEN(v_proto) == 0){
444
- v_proto = rb_ary_new();
445
- rb_ary_push(v_proto, rb_str_new2("V"));
446
- }
447
-
448
- // Set an arbitrary limit of 20 parameters
449
- if(20 < RARRAY_LEN(v_proto))
450
- rb_raise(rb_eArgError, "too many parameters: %d\n", RARRAY_LEN(v_proto));
451
-
452
- // Set the default return type to 'L' (DWORD)
453
- if(NIL_P(v_return))
454
- v_return = rb_str_new2("L");
455
-
456
- ptr->function = (FARPROC)NUM2LONG(v_address);
457
-
458
- // Push the numeric prototypes onto our int array for later use.
459
-
460
- for(i = 0; i < RARRAY_LEN(v_proto); i++){
461
- SafeStringValue(RARRAY_PTR(v_proto)[i]);
462
- switch(*(char*)StringValuePtr(RARRAY_PTR(v_proto)[i])){
463
- case 'L':
464
- ptr->prototype[i] = _T_LONG;
465
- break;
466
- case 'P':
467
- ptr->prototype[i] = _T_POINTER;
468
- break;
469
- case 'I': case 'B':
470
- ptr->prototype[i] = _T_INTEGER;
471
- break;
472
- case 'V':
473
- ptr->prototype[i] = _T_VOID;
474
- break;
475
- case 'K':
476
- ptr->prototype[i] = _T_CALLBACK;
477
- break;
478
- case 'S':
479
- ptr->prototype[i] = _T_STRING;
480
- break;
481
- default:
482
- rb_raise(cAPIProtoError, "Illegal prototype '%s'",
483
- StringValuePtr(RARRAY_PTR(v_proto)[i])
484
- );
485
- }
486
- }
487
-
488
- // Store the return type for later use.
489
-
490
- // Automatically convert empty strings or nil to type void.
491
- if(NIL_P(v_return) || RSTRING_LEN(v_return) == 0){
492
- v_return = rb_str_new2("V");
493
- ptr->return_type = _T_VOID;
494
- }
495
- else{
496
- SafeStringValue(v_return);
497
- switch(*RSTRING_PTR(v_return)){
498
- case 'L':
499
- ptr->return_type = _T_LONG;
500
- break;
501
- case 'P':
502
- ptr->return_type = _T_POINTER;
503
- break;
504
- case 'I': case 'B':
505
- ptr->return_type = _T_INTEGER;
506
- break;
507
- case 'V':
508
- ptr->return_type = _T_VOID;
509
- break;
510
- case 'S':
511
- ptr->return_type = _T_STRING;
512
- break;
513
- default:
514
- rb_raise(cAPIProtoError, "Illegal return type '%s'",
515
- RSTRING_PTR(v_return)
516
- );
517
- }
518
- }
519
-
520
- rb_iv_set(self, "@address", v_address);
521
- rb_iv_set(self, "@prototype", v_proto);
522
- rb_iv_set(self, "@return_type", v_return);
523
-
524
- return self;
525
- }
526
-
527
- typedef struct {
528
- DWORD params[20];
529
- } CALLPARAM;
530
-
531
-
532
- DWORD CallbackFunction(CALLPARAM param,VALUE callback)
533
- {
534
- VALUE v_proto, v_return, v_proc, v_retval;
535
- VALUE argv[20];
536
- int i, argc;
537
- char *a_proto;
538
- char *a_return;
539
-
540
- if(callback && !NIL_P(callback)){
541
- v_proto = rb_iv_get(callback, "@prototype");
542
- a_proto = RSTRING_PTR(v_proto);
543
-
544
- v_return = rb_iv_get(callback, "@return_type");
545
- a_return = RSTRING_PTR(v_return);
546
-
547
- v_proc = rb_iv_get(callback, "@function");
548
- argc = RSTRING_LEN(v_proto);
549
-
550
- for(i=0; i < RSTRING_LEN(v_proto); i++){
551
- argv[i] = Qnil;
552
- switch(a_proto[i]){
553
- case 'L':
554
- argv[i] = ULONG2NUM(param.params[i]);
555
- break;
556
- case 'P':
557
- if(param.params[i])
558
- argv[i] = rb_str_new2((char *)param.params[i]);
559
- break;
560
- case 'I':
561
- argv[i] = INT2NUM(param.params[i]);
562
- break;
563
- default:
564
- rb_raise(cAPIProtoError, "Illegal prototype '%s'",
565
- RSTRING_PTR(a_proto[i])
566
- );
567
- }
568
- }
569
-
570
- v_retval = rb_funcall2(v_proc, rb_intern("call"), argc, argv);
571
-
572
- /* Handle true and false explicitly, as some CALLBACK functions
573
- * require TRUE or FALSE to break out of loops, etc.
574
- */
575
- if(v_retval == Qtrue)
576
- return TRUE;
577
- else if(v_retval == Qfalse)
578
- return FALSE;
579
-
580
- switch (*a_return) {
581
- case 'I':
582
- return NUM2INT(v_retval);
583
- break;
584
- case 'L':
585
- return NUM2ULONG(v_retval);
586
- break;
587
- case 'S':
588
- return (unsigned long)RSTRING_PTR(v_retval);
589
- break;
590
- case 'P':
591
- if(NIL_P(v_retval)){
592
- return 0;
593
- }
594
- else if(FIXNUM_P(v_retval)){
595
- return NUM2ULONG(v_retval);
596
- }
597
- else{
598
- StringValue(v_retval);
599
- rb_str_modify(v_retval);
600
- return (unsigned long)StringValuePtr(v_retval);
601
- }
602
- break;
603
- }
604
- }
605
-
606
- return 0;
607
- }
608
-
609
- #define CALLBACK0(x) DWORD CALLBACK CallbackFunction0_##x() {\
610
- CALLPARAM param = {0};\
611
- param.params[0] = 0;\
612
- return CallbackFunction(param,FuncTable[0][x]);\
613
- }
614
-
615
- #define CALLBACK1(x) DWORD CALLBACK CallbackFunction1_##x(DWORD p1) {\
616
- CALLPARAM param = {p1};\
617
- return CallbackFunction(param,FuncTable[1][x]);\
618
- }
619
-
620
- #define CALLBACK2(x) DWORD CALLBACK CallbackFunction2_##x(\
621
- DWORD p1, DWORD p2){\
622
- CALLPARAM param = {p1,p2};\
623
- return CallbackFunction(param,FuncTable[2][x]);\
624
- }
625
-
626
- #define CALLBACK3(x) DWORD CALLBACK CallbackFunction3_##x(\
627
- DWORD p1, DWORD p2, DWORD p3){\
628
- CALLPARAM param = {p1,p2,p3};\
629
- return CallbackFunction(param,FuncTable[3][x]);\
630
- }
631
-
632
- #define CALLBACK4(x) DWORD CALLBACK CallbackFunction4_##x(\
633
- DWORD p1, DWORD p2, DWORD p3, DWORD p4){\
634
- CALLPARAM param = {p1,p2,p3,p4};\
635
- return CallbackFunction(param,FuncTable[4][x]);\
636
- }
637
-
638
- #define CALLBACK5(x) DWORD CALLBACK CallbackFunction5_##x(\
639
- DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5\
640
- ){\
641
- CALLPARAM param = {p1,p2,p3,p4,p5};\
642
- return CallbackFunction(param,FuncTable[5][x]);\
643
- }
644
-
645
- #define CALLBACK6(x) DWORD CALLBACK CallbackFunction6_##x(\
646
- DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5, DWORD p6\
647
- ){\
648
- CALLPARAM param = {p1,p2,p3,p4,p5,p6};\
649
- return CallbackFunction(param,FuncTable[6][x]);\
650
- }
651
-
652
- #define CALLBACK7(x) DWORD CALLBACK CallbackFunction7_##x(\
653
- DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5, DWORD p6, DWORD p7\
654
- ){\
655
- CALLPARAM param = {p1,p2,p3,p4,p5,p6,p7};\
656
- return CallbackFunction(param,FuncTable[7][x]);\
657
- }
658
-
659
- #define CALLBACK8(x) DWORD CALLBACK CallbackFunction8_##x(\
660
- DWORD p1, DWORD p2, DWORD p3, DWORD p4,\
661
- DWORD p5, DWORD p6, DWORD p7, DWORD p8\
662
- ){\
663
- CALLPARAM param = {p1,p2,p3,p4,p5,p6,p7,p8};\
664
- return CallbackFunction(param,FuncTable[8][x]);\
665
- }
666
-
667
- #define CALLBACK9(x) DWORD CALLBACK CallbackFunction9_##x(\
668
- DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5,\
669
- DWORD p6, DWORD p7, DWORD p8, DWORD p9\
670
- ){\
671
- CALLPARAM param = {p1,p2,p3,p4,p5,p6,p7,p8,p9};\
672
- return CallbackFunction(param,FuncTable[9][x]);\
673
- }
674
-
675
- #define DEFCALLBACK(x) CALLBACK##x(0)\
676
- CALLBACK##x(1)\
677
- CALLBACK##x(2)\
678
- CALLBACK##x(3)\
679
- CALLBACK##x(4)\
680
- CALLBACK##x(5)\
681
- CALLBACK##x(6)\
682
- CALLBACK##x(7)\
683
- CALLBACK##x(8)\
684
- CALLBACK##x(9)
685
-
686
- #define CF(x,y) CallbackFunction##x##_##y
687
-
688
- static VALUE FuncTable[10][10];
689
-
690
- DEFCALLBACK(0)
691
- DEFCALLBACK(1)
692
- DEFCALLBACK(2)
693
- DEFCALLBACK(3)
694
- DEFCALLBACK(4)
695
- DEFCALLBACK(5)
696
- DEFCALLBACK(6)
697
- DEFCALLBACK(7)
698
- DEFCALLBACK(8)
699
- DEFCALLBACK(9)
700
-
701
- void *CallbackTable[10][10] = {
702
- {CF(0,0),CF(0,1),CF(0,2),CF(0,3),CF(0,4),CF(0,5),CF(0,6),CF(0,7),CF(0,8),CF(0,9)},
703
- {CF(1,0),CF(1,1),CF(1,2),CF(1,3),CF(1,4),CF(1,5),CF(1,6),CF(1,7),CF(1,8),CF(1,9)},
704
- {CF(2,0),CF(2,1),CF(2,2),CF(2,3),CF(2,4),CF(2,5),CF(2,6),CF(2,7),CF(2,8),CF(2,9)},
705
- {CF(3,0),CF(3,1),CF(3,2),CF(3,3),CF(3,4),CF(3,5),CF(3,6),CF(3,7),CF(3,8),CF(3,9)},
706
- {CF(4,0),CF(4,1),CF(4,2),CF(4,3),CF(4,4),CF(4,5),CF(4,6),CF(4,7),CF(4,8),CF(4,9)},
707
- {CF(5,0),CF(5,1),CF(5,2),CF(5,3),CF(5,4),CF(5,5),CF(5,6),CF(5,7),CF(5,8),CF(5,9)},
708
- {CF(6,0),CF(6,1),CF(6,2),CF(6,3),CF(6,4),CF(6,5),CF(6,6),CF(6,7),CF(6,8),CF(6,9)},
709
- {CF(7,0),CF(7,1),CF(7,2),CF(7,3),CF(7,4),CF(7,5),CF(7,6),CF(7,7),CF(7,8),CF(7,9)},
710
- {CF(8,0),CF(8,1),CF(8,2),CF(8,3),CF(8,4),CF(8,5),CF(8,6),CF(8,7),CF(8,8),CF(8,9)},
711
- {CF(9,0),CF(9,1),CF(9,2),CF(9,3),CF(9,4),CF(9,5),CF(9,6),CF(9,7),CF(9,8),CF(9,9)}};
712
-
713
-
714
- void *find_callback(VALUE obj,int len)
715
- {
716
- int i;
717
- for(i=0;i<10;i++)
718
- {
719
- if(FuncTable[len][i]==0)
720
- break;
721
- }
722
- if(i>=10)
723
- rb_raise(cAPIError,"too many callbacks are defined.");
724
- FuncTable[len][i] = obj;
725
- return CallbackTable[len][i];
726
- }
727
-
728
- /*
729
- * call-seq:
730
- * Win32::API#call(arg1, arg2, ...)
731
- *
732
- * Calls the function pointer with the given arguments (if any). Note that,
733
- * while this method will catch some prototype mismatches (raising a TypeError
734
- * in the process), it is not fulproof. It is ultimately your job to make
735
- * sure the arguments match the +prototype+ specified in the constructor.
736
- *
737
- * For convenience, nil is converted to NULL, true is converted to TRUE (1)
738
- * and false is converted to FALSE (0).
739
- */
740
- static VALUE api_call(int argc, VALUE* argv, VALUE self){
741
- VALUE v_proto, v_args, v_arg, v_return;
742
- Win32API* ptr;
743
- unsigned long return_value;
744
- int i = 0;
745
- int len;
746
-
747
- struct{
748
- unsigned long params[20];
749
- } param;
750
-
751
- Data_Get_Struct(self, Win32API, ptr);
752
-
753
- rb_scan_args(argc, argv, "0*", &v_args);
754
-
755
- v_proto = rb_iv_get(self, "@prototype");
756
-
757
- // For void prototypes, allow either no args or an explicit nil
758
- if(RARRAY_LEN(v_proto) != RARRAY_LEN(v_args)){
759
- char* c = StringValuePtr(RARRAY_PTR(v_proto)[0]);
760
- if(!strcmp(c, "V")){
761
- rb_ary_push(v_args, Qnil);
762
- }
763
- else{
764
- rb_raise(rb_eArgError,
765
- "wrong number of parameters: expected %d, got %d",
766
- RARRAY_LEN(v_proto), RARRAY_LEN(v_args)
767
- );
768
- }
769
- }
770
-
771
- len = RARRAY_LEN(v_proto);
772
-
773
- for(i = 0; i < len; i++){
774
- v_arg = RARRAY_PTR(v_args)[i];
775
-
776
- // Convert nil to NULL. Otherwise convert as appropriate.
777
- if(NIL_P(v_arg))
778
- param.params[i] = (unsigned long)NULL;
779
- else if(v_arg == Qtrue)
780
- param.params[i] = TRUE;
781
- else if(v_arg == Qfalse)
782
- param.params[i] = FALSE;
783
- else
784
- switch(ptr->prototype[i]){
785
- case _T_LONG:
786
- param.params[i] = NUM2ULONG(v_arg);
787
- break;
788
- case _T_INTEGER:
789
- param.params[i] = NUM2INT(v_arg);
790
- break;
791
- case _T_POINTER:
792
- if(FIXNUM_P(v_arg)){
793
- param.params[i] = NUM2ULONG(v_arg);
794
- }
795
- else{
796
- StringValue(v_arg);
797
- rb_str_modify(v_arg);
798
- param.params[i] = (unsigned long)StringValuePtr(v_arg);
799
- }
800
- break;
801
- case _T_CALLBACK:
802
- ActiveCallback = v_arg;
803
- v_proto = rb_iv_get(ActiveCallback, "@prototype");
804
- param.params[i] = (LPARAM)NUM2ULONG(rb_iv_get(ActiveCallback, "@address"));;
805
- break;
806
- case _T_STRING:
807
- param.params[i] = (unsigned long)RSTRING_PTR(v_arg);
808
- break;
809
- default:
810
- param.params[i] = NUM2ULONG(v_arg);
811
- }
812
- }
813
-
814
- /* Call the function, get the return value */
815
- return_value = ptr->function(param);
816
-
817
-
818
- /* Return the appropriate type based on the return type specified
819
- * in the constructor.
820
- */
821
- switch(ptr->return_type){
822
- case _T_INTEGER:
823
- v_return = INT2NUM(return_value);
824
- break;
825
- case _T_LONG:
826
- v_return = ULONG2NUM(return_value);
827
- break;
828
- case _T_VOID:
829
- v_return = Qnil;
830
- break;
831
- case _T_POINTER:
832
- if(!return_value){
833
- v_return = Qnil;
834
- }
835
- else{
836
- VALUE v_efunc = rb_iv_get(self, "@effective_function_name");
837
- char* efunc = RSTRING_PTR(v_efunc);
838
- if(efunc[strlen(efunc)-1] == 'W'){
839
- v_return = rb_str_new(
840
- (TCHAR*)return_value,
841
- wcslen((wchar_t*)return_value)*2
842
- );
843
- }
844
- else{
845
- v_return = rb_str_new2((TCHAR*)return_value);
846
- }
847
- }
848
- break;
849
- case _T_STRING:
850
- {
851
- VALUE v_efunc = rb_iv_get(self, "@effective_function_name");
852
- char* efunc = RSTRING_PTR(v_efunc);
853
-
854
- if(efunc[strlen(efunc)-1] == 'W'){
855
- v_return = rb_str_new(
856
- (TCHAR*)return_value,
857
- wcslen((wchar_t*)return_value)*2
858
- );
859
- }
860
- else{
861
- v_return = rb_str_new2((TCHAR*)return_value);
862
- }
863
- }
864
- break;
865
- default:
866
- v_return = INT2NUM(0);
867
- }
868
-
869
- return v_return;
870
- }
871
-
872
- /*
873
- * Wraps the Windows API functions in a Ruby interface.
874
- */
875
- void Init_api(){
876
- VALUE mWin32, cAPI, cCallback, cFunction;
877
-
878
- /* Modules and Classes */
879
-
880
- /* The Win32 module serves as a namespace only */
881
- mWin32 = rb_define_module("Win32");
882
-
883
- /* The API class encapsulates a function pointer to Windows API function */
884
- cAPI = rb_define_class_under(mWin32, "API", rb_cObject);
885
-
886
- /* The API::Callback class encapsulates a Windows CALLBACK function */
887
- cCallback = rb_define_class_under(cAPI, "Callback", rb_cObject);
888
-
889
- /* The API::Function class encapsulates a raw function pointer */
890
- cFunction = rb_define_class_under(cAPI, "Function", cAPI);
891
-
892
- /* The API::Error class serves as a base class for other errors */
893
- cAPIError = rb_define_class_under(cAPI, "Error", rb_eRuntimeError);
894
-
895
- /* The LoadError class is raised if a function cannot be found or loaded */
896
- cAPILoadError = rb_define_class_under(cAPI, "LoadLibraryError", cAPIError);
897
-
898
- /* The PrototypeError class is raised if an invalid prototype is passed */
899
- cAPIProtoError = rb_define_class_under(cAPI, "PrototypeError", cAPIError);
900
-
901
- /* Miscellaneous */
902
- rb_define_alloc_func(cAPI, api_allocate);
903
-
904
- /* Win32::API Instance Methods */
905
- rb_define_method(cAPI, "initialize", api_init, -1);
906
- rb_define_method(cAPI, "call", api_call, -1);
907
-
908
- /* Win32::API::Callback Instance Methods */
909
- rb_define_method(cCallback, "initialize", callback_init, -1);
910
-
911
- /* Win32::API::Function Instance Methods */
912
- rb_define_method(cFunction, "initialize", func_init, -1);
913
-
914
- /* The name of the DLL that exports the API function */
915
- rb_define_attr(cAPI, "dll_name", 1, 0);
916
-
917
- /* The name of the function passed to the constructor */
918
- rb_define_attr(cAPI, "function_name", 1, 0);
919
-
920
- /* The name of the actual function that is returned by the constructor.
921
- * For example, if you passed 'GetUserName' to the constructor, then the
922
- * effective function name would be either 'GetUserNameA' or 'GetUserNameW'.
923
- */
924
- rb_define_attr(cAPI, "effective_function_name", 1, 0);
925
-
926
- /* The prototype, returned as an array of characters */
927
- rb_define_attr(cAPI, "prototype", 1, 0);
928
-
929
- /* The return type, returned as a single character, S, P, L, I, V or B */
930
- rb_define_attr(cAPI, "return_type", 1, 0);
931
-
932
- /* Win32::API::Callback Instance Methods */
933
-
934
- /* The prototype, returned as an array of characters */
935
- rb_define_attr(cCallback, "prototype", 1, 0);
936
-
937
- /* The return type, returned as a single character, S, P, L, I, V or B */
938
- rb_define_attr(cCallback, "return_type", 1, 0);
939
-
940
- /* The numeric address of the function pointer */
941
- rb_define_attr(cCallback, "address", 1, 0);
942
- rb_define_attr(cFunction, "address", 1, 0);
943
-
944
- /* Constants */
945
-
946
- /* 1.4.5: The version of the win32-api library */
947
- rb_define_const(cAPI, "VERSION", rb_str_new2(WINDOWS_API_VERSION));
948
- }
1
+ #include <ruby.h>
2
+ #include <windows.h>
3
+
4
+ // Ruby 1.9.x
5
+ #ifndef RSTRING_PTR
6
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
7
+ #endif
8
+ #ifndef RSTRING_LEN
9
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
10
+ #endif
11
+
12
+ #ifndef RARRAY_PTR
13
+ #define RARRAY_PTR(a) (RARRAY(a)->ptr)
14
+ #endif
15
+ #ifndef RARRAY_LEN
16
+ #define RARRAY_LEN(a) (RARRAY(a)->len)
17
+ #endif
18
+
19
+ #define MAX_BUF 1024
20
+ #define WINDOWS_API_VERSION "1.4.5"
21
+
22
+ #define _T_VOID 0
23
+ #define _T_LONG 1
24
+ #define _T_POINTER 2
25
+ #define _T_INTEGER 3
26
+ #define _T_CALLBACK 4
27
+ #define _T_STRING 5
28
+
29
+ VALUE cAPIError, cAPIProtoError, cAPILoadError;
30
+ static VALUE ActiveCallback = Qnil;
31
+
32
+ typedef struct {
33
+ HANDLE library;
34
+ FARPROC function;
35
+ int return_type;
36
+ int prototype[20];
37
+ } Win32API;
38
+
39
+ static void api_free(Win32API* ptr){
40
+ if(ptr->library)
41
+ FreeLibrary(ptr->library);
42
+
43
+ if(ptr)
44
+ free(ptr);
45
+ }
46
+
47
+ static VALUE api_allocate(VALUE klass){
48
+ Win32API* ptr = malloc(sizeof(Win32API));
49
+ return Data_Wrap_Struct(klass, 0, api_free, ptr);
50
+ }
51
+
52
+ /* Helper function that converts the error number returned by GetLastError()
53
+ * into a human readable string. Note that we always use English for error
54
+ * output because that's what Ruby itself does.
55
+ *
56
+ * Internal use only.
57
+ */
58
+ char* StringError(DWORD dwError){
59
+ LPVOID lpMsgBuf;
60
+ static char buf[MAX_PATH];
61
+ DWORD dwLen;
62
+
63
+ // Assume ASCII error messages from the Windows API
64
+ dwLen = FormatMessageA(
65
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
66
+ FORMAT_MESSAGE_FROM_SYSTEM |
67
+ FORMAT_MESSAGE_IGNORE_INSERTS,
68
+ NULL,
69
+ dwError,
70
+ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
71
+ (LPSTR)&lpMsgBuf,
72
+ 0,
73
+ NULL
74
+ );
75
+
76
+ /* It appears that Windows doesn't necessarily ship with the DLL
77
+ * required to always use English error messages. Check for error
78
+ * ERROR_MUI_FILE_NOT_FOUND (15100), and try again if necessary.
79
+ */
80
+ if(!dwLen && GetLastError() == 15100){
81
+ dwLen = FormatMessageA(
82
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
83
+ FORMAT_MESSAGE_FROM_SYSTEM |
84
+ FORMAT_MESSAGE_IGNORE_INSERTS,
85
+ NULL,
86
+ dwError,
87
+ 0,
88
+ (LPSTR)&lpMsgBuf,
89
+ 0,
90
+ NULL
91
+ );
92
+ }
93
+
94
+ if(!dwLen){
95
+ rb_raise(
96
+ cAPIError,
97
+ "Attempt to format message failed (error = '%d')",
98
+ GetLastError()
99
+ );
100
+ }
101
+
102
+ memset(buf, 0, MAX_PATH);
103
+
104
+ /* remove \r\n */
105
+ #ifdef HAVE_STRNCPY_S
106
+ strncpy_s(buf, MAX_PATH, lpMsgBuf, dwLen - 2);
107
+ #else
108
+ strncpy(buf, lpMsgBuf, dwLen - 2);
109
+ #endif
110
+
111
+ LocalFree(lpMsgBuf);
112
+
113
+ return buf;
114
+ }
115
+
116
+ /*
117
+ * call-seq:
118
+ * Win32::API::Callback.new(prototype, return='L'){ |proto| ... }
119
+ *
120
+ * Creates and returns a new Win32::API::Callback object. The prototype
121
+ * arguments are yielded back to the block in the same order they were
122
+ * declared.
123
+ *
124
+ * The +prototype+ is the function prototype for the callback function. This
125
+ * is a string. The possible valid characters are 'I' (integer), 'L' (long),
126
+ * 'V' (void), 'P' (pointer) or 'S' (string). Unlike API objects, API::Callback
127
+ * objects do not have a default prototype.
128
+ *
129
+ * The +return+ argument is the return type for the callback function. The
130
+ * valid characters are the same as for the +prototype+. The default is
131
+ * 'L' (long).
132
+ *
133
+ * Example:
134
+ * require 'win32/api'
135
+ * include Win32
136
+ *
137
+ * EnumWindows = API.new('EnumWindows', 'KP', 'L', 'user32')
138
+ * GetWindowText = API.new('GetWindowText', 'LPI', 'I', 'user32')
139
+ *
140
+ * EnumWindowsProc = API::Callback.new('LP', 'I'){ |handle, param|
141
+ * buf = "\0" * 200
142
+ * GetWindowText.call(handle, buf, 200);
143
+ * puts buf.strip unless buf.strip.empty?
144
+ * buf.index(param).nil? ? true : false
145
+ * }
146
+ *
147
+ * EnumWindows.call(EnumWindowsProc, 'UEDIT32')
148
+ */
149
+ static VALUE callback_init(int argc, VALUE* argv, VALUE self)
150
+ {
151
+ void *find_callback(VALUE,int);
152
+ VALUE v_proto, v_return, v_proc;
153
+ int i;
154
+
155
+ rb_scan_args(argc, argv, "11&", &v_proto, &v_return, &v_proc);
156
+
157
+ /* Validate prototype characters */
158
+ for(i = 0; i < RSTRING_LEN(v_proto); i++){
159
+ switch(RSTRING_PTR(v_proto)[i]){
160
+ case 'I': case 'L': case 'P': case 'V': case 'S':
161
+ break;
162
+ default:
163
+ rb_raise(cAPIProtoError, "Illegal prototype '%c'",
164
+ RSTRING_PTR(v_proto)[i]
165
+ );
166
+ }
167
+ }
168
+
169
+ if(NIL_P(v_return) || RARRAY_LEN(v_return) == 0){
170
+ v_return = rb_str_new2("L");
171
+ }
172
+ else{
173
+ switch(*(char*)RSTRING_PTR(v_return)){
174
+ case 'I': case 'L': case 'P': case 'V': case 'S':
175
+ break;
176
+ default:
177
+ rb_raise(cAPIProtoError, "Illegal return type '%s'",
178
+ RSTRING_PTR(v_return)
179
+ );
180
+ }
181
+ }
182
+
183
+ rb_iv_set(self, "@function", v_proc);
184
+ rb_iv_set(self, "@prototype", v_proto);
185
+ rb_iv_set(self, "@return_type", v_return);
186
+ rb_iv_set(self, "@address", ULONG2NUM((LPARAM)find_callback(self,RSTRING_LEN(v_proto))));
187
+ ActiveCallback = self;
188
+
189
+ return self;
190
+ }
191
+
192
+ /*
193
+ * call-seq:
194
+ * Win32::API.new(function, prototype='V', return='L', dll='kernel32')
195
+ *
196
+ * Creates and returns a new Win32::API object. The +function+ is the name
197
+ * of the Windows function.
198
+ *
199
+ * The +prototype+ is the function prototype for +function+. This can be a
200
+ * string or an array of characters. The possible valid characters are 'I'
201
+ * (integer), 'L' (long), 'V' (void), 'P' (pointer), 'K' (callback) or 'S'
202
+ * (string).
203
+ *
204
+ * The default is void ('V').
205
+ *
206
+ * Constant (const char*) strings should use 'S'. Pass by reference string
207
+ * buffers should use 'P'. The former is faster, but cannot be modified.
208
+ *
209
+ * The +return+ argument is the return type for the function. The valid
210
+ * characters are the same as for the +prototype+. The default is 'L' (long).
211
+ *
212
+ * The +dll+ is the name of the DLL file that the function is exported from.
213
+ * The default is 'kernel32'.
214
+ *
215
+ * If the function cannot be found then an API::Error is raised (a subclass
216
+ * of RuntimeError).
217
+ *
218
+ * Example:
219
+ *
220
+ * require 'win32/api'
221
+ * include Win32
222
+ *
223
+ * buf = 0.chr * 260
224
+ * len = [buf.length].pack('L')
225
+ *
226
+ * GetUserName = API.new('GetUserName', 'PP', 'I', 'advapi32')
227
+ * GetUserName.call(buf, len)
228
+ *
229
+ * puts buf.strip
230
+ */
231
+ static VALUE api_init(int argc, VALUE* argv, VALUE self)
232
+ {
233
+ HMODULE hLibrary;
234
+ FARPROC fProc;
235
+ Win32API* ptr;
236
+ int i;
237
+ char* first = "A";
238
+ char* second = "W";
239
+ VALUE v_proc, v_proto, v_return, v_dll;
240
+
241
+ rb_scan_args(argc, argv, "13", &v_proc, &v_proto, &v_return, &v_dll);
242
+
243
+ Data_Get_Struct(self, Win32API, ptr);
244
+
245
+ // Convert a string prototype to an array of characters
246
+ if(rb_respond_to(v_proto, rb_intern("split")))
247
+ v_proto = rb_str_split(v_proto, "");
248
+
249
+ // Convert a nil or empty prototype to 'V' (void) automatically
250
+ if(NIL_P(v_proto) || RARRAY_LEN(v_proto) == 0){
251
+ v_proto = rb_ary_new();
252
+ rb_ary_push(v_proto, rb_str_new2("V"));
253
+ }
254
+
255
+ // Set an arbitrary limit of 20 parameters
256
+ if(20 < RARRAY_LEN(v_proto))
257
+ rb_raise(rb_eArgError, "too many parameters: %d\n", RARRAY_LEN(v_proto));
258
+
259
+ // Set the default dll to 'kernel32'
260
+ if(NIL_P(v_dll))
261
+ v_dll = rb_str_new2("kernel32");
262
+
263
+ // Set the default return type to 'L' (DWORD)
264
+ if(NIL_P(v_return))
265
+ v_return = rb_str_new2("L");
266
+
267
+ SafeStringValue(v_dll);
268
+ SafeStringValue(v_proc);
269
+
270
+ hLibrary = LoadLibrary(TEXT(RSTRING_PTR(v_dll)));
271
+
272
+ // The most likely cause of failure is a bad DLL load path
273
+ if(!hLibrary){
274
+ rb_raise(cAPILoadError, "LoadLibrary() function failed for '%s': %s",
275
+ RSTRING_PTR(v_dll),
276
+ StringError(GetLastError())
277
+ );
278
+ }
279
+
280
+ ptr->library = hLibrary;
281
+
282
+ /* Attempt to get the function. If it fails, try again with an 'A'
283
+ * appended. If that fails, try again with a 'W' appended. If that
284
+ * still fails, raise an API::LoadLibraryError.
285
+ */
286
+
287
+ fProc = GetProcAddress(hLibrary, TEXT(RSTRING_PTR(v_proc)));
288
+
289
+ // Skip the ANSI and Wide function checks for MSVCRT functions.
290
+ if(!fProc){
291
+ if(strstr(RSTRING_PTR(v_dll), "msvcr")){
292
+ rb_raise(
293
+ cAPILoadError,
294
+ "Unable to load function '%s'",
295
+ RSTRING_PTR(v_proc)
296
+ );
297
+ }
298
+ else{
299
+ VALUE v_ascii = rb_str_new3(v_proc);
300
+ v_ascii = rb_str_cat(v_ascii, first, 1);
301
+ fProc = GetProcAddress(hLibrary, TEXT(RSTRING_PTR(v_ascii)));
302
+
303
+ if(!fProc){
304
+ VALUE v_unicode = rb_str_new3(v_proc);
305
+ v_unicode = rb_str_cat(v_unicode, second, 1);
306
+ fProc = GetProcAddress(hLibrary, TEXT(RSTRING_PTR(v_unicode)));
307
+
308
+ if(!fProc){
309
+ rb_raise(
310
+ cAPILoadError,
311
+ "Unable to load function '%s', '%s', or '%s'",
312
+ RSTRING_PTR(v_proc),
313
+ RSTRING_PTR(v_ascii),
314
+ RSTRING_PTR(v_unicode)
315
+ );
316
+ }
317
+ else{
318
+ rb_iv_set(self, "@effective_function_name", v_unicode);
319
+ }
320
+ }
321
+ else{
322
+ rb_iv_set(self, "@effective_function_name", v_ascii);
323
+ }
324
+ }
325
+ }
326
+ else{
327
+ rb_iv_set(self, "@effective_function_name", v_proc);
328
+ }
329
+
330
+ ptr->function = fProc;
331
+
332
+ // Push the numeric prototypes onto our int array for later use.
333
+
334
+ for(i = 0; i < RARRAY_LEN(v_proto); i++){
335
+ SafeStringValue(RARRAY_PTR(v_proto)[i]);
336
+ switch(*(char*)StringValuePtr(RARRAY_PTR(v_proto)[i])){
337
+ case 'L':
338
+ ptr->prototype[i] = _T_LONG;
339
+ break;
340
+ case 'P':
341
+ ptr->prototype[i] = _T_POINTER;
342
+ break;
343
+ case 'I': case 'B':
344
+ ptr->prototype[i] = _T_INTEGER;
345
+ break;
346
+ case 'V':
347
+ ptr->prototype[i] = _T_VOID;
348
+ break;
349
+ case 'K':
350
+ ptr->prototype[i] = _T_CALLBACK;
351
+ break;
352
+ case 'S':
353
+ ptr->prototype[i] = _T_STRING;
354
+ break;
355
+ default:
356
+ rb_raise(cAPIProtoError, "Illegal prototype '%s'",
357
+ StringValuePtr(RARRAY_PTR(v_proto)[i])
358
+ );
359
+ }
360
+ }
361
+
362
+ // Store the return type for later use.
363
+
364
+ // Automatically convert empty strings or nil to type void.
365
+ if(NIL_P(v_return) || RSTRING_LEN(v_return) == 0){
366
+ v_return = rb_str_new2("V");
367
+ ptr->return_type = _T_VOID;
368
+ }
369
+ else{
370
+ SafeStringValue(v_return);
371
+ switch(*RSTRING_PTR(v_return)){
372
+ case 'L':
373
+ ptr->return_type = _T_LONG;
374
+ break;
375
+ case 'P':
376
+ ptr->return_type = _T_POINTER;
377
+ break;
378
+ case 'I': case 'B':
379
+ ptr->return_type = _T_INTEGER;
380
+ break;
381
+ case 'V':
382
+ ptr->return_type = _T_VOID;
383
+ break;
384
+ case 'S':
385
+ ptr->return_type = _T_STRING;
386
+ break;
387
+ default:
388
+ rb_raise(cAPIProtoError, "Illegal return type '%s'",
389
+ RSTRING_PTR(v_return)
390
+ );
391
+ }
392
+ }
393
+
394
+ rb_iv_set(self, "@dll_name", v_dll);
395
+ rb_iv_set(self, "@function_name", v_proc);
396
+ rb_iv_set(self, "@prototype", v_proto);
397
+ rb_iv_set(self, "@return_type", v_return);
398
+
399
+ return self;
400
+ }
401
+
402
+ /*
403
+ * call-seq:
404
+ *
405
+ * API::Function.new(address, prototype = 'V', return_type = 'L')
406
+ *
407
+ * Creates and returns an API::Function object. This object is similar to an
408
+ * API object, except that instead of a character function name you pass a
409
+ * function pointer address as the first argument, and there's no associated
410
+ * DLL file.
411
+ *
412
+ * Once you have your API::Function object you can then call it the same way
413
+ * you would an API object.
414
+ *
415
+ * Example:
416
+ *
417
+ * require 'win32/api'
418
+ * include Win32
419
+ *
420
+ * LoadLibrary = API.new('LoadLibrary', 'P', 'L')
421
+ * GetProcAddress = API.new('GetProcAddress', 'LP', 'L')
422
+ *
423
+ * # Play a system beep
424
+ * hlib = LoadLibrary.call('user32')
425
+ * addr = GetProcAddress.call(hlib, 'MessageBeep')
426
+ * func = Win32::API::Function.new(addr, 'L', 'L')
427
+ * func.call(0)
428
+ */
429
+ static VALUE func_init(int argc, VALUE* argv, VALUE self){
430
+ Win32API* ptr;
431
+ int i;
432
+ VALUE v_address, v_proto, v_return;
433
+
434
+ rb_scan_args(argc, argv, "12", &v_address, &v_proto, &v_return);
435
+
436
+ Data_Get_Struct(self, Win32API, ptr);
437
+
438
+ // Convert a string prototype to an array of characters
439
+ if(rb_respond_to(v_proto, rb_intern("split")))
440
+ v_proto = rb_str_split(v_proto, "");
441
+
442
+ // Convert a nil or empty prototype to 'V' (void) automatically
443
+ if(NIL_P(v_proto) || RARRAY_LEN(v_proto) == 0){
444
+ v_proto = rb_ary_new();
445
+ rb_ary_push(v_proto, rb_str_new2("V"));
446
+ }
447
+
448
+ // Set an arbitrary limit of 20 parameters
449
+ if(20 < RARRAY_LEN(v_proto))
450
+ rb_raise(rb_eArgError, "too many parameters: %d\n", RARRAY_LEN(v_proto));
451
+
452
+ // Set the default return type to 'L' (DWORD)
453
+ if(NIL_P(v_return))
454
+ v_return = rb_str_new2("L");
455
+
456
+ ptr->function = (FARPROC)NUM2LONG(v_address);
457
+
458
+ // Push the numeric prototypes onto our int array for later use.
459
+
460
+ for(i = 0; i < RARRAY_LEN(v_proto); i++){
461
+ SafeStringValue(RARRAY_PTR(v_proto)[i]);
462
+ switch(*(char*)StringValuePtr(RARRAY_PTR(v_proto)[i])){
463
+ case 'L':
464
+ ptr->prototype[i] = _T_LONG;
465
+ break;
466
+ case 'P':
467
+ ptr->prototype[i] = _T_POINTER;
468
+ break;
469
+ case 'I': case 'B':
470
+ ptr->prototype[i] = _T_INTEGER;
471
+ break;
472
+ case 'V':
473
+ ptr->prototype[i] = _T_VOID;
474
+ break;
475
+ case 'K':
476
+ ptr->prototype[i] = _T_CALLBACK;
477
+ break;
478
+ case 'S':
479
+ ptr->prototype[i] = _T_STRING;
480
+ break;
481
+ default:
482
+ rb_raise(cAPIProtoError, "Illegal prototype '%s'",
483
+ StringValuePtr(RARRAY_PTR(v_proto)[i])
484
+ );
485
+ }
486
+ }
487
+
488
+ // Store the return type for later use.
489
+
490
+ // Automatically convert empty strings or nil to type void.
491
+ if(NIL_P(v_return) || RSTRING_LEN(v_return) == 0){
492
+ v_return = rb_str_new2("V");
493
+ ptr->return_type = _T_VOID;
494
+ }
495
+ else{
496
+ SafeStringValue(v_return);
497
+ switch(*RSTRING_PTR(v_return)){
498
+ case 'L':
499
+ ptr->return_type = _T_LONG;
500
+ break;
501
+ case 'P':
502
+ ptr->return_type = _T_POINTER;
503
+ break;
504
+ case 'I': case 'B':
505
+ ptr->return_type = _T_INTEGER;
506
+ break;
507
+ case 'V':
508
+ ptr->return_type = _T_VOID;
509
+ break;
510
+ case 'S':
511
+ ptr->return_type = _T_STRING;
512
+ break;
513
+ default:
514
+ rb_raise(cAPIProtoError, "Illegal return type '%s'",
515
+ RSTRING_PTR(v_return)
516
+ );
517
+ }
518
+ }
519
+
520
+ rb_iv_set(self, "@address", v_address);
521
+ rb_iv_set(self, "@prototype", v_proto);
522
+ rb_iv_set(self, "@return_type", v_return);
523
+
524
+ return self;
525
+ }
526
+
527
+ typedef struct {
528
+ DWORD params[20];
529
+ } CALLPARAM;
530
+
531
+
532
+ DWORD CallbackFunction(CALLPARAM param, VALUE callback)
533
+ {
534
+ VALUE v_proto, v_return, v_proc, v_retval;
535
+ VALUE argv[20];
536
+ int i, argc;
537
+ char *a_proto;
538
+ char *a_return;
539
+
540
+ if(callback && !NIL_P(callback)){
541
+ v_proto = rb_iv_get(callback, "@prototype");
542
+ a_proto = RSTRING_PTR(v_proto);
543
+
544
+ v_return = rb_iv_get(callback, "@return_type");
545
+ a_return = RSTRING_PTR(v_return);
546
+
547
+ v_proc = rb_iv_get(callback, "@function");
548
+ argc = RSTRING_LEN(v_proto);
549
+
550
+ for(i=0; i < RSTRING_LEN(v_proto); i++){
551
+ argv[i] = Qnil;
552
+ switch(a_proto[i]){
553
+ case 'L':
554
+ argv[i] = ULONG2NUM(param.params[i]);
555
+ break;
556
+ case 'P':
557
+ if(param.params[i])
558
+ argv[i] = rb_str_new2((char *)param.params[i]);
559
+ break;
560
+ case 'I':
561
+ argv[i] = INT2NUM(param.params[i]);
562
+ break;
563
+ default:
564
+ rb_raise(cAPIProtoError, "Illegal prototype '%s'", a_proto[i]);
565
+ }
566
+ }
567
+
568
+ v_retval = rb_funcall2(v_proc, rb_intern("call"), argc, argv);
569
+
570
+ /* Handle true and false explicitly, as some CALLBACK functions
571
+ * require TRUE or FALSE to break out of loops, etc.
572
+ */
573
+ if(v_retval == Qtrue)
574
+ return TRUE;
575
+ else if(v_retval == Qfalse)
576
+ return FALSE;
577
+
578
+ switch (*a_return) {
579
+ case 'I':
580
+ return NUM2INT(v_retval);
581
+ break;
582
+ case 'L':
583
+ return NUM2ULONG(v_retval);
584
+ break;
585
+ case 'S':
586
+ return (unsigned long)RSTRING_PTR(v_retval);
587
+ break;
588
+ case 'P':
589
+ if(NIL_P(v_retval)){
590
+ return 0;
591
+ }
592
+ else if(FIXNUM_P(v_retval)){
593
+ return NUM2ULONG(v_retval);
594
+ }
595
+ else{
596
+ StringValue(v_retval);
597
+ rb_str_modify(v_retval);
598
+ return (unsigned long)StringValuePtr(v_retval);
599
+ }
600
+ break;
601
+ }
602
+ }
603
+
604
+ return 0;
605
+ }
606
+
607
+ #define CALLBACK0(x) DWORD CALLBACK CallbackFunction0_##x() {\
608
+ CALLPARAM param = {0};\
609
+ param.params[0] = 0;\
610
+ return CallbackFunction(param,FuncTable[0][x]);\
611
+ }
612
+
613
+ #define CALLBACK1(x) DWORD CALLBACK CallbackFunction1_##x(DWORD p1) {\
614
+ CALLPARAM param = {p1};\
615
+ return CallbackFunction(param,FuncTable[1][x]);\
616
+ }
617
+
618
+ #define CALLBACK2(x) DWORD CALLBACK CallbackFunction2_##x(\
619
+ DWORD p1, DWORD p2){\
620
+ CALLPARAM param = {p1,p2};\
621
+ return CallbackFunction(param,FuncTable[2][x]);\
622
+ }
623
+
624
+ #define CALLBACK3(x) DWORD CALLBACK CallbackFunction3_##x(\
625
+ DWORD p1, DWORD p2, DWORD p3){\
626
+ CALLPARAM param = {p1,p2,p3};\
627
+ return CallbackFunction(param,FuncTable[3][x]);\
628
+ }
629
+
630
+ #define CALLBACK4(x) DWORD CALLBACK CallbackFunction4_##x(\
631
+ DWORD p1, DWORD p2, DWORD p3, DWORD p4){\
632
+ CALLPARAM param = {p1,p2,p3,p4};\
633
+ return CallbackFunction(param,FuncTable[4][x]);\
634
+ }
635
+
636
+ #define CALLBACK5(x) DWORD CALLBACK CallbackFunction5_##x(\
637
+ DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5\
638
+ ){\
639
+ CALLPARAM param = {p1,p2,p3,p4,p5};\
640
+ return CallbackFunction(param,FuncTable[5][x]);\
641
+ }
642
+
643
+ #define CALLBACK6(x) DWORD CALLBACK CallbackFunction6_##x(\
644
+ DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5, DWORD p6\
645
+ ){\
646
+ CALLPARAM param = {p1,p2,p3,p4,p5,p6};\
647
+ return CallbackFunction(param,FuncTable[6][x]);\
648
+ }
649
+
650
+ #define CALLBACK7(x) DWORD CALLBACK CallbackFunction7_##x(\
651
+ DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5, DWORD p6, DWORD p7\
652
+ ){\
653
+ CALLPARAM param = {p1,p2,p3,p4,p5,p6,p7};\
654
+ return CallbackFunction(param,FuncTable[7][x]);\
655
+ }
656
+
657
+ #define CALLBACK8(x) DWORD CALLBACK CallbackFunction8_##x(\
658
+ DWORD p1, DWORD p2, DWORD p3, DWORD p4,\
659
+ DWORD p5, DWORD p6, DWORD p7, DWORD p8\
660
+ ){\
661
+ CALLPARAM param = {p1,p2,p3,p4,p5,p6,p7,p8};\
662
+ return CallbackFunction(param,FuncTable[8][x]);\
663
+ }
664
+
665
+ #define CALLBACK9(x) DWORD CALLBACK CallbackFunction9_##x(\
666
+ DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5,\
667
+ DWORD p6, DWORD p7, DWORD p8, DWORD p9\
668
+ ){\
669
+ CALLPARAM param = {p1,p2,p3,p4,p5,p6,p7,p8,p9};\
670
+ return CallbackFunction(param,FuncTable[9][x]);\
671
+ }
672
+
673
+ #define DEFCALLBACK(x) CALLBACK##x(0)\
674
+ CALLBACK##x(1)\
675
+ CALLBACK##x(2)\
676
+ CALLBACK##x(3)\
677
+ CALLBACK##x(4)\
678
+ CALLBACK##x(5)\
679
+ CALLBACK##x(6)\
680
+ CALLBACK##x(7)\
681
+ CALLBACK##x(8)\
682
+ CALLBACK##x(9)
683
+
684
+ #define CF(x,y) CallbackFunction##x##_##y
685
+
686
+ static VALUE FuncTable[10][10];
687
+
688
+ DEFCALLBACK(0)
689
+ DEFCALLBACK(1)
690
+ DEFCALLBACK(2)
691
+ DEFCALLBACK(3)
692
+ DEFCALLBACK(4)
693
+ DEFCALLBACK(5)
694
+ DEFCALLBACK(6)
695
+ DEFCALLBACK(7)
696
+ DEFCALLBACK(8)
697
+ DEFCALLBACK(9)
698
+
699
+ void *CallbackTable[10][10] = {
700
+ {CF(0,0),CF(0,1),CF(0,2),CF(0,3),CF(0,4),CF(0,5),CF(0,6),CF(0,7),CF(0,8),CF(0,9)},
701
+ {CF(1,0),CF(1,1),CF(1,2),CF(1,3),CF(1,4),CF(1,5),CF(1,6),CF(1,7),CF(1,8),CF(1,9)},
702
+ {CF(2,0),CF(2,1),CF(2,2),CF(2,3),CF(2,4),CF(2,5),CF(2,6),CF(2,7),CF(2,8),CF(2,9)},
703
+ {CF(3,0),CF(3,1),CF(3,2),CF(3,3),CF(3,4),CF(3,5),CF(3,6),CF(3,7),CF(3,8),CF(3,9)},
704
+ {CF(4,0),CF(4,1),CF(4,2),CF(4,3),CF(4,4),CF(4,5),CF(4,6),CF(4,7),CF(4,8),CF(4,9)},
705
+ {CF(5,0),CF(5,1),CF(5,2),CF(5,3),CF(5,4),CF(5,5),CF(5,6),CF(5,7),CF(5,8),CF(5,9)},
706
+ {CF(6,0),CF(6,1),CF(6,2),CF(6,3),CF(6,4),CF(6,5),CF(6,6),CF(6,7),CF(6,8),CF(6,9)},
707
+ {CF(7,0),CF(7,1),CF(7,2),CF(7,3),CF(7,4),CF(7,5),CF(7,6),CF(7,7),CF(7,8),CF(7,9)},
708
+ {CF(8,0),CF(8,1),CF(8,2),CF(8,3),CF(8,4),CF(8,5),CF(8,6),CF(8,7),CF(8,8),CF(8,9)},
709
+ {CF(9,0),CF(9,1),CF(9,2),CF(9,3),CF(9,4),CF(9,5),CF(9,6),CF(9,7),CF(9,8),CF(9,9)}};
710
+
711
+
712
+ void *find_callback(VALUE obj,int len)
713
+ {
714
+ int i;
715
+ for(i=0;i<10;i++)
716
+ {
717
+ if(FuncTable[len][i]==0)
718
+ break;
719
+ }
720
+ if(i>=10)
721
+ rb_raise(cAPIError,"too many callbacks are defined.");
722
+ FuncTable[len][i] = obj;
723
+ return CallbackTable[len][i];
724
+ }
725
+
726
+ /*
727
+ * call-seq:
728
+ * Win32::API#call(arg1, arg2, ...)
729
+ *
730
+ * Calls the function pointer with the given arguments (if any). Note that,
731
+ * while this method will catch some prototype mismatches (raising a TypeError
732
+ * in the process), it is not fulproof. It is ultimately your job to make
733
+ * sure the arguments match the +prototype+ specified in the constructor.
734
+ *
735
+ * For convenience, nil is converted to NULL, true is converted to TRUE (1)
736
+ * and false is converted to FALSE (0).
737
+ */
738
+ static VALUE api_call(int argc, VALUE* argv, VALUE self){
739
+ VALUE v_proto, v_args, v_arg, v_return;
740
+ Win32API* ptr;
741
+ unsigned long return_value;
742
+ int i = 0;
743
+ int len;
744
+
745
+ struct{
746
+ unsigned long params[20];
747
+ } param;
748
+
749
+ Data_Get_Struct(self, Win32API, ptr);
750
+
751
+ rb_scan_args(argc, argv, "0*", &v_args);
752
+
753
+ v_proto = rb_iv_get(self, "@prototype");
754
+
755
+ // For void prototypes, allow either no args or an explicit nil
756
+ if(RARRAY_LEN(v_proto) != RARRAY_LEN(v_args)){
757
+ char* c = StringValuePtr(RARRAY_PTR(v_proto)[0]);
758
+ if(!strcmp(c, "V")){
759
+ rb_ary_push(v_args, Qnil);
760
+ }
761
+ else{
762
+ rb_raise(rb_eArgError,
763
+ "wrong number of parameters: expected %d, got %d",
764
+ RARRAY_LEN(v_proto), RARRAY_LEN(v_args)
765
+ );
766
+ }
767
+ }
768
+
769
+ len = RARRAY_LEN(v_proto);
770
+
771
+ for(i = 0; i < len; i++){
772
+ v_arg = RARRAY_PTR(v_args)[i];
773
+
774
+ // Convert nil to NULL. Otherwise convert as appropriate.
775
+ if(NIL_P(v_arg))
776
+ param.params[i] = (unsigned long)NULL;
777
+ else if(v_arg == Qtrue)
778
+ param.params[i] = TRUE;
779
+ else if(v_arg == Qfalse)
780
+ param.params[i] = FALSE;
781
+ else
782
+ switch(ptr->prototype[i]){
783
+ case _T_LONG:
784
+ param.params[i] = NUM2ULONG(v_arg);
785
+ break;
786
+ case _T_INTEGER:
787
+ param.params[i] = NUM2INT(v_arg);
788
+ break;
789
+ case _T_POINTER:
790
+ if(FIXNUM_P(v_arg)){
791
+ param.params[i] = NUM2ULONG(v_arg);
792
+ }
793
+ else{
794
+ StringValue(v_arg);
795
+ rb_str_modify(v_arg);
796
+ param.params[i] = (unsigned long)StringValuePtr(v_arg);
797
+ }
798
+ break;
799
+ case _T_CALLBACK:
800
+ ActiveCallback = v_arg;
801
+ v_proto = rb_iv_get(ActiveCallback, "@prototype");
802
+ param.params[i] = (LPARAM)NUM2ULONG(rb_iv_get(ActiveCallback, "@address"));;
803
+ break;
804
+ case _T_STRING:
805
+ param.params[i] = (unsigned long)RSTRING_PTR(v_arg);
806
+ break;
807
+ default:
808
+ param.params[i] = NUM2ULONG(v_arg);
809
+ }
810
+ }
811
+
812
+ /* Call the function, get the return value */
813
+ return_value = ptr->function(param);
814
+
815
+
816
+ /* Return the appropriate type based on the return type specified
817
+ * in the constructor.
818
+ */
819
+ switch(ptr->return_type){
820
+ case _T_INTEGER:
821
+ v_return = INT2NUM(return_value);
822
+ break;
823
+ case _T_LONG:
824
+ v_return = ULONG2NUM(return_value);
825
+ break;
826
+ case _T_VOID:
827
+ v_return = Qnil;
828
+ break;
829
+ case _T_POINTER:
830
+ if(!return_value){
831
+ v_return = Qnil;
832
+ }
833
+ else{
834
+ VALUE v_efunc = rb_iv_get(self, "@effective_function_name");
835
+ char* efunc = RSTRING_PTR(v_efunc);
836
+ if(efunc[strlen(efunc)-1] == 'W'){
837
+ v_return = rb_str_new(
838
+ (TCHAR*)return_value,
839
+ wcslen((wchar_t*)return_value)*2
840
+ );
841
+ }
842
+ else{
843
+ v_return = rb_str_new2((TCHAR*)return_value);
844
+ }
845
+ }
846
+ break;
847
+ case _T_STRING:
848
+ {
849
+ VALUE v_efunc = rb_iv_get(self, "@effective_function_name");
850
+ char* efunc = RSTRING_PTR(v_efunc);
851
+
852
+ if(efunc[strlen(efunc)-1] == 'W'){
853
+ v_return = rb_str_new(
854
+ (TCHAR*)return_value,
855
+ wcslen((wchar_t*)return_value)*2
856
+ );
857
+ }
858
+ else{
859
+ v_return = rb_str_new2((TCHAR*)return_value);
860
+ }
861
+ }
862
+ break;
863
+ default:
864
+ v_return = INT2NUM(0);
865
+ }
866
+
867
+ return v_return;
868
+ }
869
+
870
+ /*
871
+ * Wraps the Windows API functions in a Ruby interface.
872
+ */
873
+ void Init_api(){
874
+ VALUE mWin32, cAPI, cCallback, cFunction;
875
+
876
+ /* Modules and Classes */
877
+
878
+ /* The Win32 module serves as a namespace only */
879
+ mWin32 = rb_define_module("Win32");
880
+
881
+ /* The API class encapsulates a function pointer to Windows API function */
882
+ cAPI = rb_define_class_under(mWin32, "API", rb_cObject);
883
+
884
+ /* The API::Callback class encapsulates a Windows CALLBACK function */
885
+ cCallback = rb_define_class_under(cAPI, "Callback", rb_cObject);
886
+
887
+ /* The API::Function class encapsulates a raw function pointer */
888
+ cFunction = rb_define_class_under(cAPI, "Function", cAPI);
889
+
890
+ /* The API::Error class serves as a base class for other errors */
891
+ cAPIError = rb_define_class_under(cAPI, "Error", rb_eRuntimeError);
892
+
893
+ /* The LoadError class is raised if a function cannot be found or loaded */
894
+ cAPILoadError = rb_define_class_under(cAPI, "LoadLibraryError", cAPIError);
895
+
896
+ /* The PrototypeError class is raised if an invalid prototype is passed */
897
+ cAPIProtoError = rb_define_class_under(cAPI, "PrototypeError", cAPIError);
898
+
899
+ /* Miscellaneous */
900
+ rb_define_alloc_func(cAPI, api_allocate);
901
+
902
+ /* Win32::API Instance Methods */
903
+ rb_define_method(cAPI, "initialize", api_init, -1);
904
+ rb_define_method(cAPI, "call", api_call, -1);
905
+
906
+ /* Win32::API::Callback Instance Methods */
907
+ rb_define_method(cCallback, "initialize", callback_init, -1);
908
+
909
+ /* Win32::API::Function Instance Methods */
910
+ rb_define_method(cFunction, "initialize", func_init, -1);
911
+
912
+ /* The name of the DLL that exports the API function */
913
+ rb_define_attr(cAPI, "dll_name", 1, 0);
914
+
915
+ /* The name of the function passed to the constructor */
916
+ rb_define_attr(cAPI, "function_name", 1, 0);
917
+
918
+ /* The name of the actual function that is returned by the constructor.
919
+ * For example, if you passed 'GetUserName' to the constructor, then the
920
+ * effective function name would be either 'GetUserNameA' or 'GetUserNameW'.
921
+ */
922
+ rb_define_attr(cAPI, "effective_function_name", 1, 0);
923
+
924
+ /* The prototype, returned as an array of characters */
925
+ rb_define_attr(cAPI, "prototype", 1, 0);
926
+
927
+ /* The return type, returned as a single character, S, P, L, I, V or B */
928
+ rb_define_attr(cAPI, "return_type", 1, 0);
929
+
930
+ /* Win32::API::Callback Instance Methods */
931
+
932
+ /* The prototype, returned as an array of characters */
933
+ rb_define_attr(cCallback, "prototype", 1, 0);
934
+
935
+ /* The return type, returned as a single character, S, P, L, I, V or B */
936
+ rb_define_attr(cCallback, "return_type", 1, 0);
937
+
938
+ /* The numeric address of the function pointer */
939
+ rb_define_attr(cCallback, "address", 1, 0);
940
+ rb_define_attr(cFunction, "address", 1, 0);
941
+
942
+ /* Constants */
943
+
944
+ /* 1.4.5: The version of the win32-api library */
945
+ rb_define_const(cAPI, "VERSION", rb_str_new2(WINDOWS_API_VERSION));
946
+ }