SUMMARY
Neither Windows NT nor Windows 95 allow direct mixing of 16-bit code and 32-
bit code in the same process. Both platforms support IPC mechanisms, such
as DDE, RPC, OLE, named pipes, and WM_COPYDATA, which you can use for
communication between 16-bit code and 32-bit code. However, there are
occasions when it is necessary to call a function in a Win32-based DLL
(including functions in the system DLLs) from a 16-bit application under
WOW.
Under Windows NT and Windows 95, it is possible to call routines in a Win32
dynamic-link library (DLL) from a 16-bit Windows application using an
interface called Generic Thunking. This is not to be confused with the
Win32s Universal Thunks interface, which provides the same functionality
under Windows 3.1.
Also, Generic Thunks is not to be confused with the Windows 95 Flat Thunks
interface, which provides the same functionality under Windows 95. For more
information on the different types of thunking interfaces on each Win32
platform, please refer to the following Microsoft Knowledge Base article:
125710
PRB: Types of Thunking Available in Win32 Platforms
The Generic Thunking interface consists of functions that allow a 16-bit
application to load the Win32 DLL, get the address of an exported 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), call back into the 16-bit application from the Win32 DLL, and free the
Win32 DLL.
Generic Thunks are documented in the Win32 SDK documentation, under
"Programming and Tools Guides/Programming Techniques."
NOTE: It is a good idea to test the Win32 DLL by calling it from a Win32-
based application before attempting to call it from a 16-bit Windows-based
application, because the debugging support is superior in the 32-bit
environment.
MORE INFORMATION
The basic steps for calling a function through generic thunking are:
- Call LoadLibraryEx32W() to load the Win32 DLL.
- Call GetProcAddress32W() to get the address of the DLL routine.
- Call the DLL routine using CallProc32W() or CallProcEx32W.
CallProc32W() is a Pascal function which was designed to take a variable
number of arguments, a Proc address, a mask, and the number of parameters.
The mask is used to specify which arguments should be treated as being
passed by value and which parameters should be translated from 16:16
pointers to flat pointers. Note that the low-order bit of the mask
represents the last parameter, the next lowest bit represents the next to
the last parameter, and so forth.
The problem with CallProc32W() is that you cannot create a prototype for
it unless you restrict each file so that it only uses calls to functions
that contain the same number of parameters. This is a limitation of the
Pascal calling convention. Windows NT 3.5 and later supports
CallProcEx32W(), which uses the C calling convention to support variable
arguments. However, under Windows 95 there are certain caveats in using the
CallProc32W() and CallProcEx32W() functions. For more information, see the
documentation for these functions.
Sample Code
The following code fragments can be used as a basis for Generic Thunks.
Assume that the 16-bit Windows-based application is named app16, the
Win32 DLL is named dll32, and the following are declared:
typedef void (FAR PASCAL *MYPROC)(LPSTR, HANDLE);
DWORD ghLib;
MYPROC hProc;
char FAR *TestString = "Hello there";
The DLL routine is defined in dll32.c as follows:
void WINAPI MyPrint( LPTSTR lpString, HANDLE hWnd )
{
...
}
Attempt to load the library in the app16 WinMain():
if( NULL == (ghLib = LoadLibraryEx32W( "dll32.dll", NULL, 0 )) ) {
MessageBox( NULL, "Cannot load DLL32", "App16", MB_OK );
return 0;
}
Attempt to get the address of MyPrint():
if( NULL == (hProc = (MYPROC)GetProcAddress32W( ghLib, "MyPrint" ))) {
MessageBox( hWnd, "Cannot call DLL function", "App16", MB_OK );
...
}
Although some of the Generic Thunking functions are called in 16-bit code,
they need to be provided with 32-bit handles, and they return 32-bit
handles. Therefore, before calling CallProcEx32W() and passing it a handle,
you must convert the window handle, hWnd, to a 32-bit window handle,
hWnd32:
hWnd32 = WOWHandle32( hWnd, WOW_TYPE_HWND );
Call MyPrint() and pass it TestString and hWnd32 as arguments:
CallProcEx32W( 2, 2, hProc, (DWORD) TestString, (DWORD) hWnd32 );
Alternatively, you can use CallProc32W() as follows:
CallProc32W( (DWORD) TestString, (DWORD) hWnd32, hProc, 2, 2 );
A mask of 2 (0x10) is given because we want to pass TestString by reference
(WOW translates the pointer) and we want to pass the handle by value.
Free the library right before exiting WinMain():
FreeLibrary32W( ghLib );
NOTE: When linking the Windows-based 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.CallProcEx32W
kernel.CallProc32W
The complete sample is available for download from the Microsoft
Download Center:
For additional information about how to download Microsoft Support files, click the following article number to view the article in the Microsoft Knowledge Base:
119591 How to Obtain Microsoft Support Files from Online Services
Microsoft scanned this file for viruses. Microsoft used the most current virus-detection software that was available on the date that the file was posted. The file is stored on security-enhanced servers that help to prevent any unauthorized changes to the file.
Faults
On MIPS systems, an alignment fault will occur when a Win32-based
application de-references a pointer to unaligned data that was passed by a
16-
bit Windows application. As a workaround, declare the parameter with the
UNALIGNED keyword. For example,
void func( DWORD *var );
becomes
void func( DWORD unaligned *var);
An application can use SetErrorMode() to specify SEM_NOALIGMENTFAULTEXCEPT
flag. If this is done, the system will automatically fix up alignment
faults and make them invisible to the application.
The default value of this error mode is OFF for MIPS, and ON for ALPHA. So
on MIPS platforms, an application MUST call SetErrorMode() and specify
SEM_NOALIGMENTFAULTEXCEPT if it wants the system to automatically fix
alignment faults. This call does not have to be made on ALPHA platforms.
This flag has no effect on x86 systems. Note that the fix above is
preferable.