/* * call-seq: * Win32::API.new(function, prototype='V', return='L', dll='kernel32') * * Creates and returns a new Win32::API object. The +function+ is the name * of the Windows function. * * The +prototype+ is the function prototype for +function+. This can be a * string or an array of characters. The possible valid characters are 'I' * (integer), 'L' (long), 'V' (void), 'P' (pointer), or 'K' (callback). * The default is void ('V'). * * The +return+ argument is the return type for the function. The valid * characters are the same as for the +prototype+. The default is 'L' (long). * * The +dll+ is the name of the DLL file that the function is exported from. * The default is 'kernel32'. * * If the function cannot be found then an API::Error is raised (a subclass * of RuntimeError). */ static VALUE api_init(int argc, VALUE* argv, VALUE self) { HMODULE hLibrary; FARPROC fProc; Win32API* ptr; int i; VALUE v_proc, v_proto, v_return, v_dll, v_bool, v_name; rb_scan_args(argc, argv, "13", &v_proc, &v_proto, &v_return, &v_dll); Data_Get_Struct(self, Win32API, ptr); /* Convert a string prototype to an array of characters */ if(rb_respond_to(v_proto, rb_intern("split"))) v_proto = rb_str_split(v_proto, ""); /* Set an arbitrary limit of 16 parameters */ if(16 < RARRAY(v_proto)->len) rb_raise(rb_eArgError, "too many parameters: %d\n", RARRAY(v_proto)->len); /* Convert a nil or empty prototype to 'V' (void) automatically */ if(NIL_P(v_proto) || RARRAY(v_proto)->len == 0){ v_proto = rb_ary_new(); rb_ary_push(v_proto, rb_str_new2("V")); } /* Set the default dll to 'kernel32' */ if(NIL_P(v_dll)) v_dll = rb_str_new2("kernel32"); /* Set the default return type to 'L' (DWORD) */ if(NIL_P(v_return)) v_return = rb_str_new2("L"); SafeStringValue(v_dll); SafeStringValue(v_proc); hLibrary = LoadLibrary(TEXT(RSTRING(v_dll)->ptr)); /* The most likely cause of failure is a bad DLL load path */ if(!hLibrary){ rb_raise(cAPIError, "LoadLibrary() function failed for '%s': %s", RSTRING(v_dll)->ptr, StringError(GetLastError()) ); } ptr->library = hLibrary; /* Attempt to get the function. If it fails, try again with an 'A' * appended. If that fails, try again with a 'W' appended. If that * still fails, raise an API::Error. */ fProc = GetProcAddress(hLibrary, TEXT(RSTRING(v_proc)->ptr)); if(!fProc){ VALUE v_ascii = rb_str_new3(v_proc); v_ascii = rb_str_cat(v_ascii, "A", 1); fProc = GetProcAddress(hLibrary, TEXT(RSTRING(v_ascii)->ptr)); if(!fProc){ VALUE v_unicode = rb_str_new3(v_proc); v_unicode = rb_str_cat(v_unicode, "W", 1); fProc = GetProcAddress(hLibrary, TEXT(RSTRING(v_unicode)->ptr)); if(!fProc){ rb_raise( cAPIError, "GetProcAddress() failed for '%s', '%s' and '%s': %s", RSTRING(v_proc)->ptr, RSTRING(v_ascii)->ptr, RSTRING(v_unicode)->ptr, StringError(GetLastError()) ); } } } ptr->function = fProc; /* Push the numeric prototypes onto our int array for later use. */ for(i = 0; i < RARRAY(v_proto)->len; i++){ SafeStringValue(RARRAY(v_proto)->ptr[i]); switch(*(char*)StringValuePtr(RARRAY(v_proto)->ptr[i])){ case 'L': ptr->prototype[i] = _T_LONG; break; case 'P': ptr->prototype[i] = _T_POINTER; break; case 'I': case 'B': ptr->prototype[i] = _T_INTEGER; break; case 'V': ptr->prototype[i] = _T_VOID; break; case 'K': ptr->prototype[i] = _T_CALLBACK; break; default: rb_raise(cAPIError, "Illegal prototype '%s'", RARRAY(v_proto)->ptr[i]); } } /* Store the return type for later use. Automatically convert empty * strings or nil to type void. */ if(NIL_P(v_return) || RSTRING(v_return)->len == 0){ v_return = rb_str_new2("V"); ptr->return_type = _T_VOID; } else{ SafeStringValue(v_return); switch(*RSTRING(v_return)->ptr){ case 'L': ptr->return_type = _T_LONG; break; case 'P': ptr->return_type = _T_POINTER; break; case 'I': case 'B': ptr->return_type = _T_INTEGER; break; case 'V': ptr->return_type = _T_VOID; break; default: rb_raise(cAPIError, "Illegal prototype '%s'", RARRAY(v_proto)->ptr[i]); } } rb_iv_set(self, "@dll_name", v_dll); rb_iv_set(self, "@function_name", v_proc); rb_iv_set(self, "@prototype", v_proto); rb_iv_set(self, "@return_type", v_return); return self; }