G:/ScriptBasic/source/extensions/dyc/interface.c

Go to the documentation of this file.
00001 /*
00002 
00003   FILE   : interface.c
00004   HEADER : interface.h
00005   BAS    : dyc.bas
00006   AUTHOR : Peter Verhas
00007 
00008   DATE:    December 23, 2002
00009 
00010   CONTENT:
00011   This is the interface.c file for the ScriptBasic module dyc
00012   
00013   This module makes it possible to call arbitrary C functions implemented
00014   in a DLL that were not developed specifically for ScriptBasic. The module
00015   uses the code
00016   
00017       Dynacall.c - 32-bit Dynamic function calls. Ton Plooy 1998
00018 
00019   included into the interface.c file.
00020 
00021   This module is very much Windows specific and thus can not be compiled
00022   or used under UNIX/Linux or Mac.
00023 
00024 NTLIBS:  
00025 */
00026 #include <windows.h>
00027 
00028 
00029 #define  DC_MICROSOFT           0x0000      // Default
00030 #define  DC_BORLAND             0x0001      // Borland compat
00031 #define  DC_CALL_CDECL          0x0010      // __cdecl
00032 #define  DC_CALL_STD            0x0020      // __stdcall
00033 #define  DC_RETVAL_MATH4        0x0100      // Return value in ST
00034 #define  DC_RETVAL_MATH8        0x0200      // Return value in ST
00035 
00036 #define  DC_CALL_STD_BO         (DC_CALL_STD | DC_BORLAND)
00037 #define  DC_CALL_STD_MS         (DC_CALL_STD | DC_MICROSOFT)
00038 #define  DC_CALL_STD_M8         (DC_CALL_STD | DC_RETVAL_MATH8)
00039 
00040 #define  DC_FLAG_ARGPTR         0x00000002
00041 
00042 #pragma pack(1)                 // Set struct packing to one byte
00043 
00044 typedef union T_RESULT {          // Various result types
00045     int     Int;                // Generic four-byte type
00046     long    Long;               // Four-byte long
00047     void   *Pointer;            // 32-bit pointer
00048     float   Float;              // Four byte real
00049     double  Double;             // 8-byte real
00050     __int64 int64;              // big int (64-bit)
00051 } T_RESULT;
00052 
00053 typedef struct _DYNAPARM {
00054     DWORD       dwFlags;        // Parameter flags
00055     int         nWidth;         // Byte width
00056     union {                     //
00057         DWORD   dwArg;          // 4-byte argument
00058         void   *pArg;           // Pointer to argument
00059     };
00060 } DYNAPARM;
00061 
00062 static
00063 DWORD WINAPI SearchProcAddress(HMODULE hInst, LPSTR szFunction)
00064 {
00065     // Add some simple searching to the GetProcAddress function.
00066     // Various Win32 functions have two versions, a ASCII and
00067     // a Unicode version.
00068     DWORD dwAddr;
00069     char  szName[128];
00070 #pragma warning ( disable : 4311 )
00071     if ((dwAddr = (DWORD)GetProcAddress(hInst, szFunction)) == 0) {
00072         // Function name not found, try some variants
00073         strcpy(szName, szFunction);
00074         strcat(szName, "A");            // ASCII
00075         dwAddr = (DWORD)GetProcAddress(hInst, szName);
00076     }
00077     return dwAddr;
00078 }
00079 
00080 //------------------------------------------------------------------
00081 static
00082 T_RESULT WINAPI DynaCall(int Flags, DWORD lpFunction,
00083                                   int nArgs, DYNAPARM Parm[],
00084                                   LPVOID pRet, int nRetSiz)
00085 {
00086     // Call the specified function with the given parameters. Build a
00087     // proper stack and take care of correct return value processing.
00088     T_RESULT  Res = { 0 };
00089     int     i, nInd, nSize;
00090     DWORD   dwEAX, dwEDX, dwVal, *pStack, dwStSize = 0;
00091     BYTE   *pArg;
00092 
00093     // Reserve 256 bytes of stack space for our arguments
00094     _asm mov pStack, esp
00095     _asm sub esp, 0x100
00096 
00097     // Push args onto the stack. Every argument is aligned on a
00098     // 4-byte boundary. We start at the rightmost argument.
00099     for (i = 0; i < nArgs; i++) {
00100         nInd  = (nArgs - 1) - i;
00101         // Start at the back of the arg ptr, aligned on a DWORD
00102         nSize = (Parm[nInd].nWidth + 3) / 4 * 4;
00103         pArg  = (BYTE *)Parm[nInd].pArg + nSize - 4;
00104         dwStSize += (DWORD)nSize; // Count no of bytes on stack
00105         while (nSize > 0) {
00106             // Copy argument to the stack
00107             if (Parm[nInd].dwFlags & DC_FLAG_ARGPTR) {
00108                 // Arg has a ptr to a variable that has the arg
00109                 dwVal = *(DWORD *)pArg; // Get first four bytes
00110                 pArg -= 4;              // Next part of argument
00111             }
00112             else {
00113                 // Arg has the real arg
00114                 dwVal = Parm[nInd].dwArg;
00115             }
00116             // Do push dwVal
00117             pStack--;           // ESP = ESP - 4
00118             *pStack = dwVal;    // SS:[ESP] = dwVal
00119             nSize -= 4;
00120         }
00121     }
00122     if ((pRet != NULL) && ((Flags & DC_BORLAND) || (nRetSiz > 8))) {
00123         // Return value isn't passed through registers, memory copy
00124         // is performed instead. Pass the pointer as hidden arg.
00125         dwStSize += 4;          // Add stack size
00126         pStack--;               // ESP = ESP - 4
00127         *pStack = (DWORD)pRet;  // SS:[ESP] = pMem
00128     }
00129 
00130     _asm add esp, 0x100         // Restore to original position
00131     _asm sub esp, dwStSize      // Adjust for our new parameters
00132 
00133     // Stack is now properly built, we can call the function
00134     _asm call [lpFunction]
00135 
00136     _asm mov dwEAX, eax         // Save eax/edx registers
00137     _asm mov dwEDX, edx         //
00138 
00139     // Possibly adjust stack and read return values.
00140     if (Flags & DC_CALL_CDECL) {
00141         _asm add esp, dwStSize
00142     }
00143     if (Flags & DC_RETVAL_MATH4) {
00144         _asm fstp dword ptr [Res]
00145     }
00146     else if (Flags & DC_RETVAL_MATH8) {
00147         _asm fstp qword ptr [Res]
00148     }
00149     else if (pRet == NULL) {
00150         _asm mov  eax, [dwEAX]
00151         _asm mov  DWORD PTR [Res], eax
00152         _asm mov  edx, [dwEDX]
00153         _asm mov  DWORD PTR [Res + 4], edx
00154     }
00155     else if (((Flags & DC_BORLAND) == 0) && (nRetSiz <= 8)) {
00156         // Microsoft optimized less than 8-bytes structure passing
00157         _asm mov ecx, DWORD PTR [pRet]
00158         _asm mov eax, [dwEAX]
00159         _asm mov DWORD PTR [ecx], eax
00160         _asm mov edx, [dwEDX]
00161         _asm mov DWORD PTR [ecx + 4], edx
00162     }
00163     return Res;
00164 }
00165 
00166 #include <stdio.h>
00167 #include "../../basext.h"
00168 
00169 
00170 /*
00171 TO_BAS:
00172 */
00173 
00174 typedef struct _DllLoaded {
00175   HMODULE hDll;  /* habdle to the loaded DLL or NULL if loading was unsuccessful */
00176   char *pszName; /* the name of the loaded library as it was specified by the program */
00177   struct _DllLoaded *next; /*pointer to the next on the list or NULL if the last */
00178   } DllLoaded, *pDllLoaded;
00179 
00180 typedef struct _ModuleObject {
00181   pDllLoaded pDllList;
00182   }ModuleObject,*pModuleObject;
00183 
00184 besVERSION_NEGOTIATE
00185   return (int)INTERFACE_VERSION;
00186 besEND
00187 
00188 /*
00189 *TODO*
00190 ALTER THE ERROR MESSAGE FUNCTION
00191 */
00192 besSUB_ERRMSG
00193 
00194   switch( iError ){
00195     case 0x00080000: return "ERROR HAS HAPPENED";
00196     }
00197   return "Unknown dyc module error.";
00198 besEND
00199 
00200 
00201 besSUB_START
00202   pModuleObject p;
00203 
00204   besMODULEPOINTER = besALLOC(sizeof(ModuleObject));
00205   if( besMODULEPOINTER == NULL )return COMMAND_ERROR_MEMORY_LOW;
00206   p = besMODULEPOINTER;
00207   p->pDllList = NULL; /* no Dlls are loaded (by the module) when the module starts */
00208   return COMMAND_ERROR_SUCCESS;
00209 besEND
00210 
00211 besSUB_FINISH
00212   pModuleObject p;
00213   pDllLoaded pDll;
00214   
00215   p = (pModuleObject)besMODULEPOINTER;
00216   if( p == NULL )return 0;
00217   
00218   /* free the libraries that were loaded by teh module */
00219   pDll = p->pDllList;
00220   while( pDll ){
00221     FreeLibrary(pDll->hDll);
00222     pDll = pDll->next;
00223     }
00224   return 0;
00225 besEND
00226 
00227 /* This function loads the requested DLL safely. This means that the DLL will
00228    only be loaded calling LoadLibrary only if it was not loaded yet. In other
00229    cases the function returns without calling the system function. Thus a DLL
00230    will only be loaded only once by the module.
00231    
00232    The function returns TRUE if the module was loaded successfully and FALSE
00233    otherwise.
00234    
00235    The function can be called using the macro bLibraryLoaded(x) without the
00236    special arguments.
00237    
00238    The function can be called from within any besFUNCTION function. The arguments
00239    passed to this function allow the function to call the bes macros from
00240    within this function especially to call the memory allocation functions.
00241    
00242    Note that this function does not check if a library is specified with
00243    different names. For example if the library is named 'simple.dll' and also
00244    'c:\\winnt\\system32\\simple.dll' the function will load the dll twice.
00245    
00246 */
00247 #define LibraryLoaded(pszName) F_LibraryLoaded(pSt,ppModuleInternal,pszName)
00248 static HMODULE F_LibraryLoaded(pSupportTable pSt,
00249                             void **ppModuleInternal,
00250                             char *pszName){
00251   pDllLoaded pDll,pNew;
00252   pModuleObject p = (pModuleObject)besMODULEPOINTER;
00253 
00254   pDll = p->pDllList;
00255 
00256   while( pDll ){
00257     if( ! stricmp(pszName,pDll->pszName) )break;
00258     pDll = pDll->next;
00259     }
00260   if( pDll )
00261     return pDll->hDll;
00262 
00263   pNew = (pDllLoaded)besALLOC(sizeof(DllLoaded));
00264   /* If there is no memory then the DLL can not be loaded. The function
00265      design does not allow any more specific error handling. */
00266   if( NULL == pNew )return NULL;
00267   
00268   /* allocate space for the name of the library to be loaded */
00269   pNew->pszName = besALLOC(strlen(pszName)+1);
00270   if( NULL == pNew->pszName ){
00271     besFREE(pNew);
00272     return NULL;
00273     }
00274   /* copy the name of the library into the structure thus it can be checked
00275      against library names to avoid double loading */
00276   strcpy(pNew->pszName,pszName);
00277   
00278   /* link the new structure to the head of the list */
00279   pNew->next = p->pDllList;
00280   p->pDllList = pNew;
00281   pNew->hDll = LoadLibrary(pNew->pszName);
00282   return pNew->hDll;
00283   }
00284 
00438 besFUNCTION(dyc)
00439   pModuleObject p;
00440   VARIABLE Argument;
00441   int Flags;
00442   DWORD lpFunction;
00443   int nArgs;
00444   DYNAPARM *Parm;
00445   int nRetSiz;
00446   char *pszFormat;
00447   DWORD cbFormat;
00448   int iParseState;
00449   char cRet;
00450   char szDll[MAX_PATH];
00451   char szFunction[MAX_PATH];
00452   char *pszS;
00453   int i;
00454   HMODULE hDll;
00455   T_RESULT RetVal;
00456   float fTmp;
00457 
00458   p = (pModuleObject)besMODULEPOINTER;
00459   besRETURNVALUE = NULL;
00460 
00461   Argument = besARGUMENT(1);
00462   besDEREFERENCE(Argument);
00463 
00464   /* if argument is undef then raise the error */
00465   if( Argument == NULL )return COMMAND_ERROR_FEW_ARGS;
00466   /* make sure that the format argument is a string */
00467   Argument = besCONVERT2STRING(Argument);
00468   pszFormat = STRINGVALUE(Argument);
00469   cbFormat = STRLEN(Argument);
00470   if( cbFormat < 1 )return COMMAND_ERROR_ARGUMENT_TYPE;
00471   iParseState = 0;
00472   Flags = 0;
00473   while( cbFormat ){
00474     switch( iParseState ){
00475       case 0: //define the call type
00476         switch( *pszFormat ){
00477           case 'm': case 'M': Flags |= DC_MICROSOFT;    break;
00478           case 'b': case 'B': Flags |= DC_BORLAND;      break;
00479           case 'c': case 'C': Flags |= DC_CALL_CDECL;   break;
00480           case 's': case 'S': Flags |= DC_CALL_STD;     break;
00481           case '4':           Flags |= DC_RETVAL_MATH4; break;
00482           case '8':           Flags |= DC_RETVAL_MATH8; break;
00483           case ',' : iParseState++; break;
00484           }
00485         cbFormat --;
00486         pszFormat++;
00487         break;
00488       case 1: // define the return value using a single character
00489         if( cbFormat < 1 )return COMMAND_ERROR_ARGUMENT_TYPE;
00490         cRet = tolower(*pszFormat);
00491         switch( cRet ){
00492           case 'I': case 'i': nRetSiz = sizeof(int);    break;
00493           case 'L': case 'l': nRetSiz = sizeof(long);   break;
00494           case 'P': case 'p': nRetSiz = sizeof(void *); break;
00495           case 'F': case 'f': nRetSiz = sizeof(float);  break;
00496           case 'D': case 'd': nRetSiz = sizeof(double); break;
00497           case 'V': case 'v': nRetSiz = sizeof(__int64);break;
00498           default: return COMMAND_ERROR_ARGUMENT_RANGE;
00499           }
00500         cbFormat --;
00501         pszFormat++;
00502         if( cbFormat < 1 )return COMMAND_ERROR_ARGUMENT_TYPE;
00503         if( *pszFormat != ',' )return COMMAND_ERROR_ARGUMENT_RANGE;
00504         cbFormat --;
00505         pszFormat++;
00506         iParseState++;
00507         break;
00508       case 2: // define the name of the dll
00509       case 3: //define the name of the function
00510         if( iParseState == 2 )pszS = szDll; else pszS = szFunction;
00511         i = 0;
00512         while( cbFormat && *pszFormat != ',' ){
00513           pszS[i] = *pszFormat;
00514           pszFormat ++;
00515           i++;
00516           cbFormat --;
00517           if( i >= MAX_PATH )return COMMAND_ERROR_ARGUMENT_RANGE;
00518           }
00519         if( cbFormat < 1 )return COMMAND_ERROR_ARGUMENT_RANGE;
00520         cbFormat--;
00521         pszFormat++;
00522         pszS[i] = (char)0;
00523         iParseState++;
00524         break;
00525       case 4: // define the arguments
00526         //first of all get sure that the library loads
00527         if( NULL == (hDll = LibraryLoaded(szDll)) )return COMMAND_ERROR_MODULE_NOT_LOADED;
00528         // the rest of the format string should define the arguments, each character
00529         // should specify exactly one argument, thus the number of characters left
00530         // should give exactly the number of arguments
00531         nArgs = cbFormat;
00532         Parm = besALLOC(nArgs*sizeof(DYNAPARM));
00533         if( NULL == Parm )return COMMAND_ERROR_MEMORY_LOW;
00534         i = 0; // count the arguments
00535         while( cbFormat ){
00536           if( i <= besARGNR-1 ){
00537             Argument = besARGUMENT(i+2);
00538             besDEREFERENCE(Argument);
00539             }else{
00540             Argument = NULL;
00541             }
00542           switch( *pszFormat ){
00543             case '1': // just any 1-byte argument
00544               Parm[i].nWidth  = 1;
00545               Parm[i].dwFlags = 0;
00546               if( NULL == Argument ){
00547                 Parm[i].dwArg = 0;
00548                 }else
00549                 switch( TYPE(Argument) ){
00550                   case VTYPE_STRING:
00551                     if( STRLEN(Argument) < 1 )
00552                       Parm[i].dwArg = 0;
00553                     else
00554                       memcpy(&(Parm[i].dwArg),STRINGVALUE(Argument),1);
00555                     break;
00556                   case VTYPE_UNDEF:
00557                   case VTYPE_LONG:
00558                   case VTYPE_DOUBLE: Parm[i].dwArg = 0xFF & (unsigned long)besGETLONGVALUE(Argument); break;
00559                   default: return COMMAND_ERROR_ARGUMENT_RANGE;
00560                   }
00561             case '2': // just any 2-byte argument
00562               Parm[i].nWidth  = 2;
00563               Parm[i].dwFlags = 0;
00564               if( NULL == Argument ){
00565                 Parm[i].dwArg = 0;
00566                 }else
00567                 switch( TYPE(Argument) ){
00568                   case VTYPE_STRING:
00569                     if( STRLEN(Argument) < 2 )
00570                       Parm[i].dwArg = 0;
00571                     else
00572                       memcpy(&(Parm[i].dwArg),STRINGVALUE(Argument),2);
00573                     break;
00574                   case VTYPE_UNDEF:
00575                   case VTYPE_LONG:
00576                   case VTYPE_DOUBLE: Parm[i].dwArg = 0xFFFF & (unsigned long)besGETLONGVALUE(Argument); break;
00577                   default: return COMMAND_ERROR_ARGUMENT_RANGE;
00578                   }
00579             case '4': // just any 4-byte argument
00580               Parm[i].nWidth  = 4;
00581               Parm[i].dwFlags = 0;
00582               if( NULL == Argument ){
00583                 Parm[i].dwArg = 0;
00584                 }else
00585                 switch( TYPE(Argument) ){
00586                   case VTYPE_STRING:
00587                     if( STRLEN(Argument) < 4 )
00588                       Parm[i].dwArg = 0;
00589                     else
00590                       memcpy(&(Parm[i].dwArg),STRINGVALUE(Argument),4);
00591                       break;
00592                   case VTYPE_UNDEF:
00593                   case VTYPE_LONG:
00594                   case VTYPE_DOUBLE: Parm[i].dwArg = (long)besGETLONGVALUE(Argument); break;
00595                   default: return COMMAND_ERROR_ARGUMENT_RANGE;
00596                   }
00597             case '8':
00598               Argument = besCONVERT2STRING(Argument);
00599               if( STRLEN(Argument) < 8 )return COMMAND_ERROR_ARGUMENT_RANGE;
00600               Parm[i].nWidth  = 8;
00601               Parm[i].dwFlags = DC_FLAG_ARGPTR;
00602               Parm[i].pArg = STRINGVALUE(Argument);
00603               break;
00604 
00605             case 'c': case 'C': //char
00606               Parm[i].nWidth  = 1;
00607               Parm[i].dwFlags = 0;
00608               if( NULL == Argument ){
00609                 Parm[i].dwArg = 0;
00610                 }else
00611                 switch( TYPE(Argument) ){
00612                   case VTYPE_STRING:
00613                     if( STRLEN(Argument) < 1 )
00614                       Parm[i].dwArg = 0;
00615                     else
00616                       Parm[i].dwArg = *STRINGVALUE(Argument);
00617                     break;
00618                   case VTYPE_UNDEF:
00619                   case VTYPE_LONG:
00620                   case VTYPE_DOUBLE: Parm[i].dwArg = (char)besGETLONGVALUE(Argument); break;
00621                   default: return COMMAND_ERROR_ARGUMENT_RANGE;
00622                   }
00623             case 's': case 'S': //short
00624               Parm[i].nWidth  = 2;
00625               Parm[i].dwFlags = 0;
00626               Parm[i].dwArg = (short)besGETLONGVALUE(Argument); break;
00627               break;
00628 
00629             case 'f': case 'F': //float
00630               Parm[i].nWidth  = 4;
00631               Parm[i].dwFlags = 0;
00632               fTmp = (float)besGETDOUBLEVALUE(Argument);
00633               memcpy(&(Parm[i].dwArg) , &fTmp, sizeof(float)) ; break;
00634               break;
00635               
00636             case 'h': case 'H': //handle
00637             case 'p': case 'P': //pointer
00638             case 'l': case 'L': //long
00639               Parm[i].nWidth  = 4;
00640               Parm[i].dwFlags = 0;
00641               Parm[i].dwArg = (DWORD)besGETLONGVALUE(Argument); break;
00642               break;
00643 
00644             case 'z': case 'Z': //ZCHAR
00645               Parm[i].nWidth  = 4;
00646               Parm[i].dwFlags = 0;
00647               Argument = besCONVERT2STRING(Argument);
00648               Parm[i].pArg = STRINGVALUE(Argument); break;
00649               break;
00650 
00651             case 'd': case 'D': //double
00652               Argument = besCONVERT2DOUBLE(Argument);
00653               Parm[i].nWidth  = 8;
00654               Parm[i].dwFlags = DC_FLAG_ARGPTR;
00655               Parm[i].pArg = & (DOUBLEVALUE(Argument));
00656               break;
00657             default: return COMMAND_ERROR_ARGUMENT_RANGE;
00658             }
00659           i++;
00660           cbFormat--;
00661           pszFormat++;
00662           }
00663         break;
00664       }
00665     }
00666   lpFunction = (DWORD)SearchProcAddress(hDll,szFunction);
00667   if( lpFunction == 0 )return COMMAND_ERROR_MODULE_FUNCTION;
00668   RetVal = DynaCall(Flags,lpFunction,nArgs,Parm,NULL,0);
00669   switch( cRet ){
00670     case 'i':
00671       besALLOC_RETURN_LONG;
00672       LONGVALUE(besRETURNVALUE) = (long)RetVal.Int;
00673       break;
00674     case 'l':
00675       besALLOC_RETURN_LONG;
00676       LONGVALUE(besRETURNVALUE) = RetVal.Long;
00677       break;
00678     case 'p':
00679       besALLOC_RETURN_LONG;
00680       LONGVALUE(besRETURNVALUE) = (long)RetVal.Pointer;
00681       break;
00682     case 'f':
00683       besALLOC_RETURN_DOUBLE;
00684       DOUBLEVALUE(besRETURNVALUE) = (double)RetVal.Float;
00685       break;
00686     case 'd':
00687       besALLOC_RETURN_DOUBLE;
00688       DOUBLEVALUE(besRETURNVALUE) = (double)RetVal.Double;
00689       break;
00690     case 'v':
00691       besALLOC_RETURN_STRING(8);
00692       memcpy(STRINGVALUE(besRETURNVALUE),&(RetVal.int64),8);
00693       break;
00694     }
00695 
00696   return COMMAND_ERROR_SUCCESS;
00697 besEND
00698 
00699 
00700 SLFST DYC_SLFST[] ={
00701 
00702 { "versmodu" , versmodu },
00703 { "bootmodu" , bootmodu },
00704 { "finimodu" , finimodu },
00705 { "emsgmodu" , emsgmodu },
00706 { "dyc" , dyc },
00707 { NULL , NULL }
00708   };

Generated on Sun Mar 12 23:56:28 2006 for ScriptBasic by  doxygen 1.4.6-NO