Calling 32-bit Code from 16-bit Apps in Windows 95
  
PSS ID Number: Q125718
Article last modified on 09-25-1995
 
4.00
 
WINDOWS
 

-------------------------------------------------------------------------
The information in this article applies to:
 
 - Microsoft Win32 Application Programming Interface (API) included with:
 
    - Microsoft Windows 95 version 4.0
-------------------------------------------------------------------------
 
SUMMARY
=======
 
It is often desirable to port 16-bit Windows-based applications to Win32 a
little at a time, rather than all at once. For example, you may want to
port Windows-based DLLs to Win32, but still be able to call them from 16-
bit code. This article discusses the mechanism by which Windows-based DLLs
can call Win32-based DLLs. The mechanism is called a thunk and the method
implemented under Windows 95 is called a flat thunk.
 
The three major steps in writing the thunk code are:
 
1. Creating the Thunk Script
2. Building the Windows-based DLL
3. Building the Win32-based DLL
 
MORE INFORMATION
================
 
The recommended way to design a thunk call is to isolate all thunk
specific code in DLLs (a 16-bit DLL and a 32-bit DLL, to provide both sides
of the thunk). That way, you can install certain DLLs on one platform and
replace them on another platform, for portability.
 
Designing a new flat thunk involves creating a thunk script (.THK file).
This script is compiled with the Thunk Compiler into an assembly file. This
file is assembled using two different flags: -DIS_32 and -DIS_16. This
allows you to create both the 16-bit and 32-bit object modules. These
object modules are linked in the Windows-based and Win32-based DLLs,
respectively. The following diagram summarizes the files involves in
building the DLLs.
 
                       +------------+
                       | 16to32.THK |
                       +------------+
                             |
                       +------------+
                       | 16to32.ASM |
                       +------------+
                        /          \
                +-----------+ +-----------+
                | 16THK.OBJ | | 32THK.OBJ |
                +-----------+ +-----------+
                       /                 \
      +-------+    +-------+             +-------+
      | APP16 | -> | DLL16 | -- THUNK -- | DLL32 |
      +-------+    +-------+             +-------+
 
Creating the Thunk Script
-------------------------
 
You need to create a script that will be used by the thunk compiler to
create a thunk. A thunk script contains the function prototype and a
specification for the input and output values. You need to include the
following statement to create a 16-bit to 32-bit thunk call:
 
   enablemapdirect1632 = true
 
By default, the Win32-based DLL is loaded only on the first encounter of a
16->32 thunk. Because late binding is used, 16-bit code must not depend on
any action taken by the initialization of the Win32-based DLL. Also a
loading failure of the Win32-based DLL will not be detected until the first
16->32 thunk has been called. To disable late binding of the Win32-based
DLL add the following line in your thunk script:
 
   preload32=true;
 
The following is an example of a simple thunk script for a function that
has no input and output:
 
   enablemapdirect1632 = true
 
   void MyThunk32()
   {
   }
 
The following is an example of script that takes two parameters and
returns a value. The second parameter is an output parameter and
contains a pointer that is passed back to the Windows-based DLL.
 
   enablemapdirect1632 = true
 
   typedef int   BOOL;
   typedef char *LPSTR;
 
   BOOL MyThunk32(LPSTR lpstrInput, LPSTR lpstrOutput)
   {
      lpstrOutput = output;
   }
 
The statement "lpstrOutput = output" tells the script compiler that
the 32-bit code will return an address that needs to be converted from
flat memory pointer to a selector:offset pointer.
 
The following thunk script uses more complex parameter types, such as
structures. This example also shows how to specify input and output
parameters.
 
   enablemapdirect1632 = true
 
   typedef int BOOL;
   typedef unsigned int UINT;
   typedef char *LPSTR;
 
   typedef struct tagPOINT {
      INT x;
      INT y;
   } POINT;
   typedef POINT *LPPOINT;
 
   typedef struct tagCIRCLE {
      POINT center;
      INT   radius;
   } CIRCLE;
   typedef CIRCLE *LPCIRCLE
 
   void MyThunk32( LPCIRCLE lpCircleInOut)
   {
      lpCircleInOut = InOut;
   }
 
