Code Samples, Tips, Tricks and
All Content Copyright © 2000
New Vision Software
All rights reserved
Access 16bit Code from 32bit VB
by Bryan Stafford - New Vision Software
Believe it or not, there is still a lot of 16bit code running on Win9x systems. Much of the code is legacy stuff that is either impossible or impractical to migrate to the 32bit platform. However, a surprising amount of 16bit code has been newly written specifically for the Win9x platforms. Part of the reason for this is the fact that there are quite a few things that can be done far more easily or even exclusively in 16bit code.
A couple of examples of things that require calling 16bit code are obtaining the free system resources and enumerating control panel applets. In the case of free system resources, 16bit code can access the GetFreeSystemResources API function in the 16bit "User" library1. Surprisingly, there is no equivalent function in 32bit land. In the second case, enumerating control panel applets, several of the applets on Win9x systems are 16bit libraries. To query information from these applets, they must be loaded into the calling application's process space which is impossible to do using the 32bit LoadLibrary API function.
Let's look at the second example (enumerating control panel applets) from the previous paragraph. As was stated, your 32bit code cannot directly load a 16bit library. So, how the heck does the Control Panel do it! The Control Panel does this by a process called thunking. Thunking is a term used to describe calling cross platform code and can mean calling 16bit code from a 32bit platform or calling 32bit code from a 16bit platform. In essence, a thunk is a translation layer that sits between the 16bit and 32bit code.
There are two basic methods used for thunking that depend on which platform is doing the calling. In the case of 16bit calling 32bit code, the generic thunking functions in the 32bit kernel can be used. For the opposite scenario, the 32bit process must use a flat thunk to get the job done. It is also worth noting that flat thunks can be used to make 16bit to 32bit calls but because flat thunks are far more difficult, generic thunks are preferred for this situation. This article will explore flat thunks and the use of the thunk compiler, thunk scripts, the assembly language code generated by the thunk compiler and the C language code required to create thunking layer dlls.
A flat thunk consists of a pair of dlls (one 32bit and one 16bit) that are used to translate calls from 32bit code to 16bit code. To allow the two dlls to communicate, some intermediate code must be used to translate memory addresses (pointers) between platforms. If you have any past experience with 16bit process memory calls, you may recall that they consist of a pointer to a memory segment and the offset into that memory segment. This is different than a 32bit process memory pointer which consists of an absolute address to the memory being accessed. So, the problem, in a nutshell, is translating segment + offset pointers into absolute addresses. Since VB programmers don't usually need to worry about things like memory pointers, you might be saying, "So what? Why is this a problem?" The problem is that ALL software is ultimately based on memory pointers. It is the IDE and the programming language that hide these ugly details from us but when you get right down to it, every variable, function, sub and etc... that you write (in any language) consists of an address in memory. Now, imagine a 16bit dll being loaded into a 32bit process where none of the memory addresses match up on either side of the function calls. It just plain can't be done without proper translation.
The process of providing a 32bit to 16bit thunking layer begins with the creation of a 16bit dll written in the "C" programming language. The 16bit dll used in this project was written and compiled using VC++ 1.52 (I knew there was a good reason why I didn't trash that old 16bit CD!). This dll consists of wrappers for all of the 16bit functions that you wish to call from your 32bit code. This 16bit dll will handle loading and calling all of the code in the target 16bit library. Once the 16bit wrapper functions are written, a thunk script must be written. This thunk script describes, in detail, the function calls in the 16bit thunking layer dll with information about which parameters are for input and which are for output. The thunk script is then compiled using the thunk compiler (THUNK.EXE) available in the Win32 SDK2.
Does this sound confusing yet? If not, hang on because we've only scratched the surface! <g>
The thunk compiler, in turn, generates an ASM (Assembly Language) file containing all of the code needed to implement the thunking layer. This ASM code is compiled into two separate object (.obj) files, one for the 16bit thunking layer dll and one for the 32bit thunking layer dll. The MASM compiler (ML.EXE), also available in the Win32 SDK2, is used to compile this ASM code. The command line switches used to compile each of the object files are listed in the thunk script file (.thk) included in the project source code for this article. The two object files will be linked into their respective dlls in the linking stage of the compilation process.
Part of the ASM code generated by the thunk compiler consists of two "ThunkConnect" functions, one that will be called from the 16bit dll and one that will be called from the 32bit dll. Each dll must include a prototype of the respective version of the function in it's C code as well as the respective implementations of the dll entry points that are called by Windows when the libraries are loaded. The minimum code for each of the libraries is quite similar.
OK, we have the thunking layer 16bit dll code, the thunk script and the ASM code. Now, we need to write the 32bit thunking layer dll code that will be needed to initiate the thunking calls from our 32bit VB application. The 32bit dll used in this project was written in VC++ 5.0. This 32bit dll consists of only two functions; the dll entry point, called by Windows when the lib is loaded and the ThnukConnect function described in the last paragraph. The ASM object file provides the rest of the code needed for the calls from our 32bit VB project.
At this point, each of the thunking layer dlls must be compiled and linked to the respective ASM object files. Once the libraries are compiled, the declares for the exported functions in the 32bit DLL must be added to your VB project. Creating the declares can be tricky because of the way some data types are passed across the thunking layer. You can examine the code in the sample project to get an idea of how the different data types are handled. Additionally, some of the API functions from the WOW (Windows On Windows) compatibility library must be employed to lock and "fix" 16bit memory pointers in place and to translate any 16bit handles into their 32bit counterparts.
That's all there is to it! <said facetiously> All in all, thunking is probably one of the most complex and challenging aspects of Windows programming. Even relatively easy calls can take hours to debug because of quirks and limitations in the ASM thunking layer code. This is not intended to scare anyone away from using thunking code but is rather intended as a warning that implementing thunks is no "walk in the park". Hopefully, this article and the companion example project will go a long toward making thunk implementation from 32bit VB a much easier undertaking. You will also want to be sure to visit the section on thunking in the Win32 SDK for a full understanding of this topic.