Here we are back in the second episode of our journey into Windows IPCs. Today we are going to talk about the main mechanisms put in place by various OSs to achieve “IPC”, “Inter-Process Communication”.


IPC FOUNDATIONS

Examples

Very common in any OS is the need to exchange data between different applications and coordinate behaviors based on this. Here are some examples:

#1. A Web server such as Apache or Nginx needs to communicate with CGI or FastCGI processes.


This detail is often overlooked, but how do Apache and Nginx interface with PHP, which is a different executable and installed separately? This is where IPC steps in:

1. The web server receives a request from a client for a PHP page: it has to render it, but it does not know how to interpret the file instructions, being only a web server.
2. At this point then the server sends, in turn, using a protocol called FastCGI, a request to a socket, and calls the PHP executable passing it as a parameter the socket from which to take the request data.
3. PHP-FPM listens on the specified socket (Unix or TCP) and, upon receiving the data, assigns the job to one of its workers.
4. The PHP worker interprets the file (e.g., “index.php”) and sends the response to the socket from before
5. Apache reads the FastCGI response from the socket and sends the HTTP response with the page.

#2. UNIX daemons must monitor the status of services or receive requests from other processes


Interesting here is the distinction between Unix Domain Socket and Internet Socket: the former works and is accessible only locally, on the same machine. The second is designed for networked data transfers.

#3. PIPE operator needs to transfer data, for example, from ps to grep

#4. Desktop environments such as GNOME or KDE must communicate with graphics servers (such as X11 or Wayland)


Recall the previous post: through generalization and abstraction it comes naturally to group entities by common characteristics. Hence the need for modularity and reuse in the programming environment.

GNOME/KDE and X11 are at different levels in the architecture of a graphical system:

- GNOME/KDE are desktop environments, that is, they provide facilities for an easy and intuitive desktop experience, such as window management and graphical effects;
- X11 is a graphics server that interfaces with hardware for basic graphics operations.

Types

This non-exhaustive list highlights the most common OS mechanisms for IPC:

# Signals
Present natively only in POSIX environments, these are precisely asynchronous signals transmitted from one process to another. These signals typically contain no auxiliary data, are generated under specific hardware or software conditions, and are typically checked at each kernel-to-user mode transition after system calls, or when a process is in a suspended state. Examples are: SIGABRT, SIGSEGV, SIGHUP, SIGBUS, SIGKILL, etc.

# Pipes
Available in both POSIX and Windows systems, they allow one-way communication between processes. They can be anonymous or named.


Although they may seem bidirectional, Pipes actually open two file descriptors, one read and one write.

# Message Queues/Loops
Used especially in Windows in window management in graphical interfaces, they work by continuously populating and extracting messages from a shared buffer, and executing, depending on the type of message, the appropriate handlers (e.g. WM_MOUSEMOVE for when moving the mouse within a window).

# MailSlots
Available in both POSIX and Windows, allow one-way communication between clients and mailslot servers. Clients write a message to a server, these are added to a message buffer, and remain until a client reads them.

# Clipboard
These are a very weakly coupled medium of exchange found in Windows and POSIX, meaning that the application “copying” the data chooses the format of it itself, and the receiver must accept and interpret it.


DDE

In this scenario is located Microsoft’s DDE technology: Dynamic Data Exchange.

DDE provides a way to send data between applications, using both the Windows Message Queues mechanism we saw earlier and shared memory, according to a specific protocol (DDE Protocol). Since it has a message-based architecture, DDE conversations can only take place between windows, whether visible or hidden.

Overview

DDE uses a hierarchy of 3 names to retrieve as much specific information as possible: SERVICE, TOPIC, and ITEM. At the beginning of a conversation, the DDE client sends a broadcast message to all windows containing the “service/topic” pair (also called CHANNEL) about which it wants information. The first window (called SERVER) that accepts establishes a connection with the client.


You can also use WILDCONNECT, meaning you can leave topics and services empty and all DDE servers will be able to respond.

DDE messages, once the connection is established, can be:

