Windows 95 Dynamic Data Exchange Explained
Windows 95 managed Dynamic Data Exchange (DDE) between legacy applications by maintaining backward compatibility with 16-bit Windows architectures while integrating newer 32-bit processes. By utilizing message-based interprocess communication, global atom tables, memory translation layers, and internal thunking mechanisms, Windows 95 allowed legacy 16-bit applications and modern 32-bit applications to exchange commands, establish client-server links, and share data seamlessly despite fundamentally different memory models.
The Foundation of DDE Messaging
Dynamic Data Exchange relied directly on the standard Windows
messaging subsystem. Applications communicated by designating one window
as a client and another as a server. The transaction began when a client
broadcasted a WM_DDE_INITIATE message across the system to
locate a server capable of handling a specific application name and
topic.
Once an application responded with a WM_DDE_ACK message,
the two applications conducted all further interactions directly via
their window handles (HWND). Subsequent actions—such as
requesting continuous updates (WM_DDE_ADVISE), sending
one-off data requests (WM_DDE_REQUEST), pushing unsolicited
data (WM_DDE_POKE), or passing data payloads
(WM_DDE_DATA)—depended exclusively on this structured
message passing.
Data Referencing and Global Atoms
Because passing raw text strings across application boundaries was
inefficient, DDE leveraged global atoms. When an application initiated a
conversation or requested a specific data item, it converted the string
identifier (such as the application name, topic, or item name) into an
integer atom using the Windows GlobalAddAtom API.
The application then passed the atom within the message parameters
(wParam and lParam). The receiving application
retrieved the corresponding string via GlobalGetAtomName.
This minimized overhead by allowing applications to exchange fixed-width
integers instead of variable-length strings over the message queue.
Memory Sharing Across Address Spaces
In Windows 3.1, legacy 16-bit applications shared a single global
memory space, meaning an application could allocate a memory block using
GlobalAlloc with the GMEM_DDESHARE flag, pass
the handle via a DDE message, and allow the receiving application to
access the data directly.
Windows 95 introduced isolated 4 GB private virtual address spaces
for 32-bit applications while running 16-bit applications in a shared
compatibility space. To handle legacy DDE memory sharing across these
distinct environments, Windows 95 managed memory allocation within the
shared system arena (the 2 GB to 3 GB address range). When a legacy
application allocated memory flagged with GMEM_DDESHARE,
Windows 95 ensured that the physical memory was mapped to an address
accessible to both the source and the target process.
Message Thunking and Pointer Translation
To support mixed-environment conversations—such as a 16-bit legacy application communicating with a 32-bit application—Windows 95 introduced automatic message thunking. When a DDE message crossed the boundary between 16-bit and 32-bit architectures, the operating system intercepted the message:
- Parameter Unpacking and Repacking: 16-bit Windows
packed parameters differently into
lParamthan Win32. The operating system translated and repacked the parameters to match the bitness of the target application's message handler. - Address Translation: Pointers were translated between 16:16 segmented addresses used by legacy programs and flat 32-bit linear addresses used by modern programs.
- Handle Mapping: Handles representing global memory allocations and atoms were converted so that the receiving process could access the referenced object without causing a general protection fault.
Concurrency and the Win16 Lock
Legacy 16-bit applications were designed for cooperative multitasking, expecting to run continuously until yielding control. Because Windows 95 introduced preemptive multitasking for 32-bit software, concurrent DDE interactions risked destabilizing legacy code.
To maintain stability, Windows 95 routed legacy DDE messaging through the Win16 Mutex (commonly called the Win16 Lock). If a 32-bit application initiated a DDE transaction with a 16-bit application, the 16-bit application’s message loop only executed once it acquired the Win16 Lock. This serialized message execution, preventing the legacy application from re-entering non-reentrant code paths while processing incoming DDE requests.