home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic 5 Developer's Kit / vb5 dev kit.iso / dev / call32 / source / call32.c next >
Encoding:
C/C++ Source or Header  |  1996-09-10  |  13.8 KB  |  394 lines

  1. //----------------------------------------------------------
  2. // CALL32.C
  3. //
  4. // This creates a DLL for 16-bit Visual Basic programs to
  5. // call 32-bit DLLs on Windows NT 3.1.  It uses the 
  6. // Generic Thunks feature of the WOW subsystem on Windows
  7. // NT and 95 to load and call 32 bit DLLs.  This file should
  8. // be compiled into a 16-bit DLL.
  9. //
  10. // Written by Peter Golde
  11. //     Version 1.01 - 18-Feb-1994
  12. //     Version 2.00 - 10-Sep-1996   modified by Rob Lichtefeld
  13. //----------------------------------------------------------
  14.  
  15. #include <windows.h>
  16. #include <windowsx.h>
  17. #include "vbapi.h"
  18.  
  19. // Disable some warnings that won't go away.
  20. #pragma warning(disable: 4704 4785)
  21.  
  22. // Error codes we return.
  23. #define ERR_CANTLOADLIBRARY  30001
  24. #define ERR_CANTFINDFUNCTION 30002
  25. #define ERR_INVALIDPARMSTRING 30003 
  26. #define ERR_NOTNT 30004 
  27. #define ERR_INVALIDHWND 30005
  28. #define ERR_OOM  7
  29.  
  30. // Test for the error messages.
  31. char * szCantLoadLibrary = "Can't load DLL: \"%s\"\n(error=%d)";
  32. char * szCantFindFunction = "Can't find specified function";
  33. char * szInvalidParmString = "Invalid parameter definition string";
  34. // new
  35. char * szNotNT = "Not running on a 32-bit version of Windows";
  36. char * szInvalidHwnd = "Invalid window handle";
  37.  
  38. // This structure describes a function which has been 
  39. // registered with us.
  40. typedef struct {
  41.   DWORD hinst;        // 32-bit instance handle of library
  42.   LPVOID lpfunc;      // 32-bit function address of function
  43.   DWORD  dwAddrXlat;   // bit mask of params: 1 indicates arg is address
  44.   DWORD  dwHwndXlat;   // bit mask of params: 1 indicates arg is 16-bit hwnd
  45.   DWORD  nParams;      // number of parameters
  46.   // --- new
  47.   HANDLE hTask;        // task handle of 16-bit calling program
  48. } PROC32ENTRY;
  49.   
  50. // rgProc32Entry points to an array of PROC32ENTRY functions, which
  51. // is grown as needed.  The value returned by Declare32 is an
  52. // index into this array.
  53. int cRegistered = 0;  // number of registered functions.
  54. int cAlloc = 0;       // number of alloced PROC32ENTRY structures.
  55. PROC32ENTRY FAR * rgProc32Entry = 0;  // array of PROC32ENTRY structures.
  56. #define CALLOCGROW 10 // number of entries to grow rgProc32Entry by
  57.  
  58. // These are the addresses of the Generic Thunk functions in 
  59. // the WOW KERNEL.  
  60. BOOL fGotProcs = FALSE;    // Did we successfully get the addresses?
  61. DWORD (FAR PASCAL *CallProc32W)() = 0;
  62. BOOL (FAR PASCAL *FreeLibrary32W)(DWORD) = 0;
  63. LPVOID (FAR PASCAL *GetProcAddress32W)(DWORD, LPCSTR) = 0;
  64. DWORD (FAR PASCAL *LoadLibraryEx32W)(LPSTR, DWORD, DWORD) = 0;
  65. LPVOID lpvGetLastError = 0;   // address of 32-bit GetLastError.
  66.  
  67. //-----------------------------------------------------
  68. // XlatHwnd
  69. //   Translates a 16-bit HWND into a 32-bit HWND.
  70. //   The HWND must be one in our 16-bit process.
  71. //   NULL is translated to NULL and doesn't cause
  72. //   and error.
  73. //
  74. //   Unfortunately, WOW does not export a function (in Kernel/Kernel32)
  75. //   for doing this, so our procedure is as follows:
  76. //   We do 16-bit SetCapture call to the window
  77. //   to set the capture, and then a 32-bit GetCapture
  78. //   call to get the 32-bit equivalent handle.  The
  79. //   capture is then restored to what it was beforehand.
  80. //
  81. //   May cause VB runtime error, and hence never return.
  82. //-----------------------------------------------------
  83. // --- original 
  84. //static void PASCAL NEAR XlatHwnd
  85. // --- new
  86. void _export PASCAL XlatHwnd
  87. (
  88.   DWORD FAR * phwnd   // Points to 16-bit HWND, on return
  89.                       // points to 32-bit HWND.
  90. )
  91. {
  92.   HWND hwnd16 = LOWORD(*phwnd);  // 16-bit hwnd
  93.   HWND hwndCapturePrev;          // window that has the capture
  94.   DWORD hwnd32;                  // 32-bit hwnd
  95.   static LPVOID lpvGetCapture;   // Address of 32-bit GetCapture
  96.  
  97.   // Check for valid 16-bit handle.   
  98.   if (*phwnd != (DWORD)(LONG)(SHORT)hwnd16) 
  99.     goto BadHwnd;
  100.   if (hwnd16 != NULL && !IsWindow(hwnd16))
  101.     goto BadHwnd;
  102.   
  103.   // Get Address of 32-bit GetCapture
  104.   if (! lpvGetCapture) {
  105.     DWORD hinstUser = LoadLibraryEx32W("user32", 0, 0);
  106.     if (hinstUser) {
  107.       lpvGetCapture = GetProcAddress32W(hinstUser, "GetCapture");
  108.       FreeLibrary32W(hinstUser);
  109.     }
  110.     if (!lpvGetCapture) {
  111.       VBSetErrorMessage(ERR_NOTNT, szNotNT);
  112.       VBRuntimeError(ERR_NOTNT);
  113.     }
  114.   }
  115.   
  116.   // Set capture to window, get capture to get 32-bit handle. 
  117.   // Be sure to restore capture afterward.
  118.   // NULL isn't translated
  119.   if (hwnd16) {
  120.     hwndCapturePrev = SetCapture(hwnd16);
  121.     hwnd32 = ((DWORD (FAR PASCAL *)(LPVOID, DWORD, DWORD)) CallProc32W) (lpvGetCapture, (DWORD)0, (DWORD)0);
  122.     if (hwndCapturePrev)
  123.       SetCapture(hwndCapturePrev);
  124.     else
  125.       ReleaseCapture();
  126.     if (!hwnd32) 
  127.       goto BadHwnd;
  128.   }    
  129.  
  130.   // Success - done.    
  131.   *phwnd = hwnd32;
  132.   return;  
  133.  
  134. BadHwnd:
  135.   // Error: couldn't translate HWND or bad HWND passed.
  136.   VBSetErrorMessage(ERR_INVALIDHWND, szInvalidHwnd);
  137.   VBRuntimeError(ERR_INVALIDHWND);
  138. }
  139.  
  140.   
  141. //-----------------------------------------------------
  142. // MungeArgs
  143. //   Modify the args array so it can be passed to
  144. //   to CallProc32W.  This uses the PROC32ENTRY structure
  145. //   to set up the arg list correctly on the stack
  146. //   so CallProc32W can be call.  HWND translation is
  147. //   performed.  The frame is changed as follows:
  148. //           In:                 Out:
  149. //            unused              number of params
  150. //   dwArgs-> unused              address xlat mask
  151. //            PROC32ENTRY index   32-bit function address.
  152. //            argument            argument, possible HWND xlated
  153. //            argument            argument, possible HWND xlated
  154. //            ...                 ...
  155. //-----------------------------------------------------
  156. void PASCAL NEAR _loadds MungeArgs
  157. (
  158.   LPDWORD dwArgs           // Points to arg list
  159. )
  160. {
  161.   PROC32ENTRY FAR * pentry = & rgProc32Entry[dwArgs[1]];
  162.   int iArg = 2;
  163.   DWORD dwHwndXlat;
  164.   
  165.   dwArgs[-1] = pentry->nParams;
  166.   dwArgs[0] = pentry->dwAddrXlat;
  167.   dwArgs[1] = (DWORD) pentry->lpfunc;
  168.   dwHwndXlat = pentry->dwHwndXlat;
  169.   while (dwHwndXlat) {
  170.     if (dwHwndXlat & 1) 
  171.       XlatHwnd(& dwArgs[iArg]);
  172.     ++iArg;
  173.     dwHwndXlat >>= 1;
  174.   }  
  175.  
  176. //-----------------------------------------------------
  177. // Call32
  178. //   This function is called by VB applications directly.
  179. //   Arguments to the function are also on the stack 
  180. //   (iProc is the PROC32ENTRY index).  We correctly
  181. //   set up the stack frame, then JUMP to CallProc32W,
  182. //   which eventually returns to the user.
  183. //-----------------------------------------------------
  184. void _export PASCAL Call32(long iProc)
  185. {
  186.   __asm {
  187.   
  188.   pop     cx              // dx = callers DS
  189.   pop     bp              // restore BP
  190.   
  191.   mov     bx, sp          // bx = sp on entry
  192.   sub     sp, 8           // 2 additional words
  193.   mov     ax, ss:[bx]     // ax = return address offst
  194.   mov     dx, ss:[bx+2]   // dx = return address segment
  195.   mov     ss:[bx-8], ax
  196.   mov     ss:[bx-6], dx
  197.   push    ds              // Save our DS
  198.   push    ss
  199.   push    bx              // Push pointer to args
  200.   mov     ds, cx          // Restore caller's DS
  201.   call    MungeArgs       // Munge the args
  202.   pop     es              // es is our DS
  203.   jmp    far ptr es:[CallProc32W] // Jump to the call thunker
  204.   }
  205. }  
  206.  
  207. //-----------------------------------------------------
  208. // Declare32
  209. //   This function is called directly from VB.
  210. //   It allocates and fills in a PROC32ENTRY structure
  211. //   so that we can call the 32 bit function.
  212. //-----------------------------------------------------
  213. long _export PASCAL Declare32
  214. (
  215.   LPSTR lpstrName,       // function name
  216.   LPSTR lpstrLib,        // function library
  217.   LPSTR lpstrArg         // string indicating arg types
  218. )
  219. {
  220.   DWORD hinst;           // 32-bit DLL instance handle
  221.   LPVOID lpfunc;         // 32-bit function pointer
  222.   DWORD dwAddrXlat;      // address xlat mask
  223.   DWORD dwHwndXlat;      // hwnd xlat mask
  224.   DWORD nParams;         // number of params
  225.   CHAR szBuffer[128];    // scratch buffer
  226.  
  227.   // First time called, get the addresses of the Generic Thunk
  228.   // functions.  Raise VB runtime error if can't (probably because
  229.   // we're not running on NT).  
  230.   if (!fGotProcs) {
  231.     HINSTANCE hinstKernel;    // Instance handle of WOW KERNEL.DLL
  232.     DWORD hinstKernel32;      // Instance handle of Win32 KERNEL32.DLL
  233.     
  234.     hinstKernel = LoadLibrary("KERNEL");
  235.     if (hinstKernel < HINSTANCE_ERROR) {
  236.       VBSetErrorMessage(ERR_NOTNT, szNotNT);
  237.       VBRuntimeError(ERR_NOTNT);
  238.     }
  239.     
  240.     CallProc32W = (DWORD (FAR PASCAL *)()) GetProcAddress(hinstKernel, "CALLPROC32W");
  241.     FreeLibrary32W = (BOOL (FAR PASCAL *)(DWORD)) GetProcAddress(hinstKernel, "FREELIBRARY32W");
  242.     LoadLibraryEx32W =  (DWORD (FAR PASCAL *)(LPSTR, DWORD, DWORD))GetProcAddress(hinstKernel, "LOADLIBRARYEX32W");
  243.     GetProcAddress32W = (LPVOID (FAR PASCAL *)(DWORD, LPCSTR))  GetProcAddress(hinstKernel, "GETPROCADDRESS32W");
  244.     FreeLibrary(hinstKernel);
  245.  
  246.     if (LoadLibraryEx32W && GetProcAddress32W && FreeLibrary32W) {
  247.       hinstKernel32 = LoadLibraryEx32W("kernel32", 0, 0);
  248.       lpvGetLastError = GetProcAddress32W(hinstKernel32, "GetLastError");
  249.       FreeLibrary32W(hinstKernel32);
  250.     }
  251.     
  252.     if (!CallProc32W || !FreeLibrary32W || !LoadLibraryEx32W || 
  253.         !GetProcAddress32W || !lpvGetLastError) {
  254.       VBSetErrorMessage(ERR_NOTNT, szNotNT);
  255.       VBRuntimeError(ERR_NOTNT);
  256.     }
  257.     
  258.     fGotProcs = TRUE;
  259.   }  
  260.  
  261.   // If needed, allocate a PROC32ENTRY structure
  262.   if (cRegistered == cAlloc) {
  263.     if (rgProc32Entry) 
  264.       rgProc32Entry = (PROC32ENTRY FAR *) GlobalReAllocPtr(rgProc32Entry, 
  265.                                           (cAlloc + CALLOCGROW) * sizeof(PROC32ENTRY), GMEM_MOVEABLE | GMEM_SHARE);
  266.     else                                          
  267.       rgProc32Entry = (PROC32ENTRY FAR *) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE, CALLOCGROW * sizeof(PROC32ENTRY));
  268.     if (!rgProc32Entry) {
  269.       VBRuntimeError(ERR_OOM);
  270.     }
  271.     cAlloc += CALLOCGROW;
  272.   }  
  273.   
  274.   // Process the arg list descriptor string to 
  275.   // get the hwnd and addr translation masks, and the
  276.   // number of args.
  277.   dwAddrXlat = dwHwndXlat = 0;
  278.   if ((nParams = lstrlen(lpstrArg)) > 32) {
  279.     VBSetErrorMessage(ERR_INVALIDPARMSTRING, szInvalidParmString);
  280.     VBRuntimeError(ERR_INVALIDPARMSTRING);
  281.   }
  282.   while (*lpstrArg) {
  283.     dwAddrXlat <<= 1;
  284.     dwHwndXlat <<= 1;
  285.     switch (*lpstrArg) {
  286.     case 'p':
  287.       dwAddrXlat |= 1;
  288.       break;
  289.     case 'i':
  290.       break;
  291.     case 'w':
  292.       dwHwndXlat |= 1;
  293.       break;
  294.     default:
  295.       VBSetErrorMessage(ERR_INVALIDPARMSTRING, szInvalidParmString);
  296.       VBRuntimeError(ERR_INVALIDPARMSTRING);
  297.     }
  298.     ++lpstrArg;
  299.   }
  300.   
  301.   // Load the 32-bit library.  
  302.   hinst = LoadLibraryEx32W(lpstrLib, NULL, 0);
  303.   if (!hinst) {
  304.     DWORD errCode = 0;
  305.  
  306.     // Get NT error code (szBuffer is convenient scratch buffer)    
  307.     errCode = ((DWORD (FAR PASCAL *)(LPVOID, DWORD, DWORD)) CallProc32W) (lpvGetLastError, (DWORD)0, (DWORD)0);
  308.     wsprintf(szBuffer, szCantLoadLibrary, lpstrLib, errCode);
  309.     VBSetErrorMessage(ERR_CANTLOADLIBRARY, szBuffer);
  310.     VBRuntimeError(ERR_CANTLOADLIBRARY);
  311.   }
  312.   
  313.   // Get the 32-bit function address.  Try the following three
  314.   // variations of the name (example: NAME):
  315.   //    NAME
  316.   //    _NAME@nn     (stdcall naming convention: nn is bytes of args)
  317.   //    NAMEA        (Win32 ANSI function naming convention)
  318.   lpfunc = GetProcAddress32W(hinst, lpstrName); 
  319.   if (!lpfunc && lstrlen(lpstrName) < 122) {
  320.     // Change to stdcall naming convention.
  321.     wsprintf(szBuffer, "_%s@%d", lpstrName, nParams * 4);
  322.     lpfunc = GetProcAddress32W(hinst, szBuffer);
  323.   }  
  324.   if (!lpfunc && lstrlen(lpstrName) < 126) {
  325.     // Add suffix "A" for ansi
  326.     lstrcpy(szBuffer, lpstrName);
  327.     lstrcat(szBuffer, "A");
  328.     lpfunc = GetProcAddress32W(hinst, szBuffer);
  329.   }  
  330.   if (!lpfunc) {
  331.     FreeLibrary32W(hinst);
  332.     VBSetErrorMessage(ERR_CANTFINDFUNCTION, szCantFindFunction);
  333.     VBRuntimeError(ERR_CANTFINDFUNCTION);
  334.   }
  335.   
  336.   // Fill in PROC32ENTRY struct and return index.     
  337.   rgProc32Entry[cRegistered].hinst = hinst;
  338.   rgProc32Entry[cRegistered].lpfunc = lpfunc;
  339.   rgProc32Entry[cRegistered].dwAddrXlat = dwAddrXlat;
  340.   rgProc32Entry[cRegistered].dwHwndXlat = dwHwndXlat;
  341.   rgProc32Entry[cRegistered].nParams = nParams;
  342.   // --- new by RAL
  343.   rgProc32Entry[cRegistered].hTask = GetCurrentTask();
  344.   return cRegistered++;
  345. }
  346.  
  347. // --- new procedures
  348. //-----------------------------------------------------
  349. // FreeCall32IDs
  350. //   This function is called directly from VB.
  351. //   It frees all the libraries that were loaded in the 
  352. //   Declare32 function for this Task Handle
  353. //
  354. // Writted by Rob Lichtefeld
  355. //-----------------------------------------------------
  356. void _export PASCAL FreeCall32IDs ()
  357. {
  358.    int iProc = cRegistered;
  359.    HANDLE ThisTask = GetCurrentTask();
  360.    while (--iProc >= 0) {
  361.       if (rgProc32Entry[iProc].hTask == ThisTask) {
  362.          if (rgProc32Entry[iProc].hinst != NULL) {
  363.             FreeLibrary32W(rgProc32Entry[iProc].hinst);
  364.             rgProc32Entry[iProc].hinst = NULL;
  365.          }
  366.          rgProc32Entry[iProc].hTask = NULL;
  367.       }
  368.    }
  369. }
  370.  
  371. //-----------------------------------------------------
  372. // WEP
  373. //   Called when DLL is unloaded.  
  374. //   clears the PROC32ENTRY list and frees the memory
  375. //-----------------------------------------------------
  376. int FAR PASCAL _export WEP(int nExitType)
  377. {
  378.   // --- removed from .DLL
  379.   // --- better to never free the libraries than to cause a Win95
  380.   // --- exception error
  381.   //while (--cRegistered >= 0) {
  382.   //  if (rgProc32Entry[cRegistered].hinst != NULL) {
  383.   //     FreeLibrary32W(rgProc32Entry[cRegistered].hinst);
  384.   //  }
  385.   //}
  386.   // --- end of removed section
  387.   if (rgProc32Entry)  
  388.     GlobalFreePtr(rgProc32Entry);
  389.   rgProc32Entry = NULL;
  390.   cRegistered = cAlloc = 0;
  391.   return 1;
  392. }
  393.