The statement "lpCircleInOut = InOut" tells the script compiler that
this pointer is going to be used for input and output. This means that
conversion from a 16-bit selector:offset to a flat memory pointer and
vice-versa needs to be accomplished.
 
The thunk compiler usage is as follows:
 
   thunk.exe /options <inputfile> -o <outputfile>
 
The following line shows how to create a 16-bit thunk code.
 
   thunk -t thk 16to32.thk -o 16to32.asm
 
The "-t thk" option tells the thunk compiler to prefix the thunk
functions in the assembly language file with "thk_". This will create
an assembly language file.
 
Building the Windows-based DLL (DLL16)
--------------------------------------
 
1. The Windows-based DLL must export a function named "DllEntryPoint". This
   function must make a call to an imported function thk__ThunkConnect16.
 
      BOOL FAR PASCAL __export DllEntryPoint(DWORD dwReason,
                                             WORD  hInst,
                                             WORD  wDS,
                                             WORD  wHeapSize,
                                             DWORD dwReserved1,
                                             WORD  wReserved 2)
      {
         if (!thk_ThunkConnect16("DLL16.DLL",
                                 "DLL32.DLL",
                                 hInst,
                                 dwReason))
         {
            return FALSE;
         }
         return TRUE;
      }
 
2. Include the following lines in the IMPORTS section of the module
   definition (DEF) file for DLL16.
 
      C16ThkSL01      = KERNEL.631
      ThunkConnect16  = KERNEL.651
 
3. Include the following lines in the EXPORTS section of the module
   definition (DEF) file for DLL16. The THK_THUNKDATA16 is defined in the
   object file that is assembled from the output of the thunk compiler.
 
      THK_THUNKDATA16 @1  RESIDENTNAME
      DllEntryPoint   @2  RESIDENTNAME
 
4. Once you have done that you need to assemble the assembly language
   file produced by the thunk compiler as a 16-bit object module. The
   following line shows an example:
 
      ml /DIS_16 /c /W3 /nologo /Fo thk16.obj 16to32.asm
 
5. Link this object module as part of the 16-bit DLL (DLL16) object file.
 
6. Mark the Windows-based DLL as version 4.0. To do this you can use the
   resource compiler (RC.EXE). The following line shows the syntax:
 
      rc -40 <DLL file>
 
   This -40 option is available in the resource compiler that is provided
   with the Windows 95 SDK and later SDKs.
 
   NOTE: Be sure to use the RC.EXE in the BINW16 directory so that the
   application is marked with version 4.0. There is another RC.EXE, but it
   will not mark the application with version 4.0.
 
Building the Win32-based DLL (DLL32)
------------------------------------
 
1. In the DllEntryPoint function (DllMain if you're using the Microsoft
   C Run-time libraries) in your Win32-based DLL, you must make a call
   to the imported function thk_ThunkConnect32, as shown here:
 
      BOOL WINAPI DllMain(HINSTANCE hDLLInst,
                          DWORD fdwReason,
                          LPVOID lpvReserved)
      {
         if (!thk_ThunkConnect32("DLL16.DLL",
                                 "DLL32.DLL",
                                 hDLLInst,
                                 fdwReason))
         {
            return FALSE;
         }
         switch (fdwReason)
         {
            case DLL_PROCESS_ATTACH:
               break;
 
            case DLL_PROCESS_DETACH:
               break;
 
            case DLL_THREAD_ATTACH:
               break;
 
            case DLL_THREAD_DETACH:
               break;
         }
         return TRUE;
      }
 
2. Include the following lines into the EXPORT section of the module
   definition (DEF) file for DLL32:
 
      thk_ThunkData32
 
3. Export the function that you are thunking to.
 
4. Assemble the assembly language file produced by the thunk compiler as a
   32-bit object module. The following line shows an example:
 
      ml /DIS_32 /c /W3 /nologo /coff /Fo thk32.obj 16to32.asm
 
5. Link this object module as part of the Win32-based DLL (Win32).
 
Additional reference words: 4.00 95 flat thunk win16
KBCategory: kbprg
KBSubcategory: SubSys BseMisc
=============================================================================
Copyright Microsoft Corporation 1995.
