win32-api 1.4.2 → 1.4.3

Sign up to get free protection for your applications and to get access to all the features.
data/ext/extconf.rb CHANGED
@@ -1,11 +1,11 @@
1
- ##########################################################################
2
- # extconf.rb
3
- #
4
- # The Windows::API binary should be built using the Rake task, i.e.
5
- # 'rake build' or 'rake install'.
6
- ##########################################################################
7
- require 'mkmf'
8
-
9
- have_func('strncpy_s')
10
-
11
- create_makefile('win32/api', 'win32')
1
+ ##########################################################################
2
+ # extconf.rb
3
+ #
4
+ # The Windows::API binary should be built using the Rake task, i.e.
5
+ # 'rake build' or 'rake install'.
6
+ ##########################################################################
7
+ require 'mkmf'
8
+
9
+ have_func('strncpy_s')
10
+
11
+ create_makefile('win32/api', 'win32')
data/ext/win32/api.c CHANGED
@@ -1,948 +1,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.2"
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.2: The version of this library, returned as a String */
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.3"
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.3: The version of this library, returned as a String */
947
+ rb_define_const(cAPI, "VERSION", rb_str_new2(WINDOWS_API_VERSION));
948
+ }