32-bit DLL calling library for Visual Basic 3.0 & 4.0 (16-bit)
by Peter Golde
(last modified by Rob Lichtefeld 01-May-1999)

This program is placed in the public domain. Please feel free to redistribute as you wish. No guarantees are made as to its suitability or usefulness, and no support can be provided [by Peter Golde].

However, since I modified the .dll and pretty well understand how it works I will try to answer questions that arise from
the use of this .dll. Also, due to the price charged for the .dll, I won't promise anything that takes away from my paying job.
I must thank Peter Golde for coming up with the original.

You are welcome to contact me at:

Rob Lichtefeld
Spalding Software, Inc.
333 Research Ct
Suite 220
Norcross, GA 30092 USA

1. Summary

CALL32.DLL is a DLL that can be used for calling routines in 32-bit DLLs on Windows XP, ME, 2000, NT, and 9x.  It may or may not work on other 32-bit operating systems.

Using it, a Visual Basic program, running in the Win16 subsystem, can declare and call functions in any 32-bit DLL (including, but not limited to, the system DLLs).  CALL32.DLL works on both the x86 and MIPS versions on NT.  It has not been tested on Alpha or other versions, but should work.

You should install the CALL32.DLL in the Windows System folder.  This will allow all programs that use it to share the one copy and to make sure that all programs use the most recent version.  As a general rule, if you didn't write a .dll you are distributing it should go into the Windows System or System32 folder.

2. Usage

To call a function in a 32-bit DLL, follow the following steps:

A)    Declare the CALL32 functions as follows (each Declare should be on a single line):

    Declare Function Declare32 Lib "call32.dll" (ByVal Func$, ByVal Library$, ByVal Args$) As Long
    Declare Sub FreeCall32IDs Lib "call32.dll" ()
B)    Next, declare the function(s) you wish to call.  Declare it in the ordinary fashion, with the following exceptions:

   For example, if you are calling the function:

    GetWindowText(HWND hwnd, LPSTR lpsz, int cch)
   declare it as follows (remember that ints and all handles are 32 bits, so use a Long):
    Declare Function GetWindowText Lib "call32.dll" Alias "Call32" (ByVal hwnd As Long, ByVal lpsz As String, ByVal cch As Long, ByVal id As Long) As Long

C)    In the initialization section of your application, you declare the actual library and name of the function you want to call with    the Declare32 function.  Pass it the name of the function, the library, and a string describing the argument types.

   Each letter in the string declares the type of one argument,
   and should be either:
     "i" for a 32 bit integer or handle type,
     "p" for any pointer type, or
     "w" for an HWND parameter you want to pass a 16 bit HWND to and
         have be automatically converted to a 32 bit HWND.

The return value of Declare32 should be saved away in a global variable to be passed as the last parameter to the function you  declared earlier.  So, continue the example, you would call:

   idGetWindowText = Declare32("GetWindowText", "user32", "wpi")
   (As a side note, this would be more properly declared as
   "GetWindowTextA", since this is the real exported name.  However,
   Declare32 will automatically add an "A" to the end of a
   function name if necessary).

D)    To call the function, you would just call:

   cbCopy = GetWindowText(hwnd, sz, cb, idGetWindowText)

E)    In the shutdown section of your application, you should call the FreeCall32IDs subroutine to free the libraries that were loaded by the .DLL for your program.  If you do not call this subroutine, the libraries will not be freed.  This causes the counter for the 32-bit DLLs to never be decremented and thus never unloaded from memory.

3. Data Types and Handles

It is important to use the correct data types when calling DLL functions.  There are two important points to pay attention
to when using CALL32.DLL.  First, only 32 bit integers can be passed to a DLL procedures.  Since virtually all 32 bit
functions take int, UINT, LONG, DWORD, or HANDLE parameters, which are all 32 bits, this is not a major restriction.  However, you must remember to always declare functions arguments are Long, and not Integer.

Secondly, 16 bit handles and 32 bit handles are not interchangable. For example, a 16 bit bitmap handle that you get from calling
a 16 bit DLL or from the VB environment cannot be passed to a 32 bit function expecting a bitmap handle.  Similarly, a
32 bit handle gotten from a 32 bit function cannot be passed to a 16 bit DLL.  The only exception is window handles (HWND).  If
you declare a function parameter with the "w" letter in the argument description string passed to Declare32, the corresponding
parameter will be automatically converted from a 16 bit HWND to a 32 bit HWND when the call is made.  You must still declare the argument as a LONG.  This is convenient, for example, when passing the value returned by the "hWnd" property of a control to a 32 bit DLL function.  Only windows created by your application can be translated.

Summary of data types:
C data type Type specified in Declare Character for Declare32
int, UINT ByVal Long i
LONG, DWORD ByVal Long i
HANDLE ByVal Long i
WORD, short not supported
HWND ByVal Long w (i for no 16->32 translation)
LPSTR ByVal String p
LPWORD Integer p

4. Note on Declare32 function names

Declare32 will automatically try three different names for the function name you pass in.  First, it uses the exact
name you pass in.  If that function name isn't found, it converts the name to the stdcall decorated name convention,
by adding an underscore at the beginning, and adding "@nn" at the end, where "nn" is the number of bytes of arguments.  If
that name isn't found, it adds an "A" to the end of the original name to try the Win32 ANSI function calling convention.

5. Run-time Error Summary

The following run-time errors can be generated by CALL32.DLL

30001   Can't load DLL: "|" (error=|)
   The DLL name passed to Declare32 was not the name of a
   valid 32 bit DLL. The Win32 error code is specified at the
   end of the error message, this can help determine why
   the DLL didn't load.

30002   Can't find specified function
   The function name passed to Declare32 could not be found
   in the DLL.

30003   Invalid parameter definition string
   The parameter definition string passed to Declare32 had
   an invalid character in it, or was too long (32 parameters
   is the limit).

30004   Not running on Windows NT
   The program is not running in the Windows16 subsystem of
   Windows NT.

30005   Invalid window handle
   The 16 bit window handle passed as a parameter declared
   with the 'w' character was not a valid 16 bit window handle,
   or refers to a window from a different process.

6.  Change History

2.02 (1/5/99)
Added some small calls to assist in making the apps appear to the OS as a 32-bit app.

2.00 (9/10/96)
Fixed problem with running more than one VB program calling the  .DLL at one time:

  1. Changed memory allocation to set the GMEM_SHARE flag so that the .DLL "owns" the Global memory instead of the first task.
  2. Added the hTask element to the array to track which IDs went with which VB tasks.
  3. Added the FreeCall32IDs subroutine because errors caused by calling FreeLibrary32W() in the WEP procedure.
  4. Fixed the FreeLibrary32W() call in the Declare32 procedure where it was freeing the handle to Kernel and it should have   been freeing the handle to Kernel32.

1.01 (9/27/93)

  1. Better error message when DLL can't be loaded
  2. Stdcall name decoration support
  3. Source code available
  4. Sample Bezier program from Adam Rauch

1.00 (8/31/93)

  1. Original Version