You've just seen that far functions listed in the EXPORTS section of your .DEF file become unsuitable for normal use when Windows loads them into memory. These functions require that the value of AX on entry be the program's data segment. This data segment is different for every instance of your program. You can do only one of two things with an exported far function:
If the function is a window procedure, you can give Windows the address of the function in the window class structure when registering the window class with RegisterClass:
wndclass.lpfnWndProc = WndProc ;
If the function is a call-back function, a window subclassing function, or a dialog box function, you must give Windows an address returned from MakeProcInstance. For instance, if CallBackProc is the name of a call-back function, you must first make a call like this:
lpfnCallBack = MakeProcInstance (CallBackProc, hInstance)
Because CallBackProc is a far function, the address you pass to MakeProcInstance is actually the address of the reload thunk. The lpfnCallBack address you get back from MakeProcInstance can now be used as a parameter to a Windows function such as SetTimer.
MakeProcInstance and RegisterClass both deal with the exported far function in the same way. They create an ”instance thunk“ for the function. The address that MakeProcInstance returns is the address of the instance thunk. Instance thunks are in a fixed area of memory, and they look like this:
MOV AX, xxxx
JMP ssss:oooo
The xxxx value is the data segment address for this instance of your program. The instance thunks are different for each instance because each instance uses a different data segment. The ssss:oooo address in the instance thunk is the segment and offset address of the reload thunk that reloads (or jumps to) the function. The same reload thunks are used for all instances of a program because they jump to the same shareable code.
When Windows needs to call a function in your program (such as a window procedure), it actually calls the instance thunk. The instance thunk sets AX equal to the data segment address for that instance and jumps to the reload thunk. The reload thunk loads the segment if it is not currently present in memory and branches to the function. The function then saves the previous value of DS and sets DS from the value of AX—the data segment address for the instance. When the function ends, it retrieves the original value of DS from the stack and returns control to Windows. When Windows moves the data segment for a particular instance, Windows must also change the xxxx values in all the instance thunks for that instance.