diff options
Diffstat (limited to 'jim-win32api.c')
-rw-r--r-- | jim-win32api.c | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/jim-win32api.c b/jim-win32api.c new file mode 100644 index 0000000..1f24559 --- /dev/null +++ b/jim-win32api.c @@ -0,0 +1,448 @@ +/* WIN32 API extension + * + * Copyright (C) 2005 Pat Thoyts <patthoyts@users.sourceforge.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * A copy of the license is also included in the source distribution + * of Jim, as a TXT file name called LICENSE. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <shellapi.h> +#include <lmcons.h> + +#include <stdio.h> + +#define JIM_EXTENSION +#include "jim.h" + +#if _MSC_VER >= 1000 +#pragma comment(lib, "shell32") +#pragma comment(lib, "user32") +#pragma comment(lib, "advapi32") +#endif /* _MSC_VER >= 1000 */ + +static const char win32api_type_hash[] = "win32api:typemap"; + +__declspec(dllexport) int Jim_OnLoad(Jim_Interp *interp); + +/* ---------------------------------------------------------------------- + * Convert Win32 error codes into an error message string object. + */ + +static Jim_Obj * +Win32ErrorObj(Jim_Interp *interp, const char * szPrefix, DWORD dwError) +{ + Jim_Obj *msgObj = NULL; + char * lpBuffer = NULL; + DWORD dwLen = 0; + + dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, LANG_NEUTRAL, + (char *)&lpBuffer, 0, NULL); + if (dwLen < 1) { + dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, + "code 0x%1!08X!%n", 0, LANG_NEUTRAL, + (char *)&lpBuffer, 0, (va_list *)&dwError); + } + + msgObj = Jim_NewStringObj(interp, szPrefix, -1); + if (dwLen > 0) { + char *p = lpBuffer + dwLen - 1; /* remove cr-lf at end */ + for ( ; p && *p && isspace(*p); p--) + ; + *++p = 0; + Jim_AppendString(interp, msgObj, ": ", 2); + Jim_AppendString(interp, msgObj, lpBuffer, -1); + } + LocalFree((HLOCAL)lpBuffer); + return msgObj; +} + +/* ---------------------------------------------------------------------- + * Information recorded for API calls that we can call. + */ + +typedef struct Win32ApiDeclaration { + HMODULE module; /* Reference counted handle to the library */ + LPSTR symbol; /* The function name as used in the library */ + FARPROC lpfn; /* Generic function pointer for the API */ + LPSTR rtype; /* The return type */ + Jim_Obj *typeList; /* List of parameters as type name type ... */ +} Win32ApiDeclaration; + + +/* ---------------------------------------------------------------------- + * Type hash table + * + * This is generic string:thing* hash. + * Keys are a typename. Values are pointers Win32ApiTypeInfo structs + */ + +typedef struct Win32ApiTypeInfo { + size_t type_size; /* The size of the type in bytes (not used yet) */ + char type_spec[]; /* packing details for the type */ +} Win32ApiTypeInfo; + +static unsigned int Win32ApiTypeInfoHashTableHash(const void *key) +{ + /*return Jim_DjbHashFunction(key, strlen(key));*/ + unsigned int h = 5381; + size_t len = strlen(key); + while(len--) + h = (h + (h << 5)) ^ *((const char *)key)++; + return h; +} + +static const void *Win32ApiTypeInfoHashTableCopyKey(void *privdata, const void *key) +{ + JIM_NOTUSED(privdata); + return Jim_StrDup(key); +} + +static int Win32ApiTypeInfoHashTableCompare(void *privdata, const void *key1, const void *key2) +{ + JIM_NOTUSED(privdata); + return strcmp(key1, key2) == 0; +} + +static void Win32ApiTypeInfoHashTableDestroyKey(void *privdata, const void *key) +{ + JIM_NOTUSED(privdata); + Jim_Free((void*)key); +} + +static void Win32ApiTypeInfoHashTableDestroyValue(void *interp, void *val) +{ + Win32ApiTypeInfo *entryPtr = (Win32ApiTypeInfo *)val; + JIM_NOTUSED(interp); + Jim_Free((void*)entryPtr); +} + +static Jim_HashTableType Win32ApiTypeHashTableType = { + Win32ApiTypeInfoHashTableHash, /* hash function */ + Win32ApiTypeInfoHashTableCopyKey, /* key dup */ + NULL, /* val dup */ + Win32ApiTypeInfoHashTableCompare, /* key compare */ + Win32ApiTypeInfoHashTableDestroyKey, /* key destructor */ + Win32ApiTypeInfoHashTableDestroyValue /* val destructor */ +}; + +/* ---------------------------------------------------------------------- + * The typedef object type is a caching internal rep to avoid repeat + * lookups into the hash table. + */ + +Jim_ObjType typedefObjType = { + "win32.typedef", + NULL, + NULL, + NULL, + JIM_TYPE_REFERENCES, +}; + +int +TypedefSetFromAny(Jim_Interp *interp, Jim_Obj *objPtr) +{ + if (objPtr->typePtr != &typedefObjType) { + const char *tname = Jim_GetString(objPtr, NULL); + Jim_HashTable *hashPtr = Jim_GetAssocData(interp, win32api_type_hash); + Jim_HashEntry *entryPtr = Jim_FindHashEntry(hashPtr, tname); + if (entryPtr == NULL) { + Jim_Obj *errObj = Jim_NewEmptyStringObj(interp); + Jim_AppendStrings(interp, errObj, "type \"", tname, "\" is not defined", NULL); + Jim_SetResult(interp, errObj); + return JIM_ERR; + } + + Jim_FreeIntRep(interp, objPtr); + objPtr->internalRep.ptr = entryPtr; + objPtr->typePtr = &typedefObjType; + } + return JIM_OK; +} + +/* ---------------------------------------------------------------------- */ + +/* Clean up the package's interp associated data */ +static void +Win32_PackageDeleteProc(Jim_Interp *interp, void *clientData) +{ + Jim_HashTable *hashPtr = (Jim_HashTable *)clientData; + Jim_FreeHashTable(hashPtr); + Jim_Free(clientData); +} + +/* Update and introspect the type table + * + * Perls pack supports... + a char binary data (null padded) u uuencoded string + A cha binary data (space padded) U unicode char + Z asciiz + c char s int16 i int l int32 n be u_int16 v le uint16 q int64 f float p pointer (null term) + C uchar S u_int16 I u_int L uint_32 N be u_int32 V le uint32 Q u_int64 d double P block pointer +*/ +static int +Win32_Typedef(Jim_Interp *interp, int objc, Jim_Obj *const objv[]) +{ + enum {OPT_NULL, OPT_TYPE_NAMES, OPT_TYPE_GET, OPT_TYPE_SET}; + Jim_HashTable *hashPtr = (Jim_HashTable *)Jim_CmdPrivData(interp); + Win32ApiTypeInfo *typePtr; + const char *name = NULL, *spec = NULL; + int name_len, spec_len, n, r = JIM_OK; + struct def { char c; unsigned char n; } defs[] = { + 'c', 1, 'C', 1, 's', 2, 'S', 2, 'i', 4, 'I', 4, + 'l', 4, 'L', 4, 'n', 4, 'N', 4, 'v', 4, 'V', 4, + 'q', 8, 'Q', 8, 'f', 4, 'd', 8, 'p', 4, 'P', 4, + 'a', 1, 'A', 1, 'Z', 0, 'U', 2, 0, 0}; + struct def *sp; + const char *p; + size_t size, m = 0; + + if (objc > 3) { + Jim_WrongNumArgs(interp, 1, objv, "typename ?pack_spec?"); + return JIM_ERR; + } + + switch (objc) { + /* Assign a new type. We validate the pack format and calculate the type size at the same time */ + case OPT_TYPE_SET: { + name = Jim_GetString(objv[1], &name_len); + spec = Jim_GetString(objv[2], &spec_len); + + size = 0; + p = spec; + while (p && *p) { + for (sp = defs; sp->c != 0; sp++) { + if (sp->c == *p) + break; + } + if (sp->c == 0) { + Jim_Obj *errObj = Jim_NewStringObj(interp, "invalid pack character \"0\"", -1); + errObj->bytes[24] = *p; /* NOTE: if you change the text in the above string this must be fixed */ + Jim_AppendStrings(interp, errObj, " while defining type \"", name, "\"", NULL); + Jim_SetResult(interp, errObj); + return JIM_ERR; + } + if (isdigit(*(p+1))) { + char *pe = NULL; + m = strtol(p+1, &pe, 0); + p = pe; + } else { + m = 1; + p++; + } + size += (sp->n * m); + } + + n = name_len + spec_len + 1; + typePtr = (Win32ApiTypeInfo *)Jim_Alloc(n); + typePtr->type_size = size; + strcpy(typePtr->type_spec, spec); + Jim_AddHashEntry(hashPtr, name, typePtr); + Jim_SetResultString(interp, name, name_len); + break; + } + case OPT_TYPE_GET: { + Jim_HashEntry *entryPtr; + name = Jim_GetString(objv[1], &name_len); + if ((entryPtr = Jim_FindHashEntry(hashPtr, name)) == NULL) { + Jim_Obj *resObj = Jim_NewEmptyStringObj(interp); + Jim_AppendStrings(interp, resObj, "type \"", name, "\" not found", NULL); + Jim_SetResult(interp, resObj); + r = JIM_ERR; + } else { + typePtr = entryPtr->val; + Jim_SetResultString(interp, typePtr->type_spec, -1); + } + break; + } + case OPT_TYPE_NAMES: { + Jim_Obj *listPtr; + Jim_HashEntry *entryPtr; + Jim_HashTableIterator *it = Jim_GetHashTableIterator(hashPtr); + listPtr = Jim_NewListObj(interp, NULL, 0); + while ((entryPtr = Jim_NextHashEntry(it)) != NULL) { + Jim_ListAppendElement(interp, listPtr, Jim_NewStringObj(interp, entryPtr->key, -1)); + } + Jim_SetResult(interp, listPtr); + break; + } + } + return r; +} + +/* ---------------------------------------------------------------------- */ + + +static void +Win32_ApiCleanup(Jim_Interp *interp, void *clientData) +{ + Win32ApiDeclaration *declPtr = (Win32ApiDeclaration *)clientData; + FreeLibrary(declPtr->module); + Jim_Free((void *)declPtr->symbol); + Jim_Free((void *)declPtr->rtype); + Jim_DecrRefCount(interp, declPtr->typeList); +} + +static int +Win32_ApiHandler(Jim_Interp *interp, int objc, Jim_Obj *const objv[]) +{ + Win32ApiDeclaration *declPtr = (Win32ApiDeclaration *)Jim_CmdPrivData(interp); + Jim_HashTable *hashPtr; + int nargs = 0, n, np, r; + long lval; + double dblval; + float fltval; + struct { + unsigned long params[16]; + } param; + + Jim_ListLength(interp, declPtr->typeList, &nargs); + if (objc-1 != nargs/2) { + int tlen = 0; + const char *types = Jim_GetString(declPtr->typeList, &tlen); + char *sz = (char *)_alloca(tlen + 3); + sprintf(sz, "(%s)", types); + Jim_WrongNumArgs(interp, 1, objv, sz); + return JIM_ERR; + } + + ZeroMemory(¶m, sizeof(param)); + hashPtr = Jim_GetAssocData(interp, win32api_type_hash); + + for (n = 1, np = 0; n < objc; n++) { + Jim_HashEntry *entryPtr; + Jim_Obj *tnameObj, *pnameObj; + const char *tname, *pname; + + r = Jim_ListIndex(interp, declPtr->typeList, (np*2), &tnameObj, JIM_ERRMSG); + tname = Jim_GetString(tnameObj, NULL); + r = Jim_ListIndex(interp, declPtr->typeList, (np*2)+1, &pnameObj, JIM_ERRMSG); + pname = Jim_GetString(pnameObj, NULL); + + entryPtr = Jim_FindHashEntry(hashPtr, tname); + switch (((Win32ApiTypeInfo *)entryPtr->val)->type_spec[0]) { + case 'q': case 'Q': + Jim_GetWide(interp, objv[n], (jim_wide *)¶m.params[np]); + np += 2; + break; + case 's': case 'v': + Jim_GetLong(interp, objv[n], &lval); + param.params[np] = (unsigned short)lval; + np++; + break; + case 'd': + Jim_GetDouble(interp, objv[n], &dblval); + memcpy(¶m.params[np], &dblval, 8); + np+=2; + break; + case 'f': + Jim_GetDouble(interp, objv[n], &dblval); + fltval = (float)dblval; + memcpy(¶m.params[np], &fltval, 4); + np+=2; + break; + case 'l': case 'L': case 'i': case 'I': case 'V': + default: + Jim_GetLong(interp, objv[n], ¶m.params[np]); + np++; + break; + } + } + + if (nargs == 0) + r = declPtr->lpfn(); + else + r = declPtr->lpfn(param); + + Jim_SetResult(interp, Jim_NewIntObj(interp, r)); + return JIM_OK; +} + +static int +Win32_Declare(Jim_Interp *interp, int objc, Jim_Obj *const objv[]) +{ + Jim_HashTable *hashPtr = (Jim_HashTable *)Jim_CmdPrivData(interp); /* type hash map */ + Win32ApiDeclaration *declPtr; + HMODULE hLib = NULL; + FARPROC lpfn = NULL; + const char *lib, *rtype, *name; + Jim_Obj *cmdObj = NULL; + + if (objc != 5) { + Jim_WrongNumArgs(interp, 1, objv, "lib return_type name typelist"); + return JIM_ERR; + } + + lib = Jim_GetString(objv[1], NULL); + rtype = Jim_GetString(objv[2], NULL); + name = Jim_GetString(objv[3], NULL); + hLib = LoadLibraryA(lib); + if (hLib == NULL) { + Jim_SetResultString(interp, "failed to load library", -1); + return JIM_ERR; + } + + if ((lpfn = GetProcAddress(hLib, name)) == NULL) { + Jim_Obj *errObj = Jim_NewEmptyStringObj(interp); + FreeLibrary(hLib); + Jim_AppendStrings(interp, errObj, "could not load \"", name, "\" from \"", lib, "\"", NULL); + Jim_SetResult(interp, errObj); + return JIM_ERR; + } + + declPtr = (Win32ApiDeclaration *)Jim_Alloc(sizeof(Win32ApiDeclaration)); + declPtr->module = hLib; + declPtr->lpfn = lpfn; + declPtr->symbol = Jim_StrDup(name); + declPtr->rtype = Jim_StrDup(rtype); + declPtr->typeList = Jim_DuplicateObj(interp, objv[4]); + Jim_IncrRefCount(declPtr->typeList); + + cmdObj = Jim_NewStringObj(interp, "", strlen(name) + 9); + sprintf(cmdObj->bytes, "win32api.%s", name); + Jim_CreateCommand(interp, cmdObj->bytes, Win32_ApiHandler, declPtr, Win32_ApiCleanup); + Jim_SetResult(interp, cmdObj); + return JIM_OK; +} + +/* ---------------------------------------------------------------------- */ + +int +Jim_OnLoad(Jim_Interp *interp) +{ + Jim_HashTable *hashPtr; + Jim_InitExtension(interp); + if (Jim_PackageProvide(interp, "win32api", "1.0", JIM_ERRMSG) != JIM_OK) + return JIM_ERR; + + hashPtr = (Jim_HashTable *)Jim_Alloc(sizeof(Jim_HashTable)); + Jim_InitHashTable(hashPtr, &Win32ApiTypeHashTableType, NULL); + Jim_SetAssocData(interp, win32api_type_hash, Win32_PackageDeleteProc, hashPtr); + + Jim_CreateCommand(interp, "win32.declare", Win32_Declare, hashPtr, NULL); + Jim_CreateCommand(interp, "win32.typedef", Win32_Typedef, hashPtr, NULL); + + return JIM_OK; +} + +/* ---------------------------------------------------------------------- + * Local variables: + * indent-tabs-mode: nil + * End: + */ |