In this section, I want to give you some insights into how Windows is able to move your program's code and data segments and (in the case of code segments) discard and later reload the segments in real mode. If you'd rather not know (or if the assembly language in the pages ahead looks like Greek to you), that's fine. But you're likely to see some of this code when debugging Windows programs, so it's better that you see it here first and understand what's going on.
I'll be discussing the most general recommended case—a program that contains multiple moveable and discardable code segments and a single moveable data segment. Everything works a little differently (and more simply) when the segments are fixed and nondiscardable.
When a program has a single data segment, neither the code nor the data have to contain any explicit references to the segment address of the data. When a Windows program begins running, Windows has already set the DS and SS registers to the data segment. All pointers are near pointers. When the program needs the data segment address (for instance, for casting near pointers into far pointers when calling Windows functions), it simply uses the value of DS. (In contrast to this, when you run a regular .EXE program outside Windows, MS-DOS does not set DS to the program's data segment on entry to the program. The program must begin by executing code like this:
MOV AX, DGROUP
MOV DS, AX
You'll never see code like this in a compiled Windows program.) If you cast a near data pointer into a far pointer and store the far pointer in a variable, you're storing the current value of the data segment address. If Windows moves the data segment, that far pointer will no longer be valid. That's why it is recommended that you not do this.
Windows moves a program's data segment only during certain Windows calls. Most often, movement of a data segment occurs during a GetMessage or PeekMessage call. Windows always returns the new values of DS and SS to the program when it returns from one of these calls.
Windows programs use the same segment for data and the stack. However, Windows libraries (including the USER, KERNEL, and GDI modules, as well as the drivers) have their own data segments but not their own stacks. They use the stack of the program calling the Windows function. Thus, the stack segment address is always that of the currently running program, even if the program is calling a Windows function. When switching from one program to another, Windows must also switch the stack.
When Windows calls a function in your program (such as a window procedure, a window subclassing function, a call-back function, or a dialog box function), the stack segment address is set to your program's stack segment, but the data segment address is still the one used by the Windows library calling your function. For this reason, Windows must also include a facility so that programs retrieve their own data segments during one of these calls.
All these aspects of Windows' memory management—supporting multiple instances, moving code and data segments, and discarding and reloading code segments—are tied together.