#ACK: used to notify “the other DDE end” of the arrival and successful (or unsuccessful) processing of another message
#TERMINATE: used to terminate a conversation
#ADVISE/NOTIFY: used to get updates whenever the target of the DDE conversation changes state (a so-called DATA LINK is created, which can be WARM and HOT. In a warm data link the server notifies the client whenever the target item changes but does not send its updated content, in a hot data link the updated content is always sent along with the notification)
#REQUEST: used to retrieve messages from the server
#UNADVISE: used to inform the DDE server that it does not want to have any more updates on the target item
#DATA: used to send a data item between client and server
#EXECUTE: used to send commands to the server
#POKE: used to send additional data to the server

Notable features of DDE technology are as follows:
- DDE is asynchronous: if the client does not receive a response for a specified period of time, it goes into timeout.
- The names SERVICE, TOPIC and ITEM are case-insensitive because they use instances of Global String Atoms.
- A DDE server can also act as a client in the same conversation, and a DDE client can likewise act as a server

Theoretical Example

One scenario in which DDE can be useful is when a Microsoft Excel sheet wants to constantly update the data in one of its tables. Let’s say we have a “Players” sheet with a table containing some of the most famous soccer players with the number of goals scored next to it. To keep this table constantly updated, we can take advantage of a DDE conversation between a client (our Excel) and a server (e.g., a local program named “Statistics” containing all the updated statistics).

Here is an example flow:

1. The client starts the conversation by sending a WM_DDE_INITIATE message in broadcast to all windows
2. The DDE server returns positive WM_DDE_ACK and the connection between client and server is established. The client stores the handle of the responding window for subsequent communication.
3. The client requests a data element from the server by sending a message of type WM_DDE_REQUEST, or sends a data element to the server with a WM_DDE_POKE message.
4. In the case of WM_DDE_REQUEST, the server allocates the response and sends it in the WM_DDE_DATA message.
5. Again in the case of WM_DDE_REQUEST, the client notifies the server of the successful receipt of data with positive WM_DDE_ACK.
6. The client or server issues a WM_DDE_TERMINATE message to terminate the conversation.


Practical Example: DDE + PYTHON + ACCESS

For this practical example we will use the features of PyWin32, which, among other things, supports the creation and management of DDE conversations, to interact with Microsoft Access.


I use Microsoft Access specifically because it is still the only one that actively supports DDE. In Word the DDE features are completely disabled, in Excel partially.

First, let’s create an Access .accdb database, and, within it, create a dummy table named “Address Book” with the columns “First Name,” “Last Name,” and “Address” of type “Text” inside; let’s then populate it with a few entries.

Address Book Table
"Address Book" table in Access

At this point, we do not close Access (remember? DDE interacts with windows) and install the PyWin32 suite in our Python3 via “pip”: pip install pywin32

Then we create a file (I’ll call it ddexec.py) and insert the following snippet:

import win32ui
import dde

def execute_dde_query(database_name, query_name):
    try:
        # Create DDE Server (i know, it should be DDEClient but pywin32 specs have defined it in this way instead)
        server = dde.CreateServer()
        server.Create("MyDDEClient")
        
        # DDE: Create Conversation
        conversation = dde.CreateConversation(server)
        # DDE: Connect with SERVICE(MSACCESS) + TOPIC(DB; SQL QUERY)
        conversation.ConnectTo("MSACCESS", f"{database_name};SQL {query_name}")
        
        # DDE: Item
        dde_command = "All"
        # Send DDE Request
        response = conversation.Request(dde_command)
        
        # Print DDE Response
        print("DDE Response:\n", response)
        
    except Exception as e:
        print("Error:", e)
    finally:
        # Close DDE Connection
        server.Destroy()

# Usage example
execute_dde_query("<<Full .accdb Path>>", "SELECT * FROM [Address Book]")

Let’s run it and see DDE in action:

Address Book Table
Python DDE script output

For a more detailed understanding of the DDE message flow with related implementations and structure populating, I refer you to the Microsoft documentation: Microsoft DDE APIs.

DDE in action
API Monitor v2 showing us all DDEML calls

CONCLUSION

We looked in this chapter at IPC and some of its implementations in major operating systems. We then delved into DDE with theoretical and practical examples.

See you in the next chapter of this series, where we will delve deeper and deeper into the COM world.



I new mates!