win32-api 1.4.1 → 1.4.2

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