/* Win32API - Ruby Win32 API Import Facility */ #if !defined _MSC_VER && !defined _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #endif #define _T_VOID 0 #define _T_NUMBER 1 #define _T_POINTER 2 #define _T_INTEGER 3 #include "ruby.h" /**** unused ? typedef struct { HANDLE dll; HANDLE proc; VALUE dllname; VALUE import; VALUE export; } Win32API; ****/ static VALUE hash_lasterr; static void Win32API_FreeLibrary(hdll) HINSTANCE hdll; { FreeLibrary(hdll); } static VALUE Win32API_lasterr_get() /* Win32API.lasterr -- retrieve stored error code from previous call in same thread */ { return rb_hash_aref(hash_lasterr, rb_thread_current()); } static VALUE Win32API_lasterr_set(self, val) /* Win32API.lasterr = val -- simulate error */ VALUE self; VALUE val; { rb_hash_aset(hash_lasterr, rb_thread_current(), val); return val; } static VALUE Win32API_message(argc, argv, self) /* Win32API.message(errnum) -- get text for specific system error number Win32API.message -- get text for Win32API.lasterr */ int argc; VALUE* argv; VALUE self; { VALUE arg, msg; long len, fmerr; LPVOID lpMsgBuf; rb_scan_args(argc, argv, "01", &arg); if (NIL_P(arg)) { arg = Win32API_lasterr_get(); if (NIL_P(arg)) return rb_str_new2("** Not set **"); } len = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, 0, NUM2LONG(arg), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, 0 ); if (len > 0) { msg = rb_str_new(lpMsgBuf, len); } else { fmerr = GetLastError(); if (fmerr != ERROR_MR_MID_NOT_FOUND) rb_warning("** Win32API: Code %ld from 'FormatMessage' **", fmerr); msg = rb_str_new2("** No message for code "); rb_str_concat(msg, rb_fix2str(arg, /*base*/10)); rb_str_cat2(msg, " **"); } LocalFree(lpMsgBuf); return msg; } static VALUE Win32API_initialize(self, dllname, proc, import, export) VALUE self; VALUE dllname; VALUE proc; VALUE import; VALUE export; { HANDLE hproc; HINSTANCE hdll; VALUE str; VALUE a_import; char *s; int i; int len; int ex = _T_VOID; SafeStringValue(dllname); SafeStringValue(proc); hdll = LoadLibrary(RSTRING(dllname)->ptr); if (!hdll) rb_raise(rb_eRuntimeError, "LoadLibrary: %s\n", RSTRING(dllname)->ptr); rb_iv_set(self, "__hdll__", Data_Wrap_Struct(rb_cData, 0, Win32API_FreeLibrary, hdll)); rb_iv_set(self, "__gle__", (strcmp(RSTRING(proc)->ptr, "GetLastError") == 0) ? Qtrue : Qfalse); hproc = GetProcAddress(hdll, RSTRING(proc)->ptr); if (!hproc) { str = rb_str_new3(proc); str = rb_str_cat(str, "A", 1); hproc = GetProcAddress(hdll, RSTRING(str)->ptr); if (!hproc) rb_raise(rb_eRuntimeError, "GetProcAddress: %s or %s\n", RSTRING(proc)->ptr, RSTRING(str)->ptr); } rb_iv_set(self, "__dll__", UINT2NUM((unsigned long)hdll)); rb_iv_set(self, "__dllname__", dllname); rb_iv_set(self, "__proc__", UINT2NUM((unsigned long)hproc)); a_import = rb_ary_new(); switch (TYPE(import)) { case T_NIL: break; case T_ARRAY: import = rb_ary_join(import, Qnil); /* drop through */ default: SafeStringValue(import); s = RSTRING(import)->ptr; for (i = 0, len = RSTRING(import)->len; i < len; i++) { switch (*s++) { case 'N': case 'n': case 'L': case 'l': rb_ary_push(a_import, INT2FIX(_T_NUMBER)); break; case 'P': case 'p': rb_ary_push(a_import, INT2FIX(_T_POINTER)); break; case 'I': case 'i': rb_ary_push(a_import, INT2FIX(_T_INTEGER)); break; default: /* rb_raise(rb_eArgError, "unknown type '%c'", *(s-1)); */ rb_warn("unknown type '%c'", *(s-1)); break; } } break; } if (16 < RARRAY(a_import)->len) { rb_raise(rb_eRuntimeError, "too many parameters: %d\n", RARRAY(a_import)->len); } rb_iv_set(self, "__import__", a_import); if (NIL_P(export) || RSTRING(export)->len == 0) { ex = _T_VOID; } else { SafeStringValue(export); switch (*RSTRING(export)->ptr) { case 'V': case 'v': ex = _T_VOID; break; case 'N': case 'n': case 'L': case 'l': ex = _T_NUMBER; break; case 'P': case 'p': ex = _T_POINTER; break; case 'I': case 'i': ex = _T_INTEGER; break; default: /* rb_raise(rb_eArgError, "unknown type '%c'", *RSTRING(export)->ptr); */ rb_warn("unknown type '%c'", *RSTRING(export)->ptr); break; } } rb_iv_set(self, "__export__", INT2FIX(ex)); return Qnil; } static VALUE Win32API_Call(argc, argv, obj) int argc; VALUE *argv; VALUE obj; { VALUE args; unsigned long ret; int i; struct { unsigned long params[16]; } param; #define params param.params VALUE obj_proc = rb_iv_get(obj, "__proc__"); VALUE obj_import = rb_iv_get(obj, "__import__"); VALUE obj_export = rb_iv_get(obj, "__export__"); FARPROC ApiFunction = (FARPROC)NUM2ULONG(obj_proc); int items = rb_scan_args(argc, argv, "0*", &args); int nimport = RARRAY(obj_import)->len; if (RTEST(rb_iv_get(obj, "__gle__"))) { rb_warn("Win32API.lasterr() returns accurate 'GetLastError' code"); rb_iv_set(obj, "__gle__", Qfalse); /* warn once */ /* rb_raise(rb_eRuntimeError, "please replace calls to GetLastError"); ?future? */ } if (items != nimport) rb_raise(rb_eRuntimeError, "Wrong number of parameters: expected %d, got %d.\n", nimport, items); for (i = 0; i < nimport; i++) { unsigned long lParam = 0; switch (FIX2INT(rb_ary_entry(obj_import, i))) { VALUE str; case _T_NUMBER: case _T_INTEGER: default: lParam = NUM2ULONG(rb_ary_entry(args, i)); break; case _T_POINTER: str = rb_ary_entry(args, i); if (NIL_P(str)) { lParam = 0; } else if (FIXNUM_P(str)) { lParam = NUM2ULONG(str); } else { StringValue(str); rb_str_modify(str); lParam = (unsigned long)StringValuePtr(str); } break; } params[i] = lParam; } ret = ApiFunction(param); rb_hash_aset(hash_lasterr, rb_thread_current(), INT2NUM(GetLastError())); /* keyed on thread "id" */ switch (FIX2INT(obj_export)) { case _T_NUMBER: case _T_INTEGER: return INT2NUM(ret); case _T_POINTER: return rb_str_new2((char *)ret); case _T_VOID: default: return INT2NUM(0); } } void Init_Win32API() { VALUE cWin32API = rb_define_class("Win32API", rb_cObject); rb_define_method(cWin32API, "initialize", Win32API_initialize, 4); rb_define_method(cWin32API, "call", Win32API_Call, -1); rb_define_alias(cWin32API, "Call", "call"); rb_define_singleton_method(cWin32API, "message", Win32API_message, -1); rb_define_singleton_method(cWin32API, "lasterr", Win32API_lasterr_get, 0); rb_define_singleton_method(cWin32API, "lasterr=", Win32API_lasterr_set, 1); hash_lasterr = rb_hash_new(); rb_global_variable(&hash_lasterr); rb_cvar_set(cWin32API, rb_intern("@@__lasterr__"), hash_lasterr, Qtrue); /**********/ fprintf(stderr, "** Win32API (+lasterr / message)\n"); /**********/ }