SAMPLE: Win16 App (WOW & Win32s) Calling Win32 DLL Code

ID: Q114341

1.30 1.30c | 3.51 4.00

WINDOWS    | WINDOWS NT
kbprg kbref

The information in this article applies to:

SUMMARY

The INTEROP sample demonstrates two general methods for calling routines in a Win32 DLL from a Windows-based application: thunks and SendMessage(). There are two different thunking methods, one for each platform: Generic Thunks on Windows NT and Universal Thunks on Win32s. The message used is WM_COPYDATA, a new message introduced by Windows NT and Win32s. All three methods provide a way to call functions and pass data across the 16-32 boundary, translating any pointers in the process. The advantages of WM_COPYDATA over thunks are that it is fast and the exact same code runs on either platform (the thunk mechanism used will depend on the platform). The disadvantage of WM_COPYDATA is that a method must be devised to get a function return value (other than true or false) back to the calling application.

NOTE: Universal Thunks were designed to work with a Win32-based application calling a 16-bit DLL. The method described here has limitations. Because the application is 16-bit, no 32-bit context is created, so certain calls will not work from the Win32 DLL.

The sample consists of the following source files:

   APP16.C    -  Win16 Application
   DLL16.C    -  16-bit side of Universal Thunk/Generic Thunk
   STUB32.C   -  32-bit stub that loads the 32-bit DLLs on Win32s
   UTDLL32.C  -  32-bit side of the Universal Thunk
   DISP32.C   -  Dispatch calls sent through WM_COPYDATA
   DLL32.C    -  Win32 DLL

This sample is included with the Microsoft Win32 SDK. It is located in the \MSTOOLS\SAMPLES\Q_A\INTEROP directory.

 NOTE: There is also an RPC sample named INTEROP, but it is in a different
directory, (\MSTOOLS\SAMPLES\RPC\INTEROP).

MORE INFORMATION

Generic Thunk

Under Windows NT, it is possible to call routines in a Win32 DLL from a Windows-based application using an interface called Generic Thunks. The Win32 SDK file DOC\SDK\MISC\GENTHUNK.TXT and the Win32 SDK documentation (under the section "Programming and Tools Guides, Programming Techniques, Generic Thunks") describe the interface.

Here is a picture of the way the pieces fit together in INTEROP:

                             dll32
                           ---------
                          |  Win32  |
    32-bit                |   DLL   |
                           ---------
                              /|\ 
               ----------------|-------
                               |
                ---------     ------------
               | Win 3.1 |<->| 16-bit DLL |
    16-bit     |   app.  |   |   (GT)     |
                ---------     ------------
                  app16           dll16

DLL16 is loaded when APP16 is loaded. If it detects that WOW is present, then it loads DLL32.

WOW presents a few new 16-bit application programming interfaces (APIs) that allow you to load the Win32 DLL, get the address of the DLL routine, call the routine (passing it up to thirty-two 32-bit arguments), convert 16:16 (WOW) addresses to 0:32 addresses (useful if you need to build up a 32-bit structure that contains pointers and pass a pointer to it), and free the Win32 DLL. These functions are:

   DWORD FAR PASCAL LoadLibraryEx32W( LPCSTR, DWORD, DWORD );
   DWORD FAR PASCAL GetProcAddress32W( DWORD, LPCSTR );
   DWORD FAR PASCAL CallProc32W( DWORD, ..., LPVOID, DWORD, DWORD );
   DWORD FAR PASCAL GetVDMPointer32W( LPVOID, UINT );
   BOOL FAR PASCAL FreeLibrary32W( DWORD );

When linking the Win16 application, you need to put the following statements in the .DEF file, indicating that the functions will be imported from the WOW kernel:

   IMPORTS
      kernel.LoadLibraryEx32W
      kernel.FreeLibrary32W
      kernel.GetProcAddress32W
      kernel.GetVDMPointer32W
      kernel.CallProc32W

Note that although these functions are called in 16-bit code, they need to be provided with 32-bit handles, and they return 32-bit handles.

In addition, be sure that your Win32 DLL entry point is declared with the _stdcall convention; otherwise, you will get an access violation.

Universal Thunk

Under Win32s, it is possible to call routines in a Win32 DLL from a Win16 application using an interface called Universal Thunks. The interface is described in the Win32s Programmer's Reference. The sample UTSAMPLE, shows the opposite (and more typical) case, a Win32 application calling 16-bit routines.

Here is a picture of the way the pieces fit together in INTEROP:

                         stub           utdll32         dll32
                      -----------     -----------     ---------
                     |   Win32   |-->| Win32 DLL |<->|  Win32  |
    32-bit           |    EXE    |   |    (UT)   |   |   DLL   |
                      -----------     -----------     ---------
                              /|\      /|\ 
             ------------------|--------|-------------------------
                               |       \|/ 
                ---------     ------------
               | Win 3.1 |<->| 16-bit DLL |
    16-bit     |   app.  |   |   (UT)     |
                ---------     ------------
                  app16           dll16

The load order is as follows: The Windows 3.1 application loads the 16-bit DLL. The 16-bit DLL checks to see whether the 32-bit side has been initialized. If it has not been initialized, then the DLL spawns the 32-bit .EXE (stub), which then loads the 32-bit DLL that sets up the Universal Thunks with the 16-bit DLL. Once all of the components are loaded and initialized, when the Windows 3.x application calls an entry point in the 16-bit DLL, the 16-bit DLL uses the 32-bit Universal Thunk callback to pass the data over to the 32-bit side. Once the call has been received on the 32- bit side, the proper Win32 DLL entry point can be called.

WM_COPYDATA

The wParam and lParam for this message are as follows:

   wParam = (WPARAM) (HWND) hwndFrom;   /* handle of sending window */ 
   lParam = (LPARAM) (PCOPYDATASTRUCT) pcds;

Where hwndFrom is the handle of the sending window and COPYDATASTRUCT is defined as follows:

   typedef struct tagCOPYDATASTRUCT {
       DWORD dwData;
       DWORD cbData;
       PVOID lpData;
   } COPYDATASTRUCT;

The INTEROP sample uses dwData as a function code, indicating which Win32 DLL entry point should be calling and lpData to contain a pointer to the data structure to be passed to the function.

Here is a picture of the way the pieces fit together in INTEROP:

                         disp           dll32
                      -----------     ---------
                     |   Win32   |-->|  Win32  |
    32-bit           |    EXE    |   |   DLL   |
                      -----------     ---------
                              /|\ 
             ------------------|----------------------------------
                               |
                ---------     ------------
               | Win 3.1 |<->| 16-bit DLL |
    16-bit     |   app.  |   |  (THUNK)   |
                ---------     ------------
                  app16           dll16

DLL16 is loaded when APP16 is loaded. DISP is spawned to handle WM_COPYDATA messages, regardless of platform. DISP dispatches the calls to DLL32, marshaling the arguments.

Additional reference words: 1.30 1.30c 3.51 4.00 KBCategory: kbprg kbref KBSubcategory: SubSys BseThunks

Keywords          : kbKernBase kbThunks kbWin32s SubSys kbGrpKernBase kbThunk 
Version           : 1.30 1.30c | 3.51 4.00
Platform          : NT WINDOWS

Last Reviewed: April 10, 1997