Quick Thunking on Windows 9x Platform.

Introduction:

The most common method to call 16-bit code from Win32 process is a flat thunk which is available on Win 9x platform. It allows bidirectional calls between Win32 and Win16 code and is used by many applications but it requires special 32-bit DLL and 16-bit DLL, i.e. if you want to use functions from kernel.dll or user.dll you must write additional 16-bit DLL to communicate with it. This small artical will describe how to call 16-bit without additional dll. This is unofficial information so use it ON YOUR OWN RISK. The method is based on few undocumented functions of Windows 95 so it will not work on NT. Main idea is: load 16-bit dll via undocumented functions in KERNEL32.DLL and call required function through 'QT_Thunk' from KERNEL32.DLL. No thunk compiler required !!!

Description:

Sometime Win32 application on Win9x platform must use Win16 api functions or Windows 16-bit DLLs. The typical solution is a FLAT THUNKING. Application will call special Win32 DLL which will thunk to special Win16 DLL. So if application will call Win16 api function of third-party dll it will take a time. But that operation can time-critical and such thunking is not suitable for it. In that case solution is QUICK THUNK. Application will call Win16 function directly. This method is not documented is SDK and is only for Windows internal usage. To do such thing application requires three special functions from KERNEL32.DLL. I call that functions: LoadLibary16, FreeLibrary16 and GetProcAddress16, they will be detaily described later. Application can use this functions since they are using __stdcall protocol, but they are absent in kernel32.lib in Win32 SDK and exported only be ordinals. Another barrier is a standart Win32 'GetProcAddress' function which will not return any address from KERNEL32 module when ordinal used instead of function name. There are two solutions for this problem:

1) Use static link to that function from EXE or DLL

2) Write own 'GetProcAdress' function for PE-executables

Other problem is write call to Win16 function. 'GetProcAddress16' return 16-bit far pointer to Win16 entry point and it can not be called directly from Win32 code, 'QT_Thunk' function performs calls to 16-bit code. Problem is a specific protocol to call this function. This function was designed to use in assembly language so it is almost impossible to use this function without using assembly language. All parameters to Win16 function must be passed via stack as Win16 function requires, 16-bit entry point address must passed through EDX register and 32-bit pointer to the end of special 40h bytes buffer must be passed through EBP register. Only one solution: Use assembler to perfrom calls, main reason to usage QUICK THUNKING is a speed. Also All 32-bit flat pointers parameters must be converted to 16-bit far pointers. Application can use Map*** function for this task. Return value of Win16 function will be returned in AX register or in DX:AX pair depends on Win16 function return value type. Limitation of 'QT_Thunk': it is impossible to pass parameters to 16-bit function through registers.
Note: WATCOM C/C++ does not allow to specify EBP register in function call protocol definition.

Functions:

1) Module: KERNEL32 Ordinal: 023h
This function loads Windows 16-bit library by thunking to 'LoadLibrary 'in module 'KERNEL'.

Prototype: DWORD __stdcall LoadLibrary16(LPSTR lpszLibFileName);

Parameters:
LPSTR lpszLibFileName - address of name of library fille

Returns:
Instance handle, Win16 instance handle of module, it must be greater or equal than 0x20 if function successed.

2) Module: KERNEL32 Ordinal: 025h
This function releases 16-bit dll via thunk to 'FreeLibrary' in module 'KERNEL'.

Prototype: VOID __stdcall FreeLibrary16(DWORD dwInstance);

Parameters:
DWORD dwInstance - Identifies the module that contains the function.

Returns:
It has no return value since FreeLibrary in Win16 doesn't return anything.

3) Module: KERNEL32 Ordinal: 024h
This function return address of function in 16-bit module, thunk to 'GetProcAddress'

Prototype: DWORD __stdcall GetProcAddress16(DWORD dwInstance, LPSTR lpszProcName);

Parameters:
DWORD dwInstance - Identifies the module that contains the function.
LPSTR lpszProcName - Points to a null-terminated string containing the function name, or specifies the ordinal value of the function. If it is an ordinal value, the value must be in the low-order word and the high-order word must be zero.

Returns:
16-bit far address(SELECTOR:OFFSET) of entry point if function successful or zero if failed.

4) Module: KERNEL32 Name: QT_Thunk
This functions calls 16-bit entry point.

Parameters:
EDX - address of entry point (16-bit far pointer)
EBP - end of 40h bytes buffer
All 16-bit function parameters if any passed on the stack

Returns:
Value from 16-bit function in register AX(int) or in DX:AX(long) pair

Remarks:
Buffer usually allocated in stack so it first 40h bytes of local variables area, it is typical. All 16-bit function parameters passed on stack will be in 16-bit stack, function does not modify any of them. All parameters which are 32-bit near pointers must be converted to 16-bit far pointer via Map*** functions family.

Examples:

Quick Thunk 'Hellow World' example. - Example I made before reading Matt Pietrek book 'Windows 95 system programming sectrets'.

Flat Thunks - Article about flat thunking on Win 9x

See also:

'Thunk Examples' in Win32 SDK to understand how to pass parameters to 16-bit functions from 32-bit process.

Author:

Contact author: rgmroman@narod.ru

Hosted by uCoz