Handling Data Types Not Supported by the Thunk Compiler

There are several strategies for handling data types and calling conventions that the thunk compiler does not support. These strategies rely on substituting a supported type for the unsupported type and processing the data manually to obtain the original data. The thunk compiler does not support the following data types:

Before considering substitution strategies, it is important to understand how the thunk compiler translates data. The thunk compiler uses the following rules to translate data:

You should use the following general principles for substituting data types and calling conventions:

Handling Floating-Point Data Types

The thunk compiler does not support any floating-point types. There are three floating-point types: float, double, and long double.

A float is 4 bytes long on both the 16-bit and 32-bit sides of a thunk. To pass a float to a thunk function, declare it as a DWORD value in the thunk script. This causes the thunk compiler to pass a 4-byte value without translating its value.

A double is 8 bytes long on both the 16-bit and 32-bit sides of the thunk. To pass a double to a thunk function, declare a structure containing two DWORD values in the thunk script, and pass the structure to the function in the thunk script.

In 16-bit code, a long double is a native 80-bit type of the floating-point processors in the Intel x86 microprocessor family. Because RISC processors do not have a native 80-bit floating point type, 32-bit C and C++ compilers such as Microsoft Visual C++ implement this type as a 64-bit double. If you need additional precision, pass the 80-bit value as a structure consisting of two DWORD members and a WORD member, and handle the value manually on the 32-bit side of the thunk.

Handling Pointer Types

The thunk compiler automatically translates pointers to most types. It properly translates pointers to the following data types:

·    Structures.

The thunk compiler automatically translates pointers. To pass an address without having it translated, use the DWORD type instead of a pointer type. You can use the untranslated address on the target side of the thunk; however, keep in mind that dereferencing this address on the target side of the thunk will cause an access violation.

When a pointer is passed from 32-bit to 16-bit code, a single selector with a limit of 64K is allocated in the thunk. If the 16-bit code needs to access more than the first 64K of the block, it must change the base address of the selector or allocate additional selectors to access the block.

Pointers to pointers are translated partially. The "outside" pointer is translated, but the pointer it points to — the "inside" pointer — is not translated. The thunk compiler does not handle all cases of pointers used in aggregate types, such as arrays of pointers.

Handling Unions

The thunk compiler does not support unions because it cannot determine which type the union will represent at run time. For example, the following union must be treated differently by the thunk compiler depending on which member the application uses when it makes the thunk call:

union
{
    DWORD dwIntegerValue;
    LPSTR szFileName;
}

The problem is that the thunk compiler would have to pass the value unchanged, assuming that the dwIntegerValue member will be used, or translate the value as a pointer, assuming that the szFileName member will be used. Because the thunk compiler cannot make this decision, it cannot generate the correct code to process this union. To handle union types in thunks, declare a structure in the thunk script that is large enough to hold the union, then process the data manually on both sides of the thunk.