Thunking Mechanics
There are differences in pointers, structure packing, and parameter size between the 16-bit and 32-bit portions of the thunk. The thunk compiler must process these differences correctly. In particular, the compiler must check for the following elements in a mixed 16-bit and 32-bit environment:
- Pointers consist of a selector and a 16-bit offset in a 16-bit Windows environment. However, in a 32-bit Windows environment, pointers essentially consist of a 32-bit offset; that is, the DS, ES, SS, and CS registers all contain selectors that have the same base address. All pointers in this environment are considered to be near 0:32 pointers. Translating a 16:16 pointer to a 0:32 pointer involves determining the segment base for the selector portion of the pointer and adding the offset to it. Translating a 0:32 pointer to a 16:16 pointer involves allocating a selector and calculating the offset from the base of the corresponding segment.
- Both 16-bit and 32-bit applications pass function parameters on the stack. However, 16-bit applications address the stack using the SS:SP registers, while 32-bit applications use the SS:ESP registers. When thunking from 16-bit code to 32-bit code (or thunking in the other direction), the processor's method of addressing the stack must be switched.
- The size of an int is 32 bits in a 32-bit environment and 16 bits in a 16-bit environment. When an application uses both 16-bit and 32-bit environments, some piece of software must be able to translate 16-bit integers to 32-bit integers. The following 16-bit function illustrates the translation:
DWORD Sample(int i);
If a 32-bit component calls this 16-bit function, it pushes a 32-bit argument on the stack, but the 16-bit function only pops 16 bits off the stack. Later, when the function returns, it places the returned DWORD value in the DX:AX register pair, but the 32-bit calling application expects the return value to be in the EAX register. It is the thunking layer's responsibility to negotiate these translations.
- To correctly handle packing and conversion, the thunk compiler translates structure members one at a time. The thunk compiler supports structure packing on 1-, 2-, or 4-byte boundaries (corresponding to the /Zp1, /Zp2, and /Zp4 compiler options for Microsoft C/C++ compilers). However, the compiler does not support 8-byte structure alignment. Structure-packing boundaries must be the same on both sides of a thunk.
- Unlike 32-bit code, 16-bit code is usually not reentrant; 16-bit code is typically written with the assumption of a cooperative multitasking model. The thunk layer must serialize access to 16-bit code. In contrast, 32-bit code must execute without serialization to prevent deadlocks. The thunk layer manages serialization on transitions in both directions.