win32-api 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +2 -0
- data/MANIFEST +9 -0
- data/README +72 -0
- data/ext/extconf.rb +10 -0
- data/ext/win32/api.c +496 -0
- data/test/tc_win32_api.rb +61 -0
- data/test/tc_win32_api_callback.rb +58 -0
- metadata +57 -0
data/CHANGES
ADDED
data/MANIFEST
ADDED
data/README
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
= Description
|
2
|
+
This is a drop-in replacement for the Win32API library currently part of
|
3
|
+
Ruby's standard library.
|
4
|
+
|
5
|
+
= Synopsis
|
6
|
+
require 'win32/api'
|
7
|
+
include Win32
|
8
|
+
|
9
|
+
buf = 0.chr * 260
|
10
|
+
len = [@buf.length].pack('L')
|
11
|
+
|
12
|
+
GetUserName = API.new('GetUserName', 'PP', 'I', 'advapi32')
|
13
|
+
GetUserName.call(buf, len)
|
14
|
+
|
15
|
+
puts buf.strip
|
16
|
+
|
17
|
+
= Differences between win32-api and Win32API
|
18
|
+
* Argument order change. The DLL name is now last, not first.
|
19
|
+
* Removed the 'N' and 'n'. Always use 'L' for longs now.
|
20
|
+
* Sensible default arguments for the prototype, return type and DLL name.
|
21
|
+
* Reader methods for the function name, prototype, return type and DLL.
|
22
|
+
* Removed the support for lower case prototype and return types. Always
|
23
|
+
use capital letters.
|
24
|
+
|
25
|
+
= Developer's Notes
|
26
|
+
The current Win32API library that ships with the standard library has been
|
27
|
+
slated for removal from Ruby 2.0 and it will not receive any updates in the
|
28
|
+
Ruby 1.8.x branch. I have far too many libraries invested in it to let it
|
29
|
+
die at this point.
|
30
|
+
|
31
|
+
In addition, the current Win32API library was written in the bad old Ruby
|
32
|
+
1.6.x days, which means it doesn't use the newer allocation framework.
|
33
|
+
There were several other refactorings that I felt it needed to more closely
|
34
|
+
match how it was actually being used in practice.
|
35
|
+
|
36
|
+
The first order of business was changing the order of the arguments. By
|
37
|
+
moving the DLL name from first to last, I was able to provide reasonable
|
38
|
+
default arguments for the prototype, return type and the DLL. Only the
|
39
|
+
function name is required now.
|
40
|
+
|
41
|
+
There was a laundry list of other refactorings that were needed: sensical
|
42
|
+
instance variable names with proper accessors, removing support for lower
|
43
|
+
case prototype and return value characters that no one used in practice,
|
44
|
+
better naming conventions, the addition of RDoc ready comments and,
|
45
|
+
especially, callback support.
|
46
|
+
|
47
|
+
Most importantly, we can now add, modify and fix any features that we feel
|
48
|
+
best benefit our end users.
|
49
|
+
|
50
|
+
= Documentation
|
51
|
+
The source file contains inline RDoc documentation. If you installed
|
52
|
+
this file as a gem, then you have the docs.
|
53
|
+
|
54
|
+
= Warranty
|
55
|
+
This package is provided "as is" and without any express or
|
56
|
+
implied warranties, including, without limitation, the implied
|
57
|
+
warranties of merchantability and fitness for a particular purpose.
|
58
|
+
|
59
|
+
= Known Bugs
|
60
|
+
None that I'm aware of. Please submit any bug reports to the project page
|
61
|
+
at http://www.rubyforge.org/projects/win32utils.
|
62
|
+
|
63
|
+
= Copyright
|
64
|
+
(C) 2003-2007 Daniel J. Berger
|
65
|
+
All Rights Reserved
|
66
|
+
|
67
|
+
= License
|
68
|
+
Ruby's
|
69
|
+
|
70
|
+
= Authors
|
71
|
+
Daniel J. Berger
|
72
|
+
Park Heesob
|
data/ext/extconf.rb
ADDED
@@ -0,0 +1,10 @@
|
|
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
|
+
create_makefile('win32/api')
|
data/ext/win32/api.c
ADDED
@@ -0,0 +1,496 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <windows.h>
|
3
|
+
|
4
|
+
#define MAX_BUF 1024
|
5
|
+
#define WINDOWS_API_VERSION "1.0.0"
|
6
|
+
|
7
|
+
#define _T_VOID 0
|
8
|
+
#define _T_LONG 1
|
9
|
+
#define _T_POINTER 2
|
10
|
+
#define _T_INTEGER 3
|
11
|
+
#define _T_CALLBACK 4
|
12
|
+
|
13
|
+
VALUE cAPIError, cCallbackError;
|
14
|
+
|
15
|
+
/* Helper function that converts the error number returned by GetLastError()
|
16
|
+
* into a human readable string. Internal use only.
|
17
|
+
*/
|
18
|
+
char* StringError(DWORD dwError){
|
19
|
+
LPVOID lpMsgBuf;
|
20
|
+
static char buf[MAX_PATH];
|
21
|
+
DWORD dwLen;
|
22
|
+
|
23
|
+
/* Assume ASCII error messages from the Windows API */
|
24
|
+
dwLen = FormatMessageA(
|
25
|
+
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
26
|
+
FORMAT_MESSAGE_FROM_SYSTEM |
|
27
|
+
FORMAT_MESSAGE_IGNORE_INSERTS,
|
28
|
+
NULL,
|
29
|
+
dwError,
|
30
|
+
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
31
|
+
(LPSTR)&lpMsgBuf,
|
32
|
+
0,
|
33
|
+
NULL
|
34
|
+
);
|
35
|
+
|
36
|
+
if(!dwLen)
|
37
|
+
rb_raise(cAPIError, "Attempt to format message failed");
|
38
|
+
|
39
|
+
memset(buf, 0, MAX_PATH);
|
40
|
+
|
41
|
+
/* remove \r\n */
|
42
|
+
#ifdef HAVE_STRNCPY_S
|
43
|
+
strncpy_s(buf, MAX_PATH, lpMsgBuf, dwLen - 2);
|
44
|
+
#else
|
45
|
+
strncpy(buf, lpMsgBuf, dwLen - 2);
|
46
|
+
#endif
|
47
|
+
|
48
|
+
LocalFree(lpMsgBuf);
|
49
|
+
|
50
|
+
return buf;
|
51
|
+
}
|
52
|
+
|
53
|
+
/*
|
54
|
+
* call-seq:
|
55
|
+
* Win32::API::Callback.new(prototype, return='L')
|
56
|
+
*
|
57
|
+
* Creates and returns a new Win32::API::Callback object. The +function+ is
|
58
|
+
* the name of the Windows function.
|
59
|
+
*
|
60
|
+
* The +prototype+ is the function prototype for the callback function. This
|
61
|
+
* is a string. The possible valid characters are 'I' (integer), 'L' (long),
|
62
|
+
* 'V' (void), or 'P' (pointer). Unlike API objects, API::Callback objects
|
63
|
+
* do not have a default prototype.
|
64
|
+
*
|
65
|
+
* The +return+ argument is the return type for the callback function. The
|
66
|
+
* valid characters are the same as for the +prototype+. The default is
|
67
|
+
* 'L' (long).
|
68
|
+
*/
|
69
|
+
static VALUE callback_init(int argc, VALUE* argv, VALUE self)
|
70
|
+
{
|
71
|
+
VALUE v_proto, v_return, v_proc;
|
72
|
+
|
73
|
+
rb_scan_args(argc, argv, "11", &v_proto, &v_return);
|
74
|
+
|
75
|
+
if(NIL_P(v_return) || RARRAY(v_return)->len == 0)
|
76
|
+
v_return = rb_str_new2("L");
|
77
|
+
|
78
|
+
if(rb_block_given_p())
|
79
|
+
v_proc = rb_block_proc();
|
80
|
+
else
|
81
|
+
v_proc = Qnil;
|
82
|
+
|
83
|
+
rb_iv_set(self, "@function", v_proc);
|
84
|
+
rb_iv_set(self, "@prototype", v_proto);
|
85
|
+
rb_iv_set(self, "@return_type", v_return);
|
86
|
+
|
87
|
+
return self;
|
88
|
+
}
|
89
|
+
|
90
|
+
|
91
|
+
typedef struct {
|
92
|
+
HANDLE library;
|
93
|
+
FARPROC function;
|
94
|
+
int return_type;
|
95
|
+
int prototype[];
|
96
|
+
} Win32API;
|
97
|
+
|
98
|
+
static void api_free(Win32API* ptr){
|
99
|
+
if(ptr->library)
|
100
|
+
FreeLibrary(ptr->library);
|
101
|
+
|
102
|
+
free(ptr);
|
103
|
+
}
|
104
|
+
|
105
|
+
static VALUE api_allocate(VALUE klass){
|
106
|
+
Win32API* ptr = malloc(sizeof(Win32API));
|
107
|
+
return Data_Wrap_Struct(klass, 0, api_free, ptr);
|
108
|
+
}
|
109
|
+
|
110
|
+
/*
|
111
|
+
* call-seq:
|
112
|
+
* Win32::API.new(function, prototype='V', return='L', dll='kernel32')
|
113
|
+
*
|
114
|
+
* Creates and returns a new Win32::API object. The +function+ is the name
|
115
|
+
* of the Windows function.
|
116
|
+
*
|
117
|
+
* The +prototype+ is the function prototype for +function+. This can be a
|
118
|
+
* string or an array of characters. The possible valid characters are 'I'
|
119
|
+
* (integer), 'L' (long), 'V' (void), 'P' (pointer), or 'K' (callback).
|
120
|
+
* The default is void ('V').
|
121
|
+
*
|
122
|
+
* The +return+ argument is the return type for the function. The valid
|
123
|
+
* characters are the same as for the +prototype+. The default is 'L' (long).
|
124
|
+
*
|
125
|
+
* The +dll+ is the name of the DLL file that the function is exported from.
|
126
|
+
* The default is 'kernel32'.
|
127
|
+
*
|
128
|
+
* If the function cannot be found then an API::Error is raised (a subclass
|
129
|
+
* of RuntimeError).
|
130
|
+
*/
|
131
|
+
static VALUE api_init(int argc, VALUE* argv, VALUE self)
|
132
|
+
{
|
133
|
+
HMODULE hLibrary;
|
134
|
+
FARPROC fProc;
|
135
|
+
Win32API* ptr;
|
136
|
+
int i;
|
137
|
+
VALUE v_proc, v_proto, v_return, v_dll, v_bool, v_name;
|
138
|
+
|
139
|
+
rb_scan_args(argc, argv, "13", &v_proc, &v_proto, &v_return, &v_dll);
|
140
|
+
|
141
|
+
Data_Get_Struct(self, Win32API, ptr);
|
142
|
+
|
143
|
+
/* Convert a string prototype to an array of characters */
|
144
|
+
if(rb_respond_to(v_proto, rb_intern("split")))
|
145
|
+
v_proto = rb_str_split(v_proto, "");
|
146
|
+
|
147
|
+
/* Set an arbitrary limit of 16 parameters */
|
148
|
+
if(16 < RARRAY(v_proto)->len)
|
149
|
+
rb_raise(rb_eArgError, "too many parameters: %d\n", RARRAY(v_proto)->len);
|
150
|
+
|
151
|
+
/* Convert a nil or empty prototype to 'V' (void) automatically */
|
152
|
+
if(NIL_P(v_proto) || RARRAY(v_proto)->len == 0){
|
153
|
+
v_proto = rb_ary_new();
|
154
|
+
rb_ary_push(v_proto, rb_str_new2("V"));
|
155
|
+
}
|
156
|
+
|
157
|
+
/* Set the default dll to 'kernel32' */
|
158
|
+
if(NIL_P(v_dll))
|
159
|
+
v_dll = rb_str_new2("kernel32");
|
160
|
+
|
161
|
+
/* Set the default return type to 'L' (DWORD) */
|
162
|
+
if(NIL_P(v_return))
|
163
|
+
v_return = rb_str_new2("L");
|
164
|
+
|
165
|
+
SafeStringValue(v_dll);
|
166
|
+
SafeStringValue(v_proc);
|
167
|
+
|
168
|
+
hLibrary = LoadLibrary(TEXT(RSTRING(v_dll)->ptr));
|
169
|
+
|
170
|
+
/* The most likely cause of failure is a bad DLL load path */
|
171
|
+
if(!hLibrary){
|
172
|
+
rb_raise(cAPIError, "LoadLibrary() function failed for '%s': %s",
|
173
|
+
RSTRING(v_dll)->ptr,
|
174
|
+
StringError(GetLastError())
|
175
|
+
);
|
176
|
+
}
|
177
|
+
|
178
|
+
ptr->library = hLibrary;
|
179
|
+
|
180
|
+
/* Attempt to get the function. If it fails, try again with an 'A'
|
181
|
+
* appended. If that fails, try again with a 'W' appended. If that
|
182
|
+
* still fails, raise an API::Error.
|
183
|
+
*/
|
184
|
+
fProc = GetProcAddress(hLibrary, TEXT(RSTRING(v_proc)->ptr));
|
185
|
+
|
186
|
+
if(!fProc){
|
187
|
+
VALUE v_ascii = rb_str_new3(v_proc);
|
188
|
+
v_ascii = rb_str_cat(v_ascii, "A", 1);
|
189
|
+
fProc = GetProcAddress(hLibrary, TEXT(RSTRING(v_ascii)->ptr));
|
190
|
+
|
191
|
+
if(!fProc){
|
192
|
+
VALUE v_unicode = rb_str_new3(v_proc);
|
193
|
+
v_unicode = rb_str_cat(v_unicode, "W", 1);
|
194
|
+
fProc = GetProcAddress(hLibrary, TEXT(RSTRING(v_unicode)->ptr));
|
195
|
+
|
196
|
+
if(!fProc){
|
197
|
+
rb_raise(
|
198
|
+
cAPIError,
|
199
|
+
"GetProcAddress() failed for '%s', '%s' and '%s': %s",
|
200
|
+
RSTRING(v_proc)->ptr,
|
201
|
+
RSTRING(v_ascii)->ptr,
|
202
|
+
RSTRING(v_unicode)->ptr,
|
203
|
+
StringError(GetLastError())
|
204
|
+
);
|
205
|
+
}
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
ptr->function = fProc;
|
210
|
+
|
211
|
+
/* Push the numeric prototypes onto our int array for later use. */
|
212
|
+
for(i = 0; i < RARRAY(v_proto)->len; i++){
|
213
|
+
SafeStringValue(RARRAY(v_proto)->ptr[i]);
|
214
|
+
switch(*(char*)StringValuePtr(RARRAY(v_proto)->ptr[i])){
|
215
|
+
case 'L':
|
216
|
+
ptr->prototype[i] = _T_LONG;
|
217
|
+
break;
|
218
|
+
case 'P':
|
219
|
+
ptr->prototype[i] = _T_POINTER;
|
220
|
+
break;
|
221
|
+
case 'I': case 'B':
|
222
|
+
ptr->prototype[i] = _T_INTEGER;
|
223
|
+
break;
|
224
|
+
case 'V':
|
225
|
+
ptr->prototype[i] = _T_VOID;
|
226
|
+
break;
|
227
|
+
case 'K':
|
228
|
+
ptr->prototype[i] = _T_CALLBACK;
|
229
|
+
break;
|
230
|
+
default:
|
231
|
+
rb_raise(cAPIError, "Illegal prototype '%s'", RARRAY(v_proto)->ptr[i]);
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
/* Store the return type for later use. Automatically convert empty
|
236
|
+
* strings or nil to type void.
|
237
|
+
*/
|
238
|
+
if(NIL_P(v_return) || RSTRING(v_return)->len == 0){
|
239
|
+
v_return = rb_str_new2("V");
|
240
|
+
ptr->return_type = _T_VOID;
|
241
|
+
}
|
242
|
+
else{
|
243
|
+
SafeStringValue(v_return);
|
244
|
+
switch(*RSTRING(v_return)->ptr){
|
245
|
+
case 'L':
|
246
|
+
ptr->return_type = _T_LONG;
|
247
|
+
break;
|
248
|
+
case 'P':
|
249
|
+
ptr->return_type = _T_POINTER;
|
250
|
+
break;
|
251
|
+
case 'I': case 'B':
|
252
|
+
ptr->return_type = _T_INTEGER;
|
253
|
+
break;
|
254
|
+
case 'V':
|
255
|
+
ptr->return_type = _T_VOID;
|
256
|
+
break;
|
257
|
+
default:
|
258
|
+
rb_raise(cAPIError, "Illegal prototype '%s'", RARRAY(v_proto)->ptr[i]);
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
rb_iv_set(self, "@dll_name", v_dll);
|
263
|
+
rb_iv_set(self, "@function_name", v_proc);
|
264
|
+
rb_iv_set(self, "@prototype", v_proto);
|
265
|
+
rb_iv_set(self, "@return_type", v_return);
|
266
|
+
|
267
|
+
return self;
|
268
|
+
}
|
269
|
+
|
270
|
+
typedef struct {
|
271
|
+
DWORD params[1];
|
272
|
+
} PARAM;
|
273
|
+
|
274
|
+
static VALUE ActiveCallback;
|
275
|
+
|
276
|
+
DWORD CallbackFunction(PARAM param)
|
277
|
+
{
|
278
|
+
VALUE v_proto, v_return, v_proc, v_retval;
|
279
|
+
VALUE argv[16];
|
280
|
+
int i, argc;
|
281
|
+
char *a_proto;
|
282
|
+
char *a_return;
|
283
|
+
|
284
|
+
if(!NIL_P(ActiveCallback)){
|
285
|
+
v_proto = rb_iv_get(ActiveCallback, "@prototype");
|
286
|
+
a_proto = RSTRING(v_proto)->ptr;
|
287
|
+
|
288
|
+
v_return = rb_iv_get(ActiveCallback, "@return_type");
|
289
|
+
a_return = RSTRING(v_return)->ptr;
|
290
|
+
|
291
|
+
v_proc = rb_iv_get(ActiveCallback, "@function");
|
292
|
+
argc = RSTRING(v_proto)->len;
|
293
|
+
|
294
|
+
for(i=0; i < RSTRING(v_proto)->len; i++){
|
295
|
+
argv[i] = Qnil;
|
296
|
+
switch(a_proto[i]){
|
297
|
+
case 'L':
|
298
|
+
argv[i] = ULONG2NUM(param.params[i]);
|
299
|
+
break;
|
300
|
+
case 'P':
|
301
|
+
if(param.params[i])
|
302
|
+
argv[i] = rb_str_new2((char *)param.params[i]);
|
303
|
+
break;
|
304
|
+
case 'I':
|
305
|
+
argv[i] = INT2NUM(param.params[i]);
|
306
|
+
break;
|
307
|
+
default:
|
308
|
+
rb_raise(cCallbackError, "Illegal prototype '%s'", a_proto[i]);
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
v_retval = rb_funcall2(v_proc, rb_intern("call"), argc, argv);
|
313
|
+
|
314
|
+
/* Handle true and false explicitly, as some CALLBACK functions
|
315
|
+
* require TRUE or FALSE to break out of loops, etc.
|
316
|
+
*/
|
317
|
+
if(v_retval == Qtrue)
|
318
|
+
return TRUE;
|
319
|
+
else if(v_retval == Qfalse)
|
320
|
+
return FALSE;
|
321
|
+
|
322
|
+
switch (*a_return) {
|
323
|
+
case 'I':
|
324
|
+
return NUM2INT(v_retval);
|
325
|
+
break;
|
326
|
+
case 'L':
|
327
|
+
return NUM2ULONG(v_retval);
|
328
|
+
break;
|
329
|
+
case 'P':
|
330
|
+
if(NIL_P(v_retval)){
|
331
|
+
return 0;
|
332
|
+
}
|
333
|
+
else if (FIXNUM_P(v_retval)){
|
334
|
+
return NUM2ULONG(v_retval);
|
335
|
+
}
|
336
|
+
else{
|
337
|
+
StringValue(v_retval);
|
338
|
+
rb_str_modify(v_retval);
|
339
|
+
return (unsigned long)StringValuePtr(v_retval);
|
340
|
+
}
|
341
|
+
break;
|
342
|
+
}
|
343
|
+
}
|
344
|
+
|
345
|
+
return 0;
|
346
|
+
}
|
347
|
+
|
348
|
+
/*
|
349
|
+
* call-seq:
|
350
|
+
* Win32::API#call(arg1, arg2, ...)
|
351
|
+
*
|
352
|
+
* Calls the function pointer with the given arguments (if any). Note that,
|
353
|
+
* while this method will catch some prototype mismatches (raising a TypeError
|
354
|
+
* in the process), it is not fulproof. It is ultimately your job to make
|
355
|
+
* sure the arguments match the +prototype+ specified in the constructor.
|
356
|
+
*
|
357
|
+
* For convenience, nil is converted to NULL, true is converted to TRUE (1)
|
358
|
+
* and false is converted to FALSE (0).
|
359
|
+
*/
|
360
|
+
static VALUE api_call(int argc, VALUE* argv, VALUE self){
|
361
|
+
VALUE v_proto, v_args, v_arg, v_return;
|
362
|
+
Win32API* ptr;
|
363
|
+
unsigned long return_value;
|
364
|
+
int i = 0;
|
365
|
+
|
366
|
+
struct{
|
367
|
+
unsigned long params[16];
|
368
|
+
} param;
|
369
|
+
|
370
|
+
Data_Get_Struct(self, Win32API, ptr);
|
371
|
+
|
372
|
+
rb_scan_args(argc, argv, "0*", &v_args);
|
373
|
+
|
374
|
+
v_proto = rb_iv_get(self, "@prototype");
|
375
|
+
|
376
|
+
if(RARRAY(v_proto)->len != RARRAY(v_args)->len)
|
377
|
+
rb_raise(rb_eArgError, "wrong number of parameters: expected %d, got %d",
|
378
|
+
RARRAY(v_proto)->len, RARRAY(v_args)->len
|
379
|
+
);
|
380
|
+
|
381
|
+
for(i = 0; i < RARRAY(v_proto)->len; i++){
|
382
|
+
v_arg = RARRAY(v_args)->ptr[i];
|
383
|
+
|
384
|
+
/* Convert nil to NULL. Otherwise convert as appropriate. */
|
385
|
+
if(NIL_P(v_arg))
|
386
|
+
param.params[i] = (unsigned long)NULL;
|
387
|
+
else if(v_arg == Qtrue)
|
388
|
+
param.params[i] = TRUE;
|
389
|
+
else if(v_arg == Qfalse)
|
390
|
+
param.params[i] = FALSE;
|
391
|
+
else
|
392
|
+
switch(ptr->prototype[i]){
|
393
|
+
case _T_LONG:
|
394
|
+
param.params[i] = NUM2ULONG(v_arg);
|
395
|
+
break;
|
396
|
+
case _T_INTEGER:
|
397
|
+
param.params[i] = NUM2INT(v_arg);
|
398
|
+
break;
|
399
|
+
case _T_POINTER:
|
400
|
+
if(FIXNUM_P(v_arg))
|
401
|
+
param.params[i] = NUM2ULONG(v_arg);
|
402
|
+
else
|
403
|
+
param.params[i] = (unsigned long)StringValuePtr(v_arg);
|
404
|
+
break;
|
405
|
+
case _T_CALLBACK:
|
406
|
+
ActiveCallback = v_arg;
|
407
|
+
param.params[i] = (LPARAM)CallbackFunction;
|
408
|
+
break;
|
409
|
+
default:
|
410
|
+
param.params[i] = NUM2ULONG(v_arg);
|
411
|
+
}
|
412
|
+
}
|
413
|
+
|
414
|
+
/* Call the function, get the return value */
|
415
|
+
return_value = ptr->function(param);
|
416
|
+
|
417
|
+
/* Return the appropriate type based on the return type specified
|
418
|
+
* in the constructor.
|
419
|
+
*/
|
420
|
+
switch(ptr->return_type){
|
421
|
+
case _T_INTEGER:
|
422
|
+
v_return = INT2NUM(return_value);
|
423
|
+
break;
|
424
|
+
case _T_LONG:
|
425
|
+
v_return = LONG2NUM(return_value);
|
426
|
+
break;
|
427
|
+
case _T_VOID:
|
428
|
+
v_return = Qnil;
|
429
|
+
break;
|
430
|
+
case _T_POINTER:
|
431
|
+
v_return = rb_str_new2((TCHAR*)return_value);
|
432
|
+
break;
|
433
|
+
default:
|
434
|
+
v_return = INT2NUM(0);
|
435
|
+
}
|
436
|
+
|
437
|
+
return v_return;
|
438
|
+
}
|
439
|
+
|
440
|
+
/*
|
441
|
+
* Wraps the Windows API functions in a Ruby interface.
|
442
|
+
*/
|
443
|
+
void Init_api(){
|
444
|
+
VALUE mWin32, cAPI, cCallback;
|
445
|
+
|
446
|
+
/* Modules and Classes */
|
447
|
+
|
448
|
+
/* The Win32 module serves as a namespace only */
|
449
|
+
mWin32 = rb_define_module("Win32");
|
450
|
+
|
451
|
+
/* The API class encapsulates a function pointer to Windows API function */
|
452
|
+
cAPI = rb_define_class_under(mWin32, "API", rb_cObject);
|
453
|
+
|
454
|
+
/* The API::Callback class encapsulates a Windows CALLBACK function */
|
455
|
+
cCallback = rb_define_class_under(cAPI, "Callback", rb_cObject);
|
456
|
+
|
457
|
+
/* The API::Error class is raised if the constructor fails */
|
458
|
+
cAPIError = rb_define_class_under(cAPI, "Error", rb_eRuntimeError);
|
459
|
+
|
460
|
+
/* The API::Callback::Error class is raised if the constructor fails */
|
461
|
+
cCallbackError = rb_define_class_under(cCallback, "Error", rb_eRuntimeError);
|
462
|
+
|
463
|
+
/* Miscellaneous */
|
464
|
+
rb_define_alloc_func(cAPI, api_allocate);
|
465
|
+
|
466
|
+
/* Win32::API Instance Methods */
|
467
|
+
rb_define_method(cAPI, "initialize", api_init, -1);
|
468
|
+
rb_define_method(cAPI, "call", api_call, -1);
|
469
|
+
|
470
|
+
rb_define_method(cCallback, "initialize", callback_init, -1);
|
471
|
+
|
472
|
+
/* The name of the DLL that exports the API function */
|
473
|
+
rb_define_attr(cAPI, "dll_name", 1, 0);
|
474
|
+
|
475
|
+
/* The name of the function */
|
476
|
+
rb_define_attr(cAPI, "function_name", 1, 0);
|
477
|
+
|
478
|
+
/* The prototype, returned as an array of characters */
|
479
|
+
rb_define_attr(cAPI, "prototype", 1, 0);
|
480
|
+
|
481
|
+
/* The return type, returned as a single character, P, L, I, V or B */
|
482
|
+
rb_define_attr(cAPI, "return_type", 1, 0);
|
483
|
+
|
484
|
+
/* Win32::API::Callback Instance Methods */
|
485
|
+
|
486
|
+
/* The prototype, returned as an array of characters */
|
487
|
+
rb_define_attr(cCallback, "prototype", 1, 0);
|
488
|
+
|
489
|
+
/* The return type, returned as a single character, P, L, I, V or B */
|
490
|
+
rb_define_attr(cCallback, "return_type", 1, 0);
|
491
|
+
|
492
|
+
/* Constants */
|
493
|
+
|
494
|
+
/* 1.0.0: The version of this library, returned as a String */
|
495
|
+
rb_define_const(cAPI, "VERSION", rb_str_new2(WINDOWS_API_VERSION));
|
496
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
############################################################################
|
2
|
+
# tc_win32_api.rb
|
3
|
+
#
|
4
|
+
# Test case for the Win32::API class. You should run this as Rake task,
|
5
|
+
# i.e. 'rake test', instead of running it directly.
|
6
|
+
############################################################################
|
7
|
+
require 'win32/api'
|
8
|
+
require 'test/unit'
|
9
|
+
include Win32
|
10
|
+
|
11
|
+
class TC_Win32_API < Test::Unit::TestCase
|
12
|
+
def setup
|
13
|
+
@buf = 0.chr * 260
|
14
|
+
@api = API.new('GetCurrentDirectory', 'LP')
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_version
|
18
|
+
assert_equal('1.0.0', API::VERSION)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_call
|
22
|
+
assert_respond_to(@api, :call)
|
23
|
+
assert_nothing_raised{ @api.call(@buf.length, @buf) }
|
24
|
+
assert_equal(Dir.pwd.tr('/', "\\"), @buf.strip)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_dll_name
|
28
|
+
assert_respond_to(@api, :dll_name)
|
29
|
+
assert_equal('kernel32', @api.dll_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_function_name
|
33
|
+
assert_respond_to(@api, :function_name)
|
34
|
+
assert_equal('GetCurrentDirectory', @api.function_name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_prototype
|
38
|
+
assert_respond_to(@api, :prototype)
|
39
|
+
assert_equal(['L', 'P'], @api.prototype)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_return_type
|
43
|
+
assert_respond_to(@api, :return_type)
|
44
|
+
assert_equal('L', @api.return_type)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_constructor_expected_failures
|
48
|
+
assert_raise(API::Error){ API.new('GetUserName', 'PL', 'I', 'foo') }
|
49
|
+
assert_raise(API::Error){ API.new('GetUserName', 'X', 'I', 'foo') }
|
50
|
+
assert_raise(API::Error){ API.new('GetUserName', 'PL', 'X', 'foo') }
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_call_expected_failures
|
54
|
+
assert_raise(TypeError){ @api.call('test', @buf) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def teardown
|
58
|
+
@buf = nil
|
59
|
+
@api = nil
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
############################################################################
|
2
|
+
# tc_win32_api_callback.rb
|
3
|
+
#
|
4
|
+
# Test case for the Win32::API::Callback class. You should run this as Rake
|
5
|
+
# task, i.e. 'rake test', instead of running it directly.
|
6
|
+
############################################################################
|
7
|
+
require 'win32/api'
|
8
|
+
require 'test/unit'
|
9
|
+
include Win32
|
10
|
+
|
11
|
+
class TC_Win32_API_Callback < Test::Unit::TestCase
|
12
|
+
def setup
|
13
|
+
@buffer = 0.chr * 260
|
14
|
+
@api_ew = API.new('EnumWindows', 'KP', 'L', 'user32')
|
15
|
+
@api_gwt = API.new('GetWindowText', 'LPI', 'I', 'user32')
|
16
|
+
@callback = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_constructor
|
20
|
+
assert_respond_to(API::Callback, :new)
|
21
|
+
assert_nothing_raised{ API::Callback.new('LP', 'I') }
|
22
|
+
assert_nothing_raised{ API::Callback.new('LP', 'I'){} }
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_prototype
|
26
|
+
assert_nothing_raised{ @callback = API::Callback.new('LP', 'I') }
|
27
|
+
assert_respond_to(@callback, :prototype)
|
28
|
+
assert_equal('LP', @callback.prototype)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_return_value
|
32
|
+
assert_nothing_raised{ @callback = API::Callback.new('LP', 'I') }
|
33
|
+
assert_respond_to(@callback, :return_type)
|
34
|
+
assert_equal('I', @callback.return_type)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_callback
|
38
|
+
assert_nothing_raised{
|
39
|
+
@callback = API::Callback.new('LP', 'I'){ |handle, param|
|
40
|
+
buf = "\0" * 200
|
41
|
+
@api_gwt.call(handle, buf, 200);
|
42
|
+
buf.index(param).nil? ? true : false
|
43
|
+
}
|
44
|
+
}
|
45
|
+
assert_nothing_raised{ @api_ew.call(@callback, 'UEDIT32') }
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_constructor_expected_errors
|
49
|
+
assert_raise(API::Callback::Error){ API::Callback.new('X') }
|
50
|
+
end
|
51
|
+
|
52
|
+
def teardown
|
53
|
+
@buffer = nil
|
54
|
+
@api_ew = nil
|
55
|
+
@api_gwt = nil
|
56
|
+
@callback = nil
|
57
|
+
end
|
58
|
+
end
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: win32-api
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 1.0.0
|
7
|
+
date: 2007-09-14 00:00:00 -06:00
|
8
|
+
summary: A superior replacement for Win32API
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: djberg96@gmail.com
|
12
|
+
homepage: http://www.rubyforge.org/projects/win32utils
|
13
|
+
rubyforge_project: win32utils
|
14
|
+
description: A superior replacement for Win32API
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.8.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Daniel J. Berger
|
31
|
+
files:
|
32
|
+
- ext/extconf.rb
|
33
|
+
- ext/win32
|
34
|
+
- test/tc_win32_api.rb
|
35
|
+
- test/tc_win32_api_callback.rb
|
36
|
+
- README
|
37
|
+
- CHANGES
|
38
|
+
- MANIFEST
|
39
|
+
- ext/win32/api.c
|
40
|
+
test_files:
|
41
|
+
- test/tc_win32_api.rb
|
42
|
+
- test/tc_win32_api_callback.rb
|
43
|
+
rdoc_options: []
|
44
|
+
|
45
|
+
extra_rdoc_files:
|
46
|
+
- README
|
47
|
+
- CHANGES
|
48
|
+
- MANIFEST
|
49
|
+
- ext/win32/api.c
|
50
|
+
executables: []
|
51
|
+
|
52
|
+
extensions:
|
53
|
+
- ext/extconf.rb
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
dependencies: []
|
57
|
+
|