Mailslots

Introduction

There are several techniques for Inter Process Communication (IPC) like DDE, pipes, atoms, sockets and mailslots. Each technique has its own characteristics; this topic concentrates on Mailslots.
Mailslots offer an easy way for a process to broadcast a message to several other processes at once. A Mailslot is a pseudo-file created by one particular process, known as the Mailslot server. The Maislot server can read messages from the mailslot.
Other processes, known as Mailslot clients, can write messages to mailslots owned by Mailslot servers. A Mailslot client is not allowed to read from another process' mailslot.
Remember: a mailslot is a pseudo-file and lives in memory only for the lifetime of the process, or shorter. Don't confuse it with email where messages are persistently stored in an "inbox folder" or file.

A simple scenario

For the sake of simplicity it's easiest to think of processes as 'computers'. It is possible to set up mailsots between processes on the same computer but this has some limitations.
In it's simplest form, a server can receive messages from one or many clients but the clients can not receive anything.

In this case, Sue has set up a mailslot on her local machine. She named this mailslot

      \\.\mailslot\myapp\finance

(The "dot" means "local machine". "myapp" and "finance" are path and name to be picked by your application). This path and name have to be known by other processes. That won't be a problem if those other processes are running instances of the same application!
Bob and Pete are clients; they can write messages to Sue's mailslot by addressing

      \\Sue\mailslot\myapp\finance

Where "Sue" is a machine name. More realisticly, they would address to

     \\domainname\mailslot\myapp\finance

or even to

      \\*\mailslot\myapp\finance

where the asterisk stands for the primary domain. In these last two cases, the messages will also be picked up by anyone else in the domain who defined the mailslot like Sue did. Hence you have a broadcasting mechanism.

A more realistic scenario

Although Sue is the mailslot server for her own local mailslot, there is no reason why she shouldn't be able to send messages to other processes who also have a local mailslot named ".\mailslot\myapp\finance". Sue can be a server and a client at the same time.
Likewise, there is no reason why Bob and Pete should't be able to serve a mailslot each, also using this same mailslot name.
A message sent by either Bob, Pete or Sue (or anyone else) will now be recieved by all others.

Limitations

Only one process on a computer can serve a mailslot with a particular name. In other words: this broadcasting mechanism will not work between multiple instances of an application running on the same computer.
The maximum size of a message is 64K if it is not broadcast. A broadcasted message can be no longer than 400 byte.
Mailslots use datagrams. There is no way of knowing if a broadcasted message will actually be received by everyone.
If you need to broadcast across a WAN you may consider setting up sockets on an "IP Multicast" enabled network. See http:www.ipmulticast.com.

Procedures

A server creates a mailslot by calling procedure CreateMailslot(). This procedure returns a Mailslot-handle. The Mailslot-handle is somewhat compatible with a file-handle: the server can read messages from the mailslot by calling procedure ReadFile(). Procedure GetMailslotInfo() tells how many messages are in the pseudo-file and how long they are. The server calls CloseHandle() to destroy the mailslot.
A client writes to a mailslot just as if it writes to a file, except the filename must be a valid mailslot-name. In other words a client uses CreateFile(), WriteFile() and CloseHandle().
Note: CreateFile() doesn't create the file of course, it only gets a handle to the existing pseudo-file (the "share"-flags are especially important here).


Mailslot example

Source code by Todd. G. Nist, Protech Systems Inc.

Source is attached for download.

This procedure can run either as a mailslot server or as a mailslot client. If it runs as a server, it will be able to read messages (it uses a PSTimer.ocx to scan for new messages each 500 msec). If it runs as a client it is able to send messages if there is also a server active.
I (Jurjen) had only one PC to play with, so I launched two instances of Progress on that PC, each running a copy of w-mailslot.w as shown on the pic.

User Manual for this example

