Platform SDK: Win32 API

Testing the Thunk

After you complete the preliminary checklist provided in the previous topic, build your thunk DLLs and try to run them. If they run, continue with further testing to make sure they work as expected. If the DLLs do not run, use the following troubleshooting guide to determine and fix the cause of the problem.

If XXX_ThunkConnect16 or XXX_ThunkConnect32 fails, perform the following tasks:

  1. Run the debugging versions of the system DLLs. The debugging versions of KERNEL32.DLL and KRNL386.EXE contain many diagnostic messages to indicate why the thunk did not initialize. To run the debugging versions of the system DLLs, use the Switch to Debug DLLs icon in the SDK program group. Use Switch to Non-debugging DLLs to switch back to the retail version.
  2. Verify that the 16-bit DLL has a call to XXX_ThunkConnect16 and that the Win32-based DLL has a corresponding call to XXX_ThunkConnect32. If one of these is missing, the other will fail, and the thunk DLLs will fail to load.
  3. Put breakpoints in your Win32 DLL's DllMain, and in your 16-bit DLL's DllMain and LibMain functions to see which DLLs are not loading.

If your XXX_ThunkConnect16 and XXX_ThunkConnect32 calls are working properly, but the thunk still does not work, it is time to simplify your thunk. You can do this by removing parameters from the thunk one by one, recompiling, and testing. If you determine that a parameter is causing the failure, make sure it is the right type and that the function is declared and defined with the same number and types of parameters in both DLLs and the in the thunk script. Alternatively, you can create a simple thunk that works, and build it up until it fails by following these steps:

  1. Create a simple thunk and execute it just to make sure the thunk mechanism is set up correctly. A good choice for a simple thunk is a function with no return value and no parameters. If even the simple thunk does not work, run through the preliminary checklist in the previous topic to make sure the thunk is set up correctly. Then proceed with step 2.
  2. Check to make sure the target DLL and any DLLs it relies on can be found and loaded. If one is missing, or the loader cannot find it, the thunk will not work.
  3. Make sure your target DLL is not trying to do something that it cannot do in the context of a thunk. For more information, see Behavior of Win32 Functions Inside 16-Bit Processes.

After you have a simplified thunk that works, but the original thunk still does not work, check for the following symptoms:

  1. If your target DLL is a 16-bit DLL and it cannot access its global or static data, make sure you have exported the function correctly. If you use the /GD option with Visual C++, you must declare and define the function with the __export keyword in the 16-bit DLL's source code. Just listing the function's name in the DLL's module definition (.DEF) file is not enough. The compiler does not process the .DEF file, so it will not generate the prolog and epilog code that exported functions require.
  2. If calls to LocalAlloc in your target 16-bit DLL cause general protection (GP) faults, make sure your function is exported as described in step 1.
  3. If you get a GP fault in KERNEL32 just after your target 16-bit function returns, make sure the target function is declared and defined as PASCAL. You can not use the C calling convention. Although uncommon in C or C++ code, make sure the target function did not modify the DS, ES, FS, SS, EBP, EBX, ESI, or EDI registers. C or C++ code should not cause the registers to be modified, but check assembly-language code carefully.
  4. If you get a GP fault in your 32-bit thunk DLL or KERNEL32 immediately after your Win32-based target function returns, make sure the target function is declared with WINAPI and that it didn't modify the DS, ES, FS, SS, EBP, EBX, ESI, or EDI registers. C or C++ code should not cause the registers to be modified, but check assembly-language code carefully.
  5. If your 16-bit target function returns to an invalid location, make sure it is declared and defined as FAR. This is especially important for small-model DLLs; functions in medium- and large-model DLLs are FAR by default.
  6. If you experience a GP fault in a 16-bit function when you access more than 64K of data from a pointer passed as a parameter (that is, a translated pointer), you need to allocate an array of tiled selectors. This is described in the following article in the Microsoft Knowledge Base:

    ARTICLE-ID: Q132005
    TITLE: AllocSelector & FreeSelector Documentation Incomplete

    On the 16-bit side, thunked pointers always consist of a single selector with a limit of 64K, which means you cannot use them as huge pointers. The entire original range of data that the pointer addresses is accessible to the 16-bit target DLL. However, this is true only if the DLL creates an array of tiled selectors to reference it, and if it uses huge pointer variables to access the data.

The following are additional debugging tips:

  1. Make sure you use only a translated pointer in the context of the thunk. Selectors allocated by the thunk compiler for use by 16-bit targets are freed as soon as the thunk returns.
  2. Put breakpoints at the beginning of your target functions to make sure they are being executed. If they are, and you have debugged the target side independently of the thunk, and the error is caused inside the target DLL, it is possible that the target is doing something that cannot be done in a thunk, or referencing memory that does not exist. See steps 7 and 8.