win32-api 1.5.0-universal-mingw32

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