The information in this article applies to:
SUMMARYThe thunk compiler supports most of the built-in and user-defined types that the C or C++ languages support. It also supports a subset of the calling conventions that the Microsoft Visual C++ compiler supports. To handle situations where you need to pass or return data of an unsupported type, you need to massage the data into a type that the thunk compiler does recognize. This article explains how to do so for commonly used types supported by the C and C++ languages but not by the thunk compiler. MORE INFORMATION
There are several strategies for handling data types and function calling
conventions that the thunk compiler does not support. These strategies
generally rely on substitution and pre- or post-processing the data, and
require an understanding of how the thunk compiler translates data. The
thunk compiler uses the following rules to translate data:
Before delving into how to handle specific data types, you need to consider the general principles for substituting function calling conventions and data types. These are simple:
Function Calling ConventionsThe Windows 95 thunk compiler supports a subset of the calling conventions supported by the Microsoft Visual C++ compiler. Only __stdcall is supported on the 32-bit side of a thunk; on the 16-bit side, only __pascal is supported. __cdecl and __fastcall are not supported calling conventions for either the 16-bit or 32-bit sides of the thunk. Although the thunk must use __pascal and __stdcall calling conventions, the thunk may call or be called by other functions that use any calling convention.To thunk to a function that uses the __cdecl or __fastcall calling convention, you must create your thunk function with the supported calling conventions and have the target side of the thunk call the real function. Here is an example that shows how a Win32 application might call a 16-bit __cdecl function:
If you try to build a thunk and don't use the __stdcall or __pascal calling conventions, you will get "unresolved external" errors from the linker when building because the function names produced by the C/C++ compiler don't match the names in the code produced by the thunk compiler. Return ValuesThe thunk compiler does not support all types as return values that the C and C++ languages support. The limitations stem from the problems of translating addresses from 32-bit address spaces to 16-bit address spaces and vice-versa.The thunk compiler supports returning all integral types. The signed and unsigned varieties of char, short int, and long int are the same size on both sides of a thunk and thus are returned unmodified. The values of the int type will be truncated to 16-bits upon return from a 16->32 thunk, and will be sign-extended to 32-bits on return from a 32->16 thunk. Structures may not be returned from either 32->16 or 16->32 thunks. However, pointers to structures can be returned from 32->16 thunks if the structure does not need to be repacked and does not contain pointers. A structure does not require repacking if the types of all members of the structure are the same size in 16-bit and 32-bit code and the structure is packed with the same alignment on both sides of the thunk. Use #pragma pack to pack the structure with the same value in the 32-bit and 16-bit source code, and specify the same packing on the thunk compiler command line. Pointers cannot be returned from 16->32 thunks because the context of the 32-bit address space is not global. The 32-bit address space for the Win32 target DLL is mapped between 4 megabytes and 2 gigabytes, and it is context- switched as are all other Win32 process address spaces. This means that a Win16 application or DLL could not use a pointer returned by a 16->32 thunk without causing a general protection (GP) fault. You can work around this limitation by writing a wrapper function for the 16-bit side of the thunk that allocates memory and passes it to the thunk function as an extra parameter. The 32-bit side of the thunk should also be a wrapper around the real function to handle the extra parameter and copy the data to the memory allocated by the 16-bit wrapper function. Pointers are supported as return types in 32->16 thunks as long as the base type requires the same number of bytes on both sides of the thunk and does not require repacking. If the size of the base type of a pointer differs between the Win16 and Win32 sides, the thunk compiler generates an error. For example, a char is one byte on both sides of the thunk, and thus a pointer to a char is supported as a return type. A pointer to an int cannot be returned because an int is 16 bits on the 16-bit side of the thunk, but 32 bits on the 32-bit side. To return a pointer to a data type whose size is different on the 16-bit and 32-bit sides of a 32->16 thunk, you should write a wrapper function that returns the real type, and have the wrapper pass a buffer to the thunk function as an extra parameter. The target side of the thunk should put the return value into this buffer, and then the wrapper on the calling side should return the data from the buffer once the target side of the thunk returns. The thunk compiler has one special type (bool) that is not found in C or C++ compilers. You should use this type in your thunk scripts for functions that return boolean values when the TRUE result is any non-zero value. Floating-Point Data TypesThe thunk compiler does not support any floating point types. There are three floating point types to consider, and how you approach them depends on which you need to use.A float is four bytes long on both the 16-bit and 32-bit sides of a thunk. A float can be passed to a thunk function by declaring it as a DWORD (unsigned long) in the thunk script. This makes the thunk compiler pass a four byte value without translating its value. A double is eight bytes long on both the 16-bit and 32-bit sides of the thunk. To pass a double to a thunk function, declare a struct containing two DWORDs (unsigned longs) in the thunk script, and pass the struct into the function in the thunk script. Here is an example 32->16 thunk:
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, C and C++ compilers for Win32 platforms such as Microsoft Visual C++ implement this type as a 64-bit double. If you cannot tolerate the loss of precision of converting 80-bit long doubles to 64-bit doubles, then you should pass the 80-bit value as a structure consisting of two DWORDs (unsigned longs) and a WORD (unsigned short), and you will have to handle the value manually on the 32-bit side of the thunk. PointersThe thunk compiler automatically translates pointers to most types. It properly handles pointers to the following data types:
The thunk compiler does not handle all cases of pointers used in aggregate types, such as arrays of pointers. Finally, the thunk compiler places limitations on pointers as return values as described in the "Return Values" section earlier in this article. There may be times when you want to pass an address without having it translated. Because the thunk compiler automatically translates pointers, you should use the DWORD type instead of a pointer type to pass addresses that you don't want translated. Then, you can use the untranslated address on the target side of the thunk, possibly in calls to the Generic Thunk APIs. However, keep in mind that addresses that have not been translated will cause an access violation if dereferenced on the target side of the thunk. Unions and C++ ClassesUnions are not supported by the thunk compiler because it cannot determine which type the union will actually hold at run time. For example, the following union must be treated differently by the thunk compiler depending on which member the application was actually using when it made the thunk call:
The problem the thunk compiler has with this union is whether it should generate code to pass the value unchanged, assuming that the dwIntegerValue member will be used, or to translate the value as a pointer, assuming that szFileName will be used. Because the thunk compiler cannot make this determination, it cannot generate the correct code to handle this union. You should handle union types in thunks by declaring a structure in the thunk script large enough to hold the union, and then handle the union's data manually on both sides of the thunk. Do not thunk C++ objects. Objects are not supported by the thunk compiler because there isn't any way to reliably pass them as parameters to thunks. In addition to specifying the object's physical layout, a class may also define a vtable which contains addresses of all the class's virtual functions. Because all objects of a class share a single vtable, there is no way to translate the vtable so that it can be used by some objects that have been thunked and others that haven't. Linked Lists and other Dynamic Data StructuresAlthough the thunk compiler supports structures with pointers, it does not provide a way to handle linked lists and other dynamic structures. This is because the thunk compiler must know the size of the data it translates at compile time, but the number of elements that dynamically-allocated structures contain is indeterminate at compile time.REFERENCESThunk Compiler reference in the Microsoft Win32 SDK Documentation Generic Thunk API reference in the Microsoft Win32 SDK Documentation Additional query words: 4.00 GPF
Keywords : kbcode kbnokeyword kbKernBase kbGrpKernBase |
Last Reviewed: March 7, 1999 © 2000 Microsoft Corporation. All rights reserved. Terms of Use. |