First you have to start a mailslot server: run w-mailslot.w and make sure the toggle-box is checked. Click on the editor-widget: the ON ENTRY trigger of the editor will create the mailslot. From now on the PSTimer will check for new messages and will show them in the editor.
After you have created a server you can start one or more clients: run w-mailslot.w again and make sure the toggle-box is NOT checked. Change the mailslot name if the server is not running on the same PC. Click on the editor widget: the ON-ENTRY trigger will now open the mailslot for writing and the PSTimer.ocx will be closed.
Type a message, press the "Write" button and watch how the server receives it.

About the source

This program will not act as a server and a client at the same time, so let's look at it as if it were two separate programs.

the server functionality

These three parts do the core functionality: CreateMailslot, ReadMailslot and CloseHandle.

RUN CreateMailSlot{&A} (INPUT  cMailslotName:SCREEN-VALUE,
                        INPUT  0, /* Maximum message length */
                        INPUT  0, /* Read timeout */
                        INPUT  0, /* security attributes */
                        OUTPUT hMailSlot). /* handle to mailslot 
                                              or INVALID_HANDLE_VALUE */
--------------------------------------------------------------------------------
FUNCTION ReadMailSlot RETURNS CHARACTER
  ( /* parameter-definitions */ ) :
 
  DEFINE VARIABLE cTempStr    AS CHARACTER NO-UNDO.
  DEFINE VARIABLE iBytesRead  AS INTEGER  NO-UNDO.
  DEFINE VARIABLE iResultCode AS INTEGER  NO-UNDO.
 
  /* allocate some space */
  cTempStr = FILL(' ', 512).
 
  RUN ReadFile (INPUT  hMailslot,
                OUTPUT cTempStr,
                INPUT  512,
                OUTPUT iBytesRead,
                INPUT  0,
                OUTPUT iResultCode).
 
 
  RETURN TRIM(cTempStr) + 
         (IF TRIM(cTempStr) = '' THEN '' ELSE CHR(10)).
 
END FUNCTION.
--------------------------------------------------------------------------------
  RUN CloseHandle(hMailslot,
                  OUTPUT iResultCode).

Function ReadFile() reads one message even if there are more messages pending. The size (512) may not be enough. I think you should call function GetMailslotInfo() first: this returns the number of pending messages (so you can read all of them) and the size of the next message (so you can allocate the proper size).

the client functionality

The most important parts are now: CreateFile, WriteMailSlot and CloseHandle.

RUN CreateFile{&A}( INPUT cMailslotName:SCREEN-VALUE,
                    {&GENERIC_WRITE},
                    {&FILE_SHARE_READ},
                    0,
                    {&OPEN_EXISTING},
                    {&FILE_ATTRIBUTE_NORMAL},
                    0,   
                    OUTPUT hMailslot).
--------------------------------------------------------------------------------
FUNCTION WriteMailSlot RETURNS CHARACTER
  ( /* parameter-definitions */ ) :
 
  DEFINE VARIABLE iBytesWritten  AS  INTEGER     NO-UNDO.
  DEFINE VARIABLE iResultCode    AS  INTEGER     NO-UNDO.
 
  /* Write to the mailslot */
  cMsg =  "\\":U + cComputerName 
         + " - ":U 
         + cMsg:SCREEN-VALUE IN FRAME {&frame-name}.
 
  RUN WriteFile(INPUT  hMailslot,
                INPUT  cMsg,
                INPUT  LENGTH(cMsg) + 1,
                OUTPUT iBytesWritten,
                INPUT  0,
                OUTPUT iResultCode).
 
  IF iResultCode = 0 THEN
  DO:
    MESSAGE "Error on WriteFile. "  
            "Terminating client." 
            VIEW-AS ALERT-BOX.
    APPLY "window-close" TO {&window-name}.
  END.
  ELSE
    cMsg:SCREEN-VALUE = "".
 
 
  RETURN "".   /* Function return value. */
 
END FUNCTION.
--------------------------------------------------------------------------------
  RUN CloseHandle(hMailslot,
                  OUTPUT iResultCode).

Attachments

mailslot.zip : demo