win32-api 1.4.6-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }