win32-api 1.4.6-x86-mingw32

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