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:

  1. Parameter Unpacking and Repacking: 16-bit Windows packed parameters differently into lParam than Win32. The operating system translated and repacked the parameters to match the bitness of the target application's message handler.
  2. Address Translation: Pointers were translated between 16:16 segmented addresses used by legacy programs and flat 32-bit linear addresses used by modern programs.
  3. 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.