Communication between OS/2 and Windows Processes

by David Kenner

As part of The Developer Connection team, part of my job is to travel to the technical seminars and talk to the development community. Frequently, I give a presentation on the architecture of virtual device drivers (VDDs), how they fit into the OS/2 architecture, and how to build them. During the Question and Answer session that follows my presentation, the same question would consistently come up: What is the recommended interprocess communication (IPC) mechanism for Windows and OS/2 processes to communicate? The answer: Use named pipes. Several named pipe APIs exist that would be appropriate for this. Typically, the OS/2 process creates the pipe, and the DOS or Windows process opens the other end of the pipe to read or write to it. This mechanism is used in OS/2 2.0 for dynamic data exchange (DDE) support.

It became clear after talking to developers that a better mechanism is needed to allow for larger amounts of data to go back and forth between the processes. Because named pipes use the file system, the transfer rate was limited by the speed of the target system. So I set off to investigate how we could improve on the existing mechanism.

The Journey
First, my research took me into the bowels of a OS/2 VDD named VWIN.SYS. Within VWIN.SYS, a lot of the logical interprocess communication (IPC) that I needed to do already resides. VWIN.SYS is the VDD that allows for windowed WIN-OS/2 sessions or seamless OS/2. Since VWIN.SYS was a proven mechanism for accomplishing this, I followed the logic of this VDD.

What we must do was to use our new VDD (based on VWIN.SYS) as the interface layer between our WIN-OS/2 process and our OS/2 process. We construct a VDD that lets us send messages in either direction, and receive responses back to the originating process, if required. We wanted our VDD to be fairly generic; that is, it must be structured to queue up messages and notify the destination process that a message is waiting. Part of the message data includes the originating process identification (PID), so we optionally can return a message to the originator. Our VDD allocates memory from the system arena for each new message in the queue.

Because each message put into or extracted from our message queue is performed in the context of the currently executing thread, the copy is performed while the correct LDT is active. During message posting, each process copies the message data from a portion of its own address space to the global data space of the VDD. If the process is extracting a message, it copies the message data into its local data space. This allows us to avoid the same context issue.

Start off with the OS/2 side of the interface. We have to set up a way for the OS/2 application to be able to talk to the VDD. Do this by exporting an interface using the VDHRegisterVDD service. In our VDD, we will set up an interface that our OS/2 application can use to wait for any message data. Now our OS/2 process can use the DosOpenVdd and DosRequestVDD to talk to the VDD. It's up to the application developer to define the interface between the OS/2 process and the VDD. Typically, the interface mechanism is contained in another thread. This thread is blocked until messages are sent. The thread will block on the DosRequestVDD when the command packet that the VDD receives specifies WAITE.

The Details
We will use a set of logical interfaces that can be mapped to the set of OS/2 and VDH services as follows:

OpenVirtQueue: Within our OS/2 application, we will use two system APIs to accomplish this. Our application will use the DosOpenVDD API and the DosRequestVDD API. Start, by opening our VDD with the DosOpenVDD. From this call, we get a handle to our VDD. Now that we have the handle to our VDD, we can use the DosRequestVDD service to call into the VDD and perform any preprocess initialization for us. In our VDD, for example we will configure a message queueing mechanism.

The OpenVirtQueue routine is shown in Sample Program 1.  /****************************************************************************/ /*OpenVirtQueue                                                            */ /*                                                                         */ /*                                                                          */ /*A service routine that will allow us to do our initilization with the VDD */ /****************************************************************************/ APIRET OpenVirtQueue(VOID) {    APIRET apiRet;

/*     *See if our VDD is alive and if so get *a handle that we may use later */    if(apiRet = DosOpenVDD(VDDNAME,&HandleToVDD) ) {         return(apiRet); }

/*     *Call into our VDD so we perform any initialization *we need to do at this time. */    if(apiRet  = DosRequestVDD(HandleToVDD, 0,                               INIT_COMMAND, 0,                               NULL, 0,                               NULL) ) {         return(apiRet); }    return(apiRet); }  Sample Program 1. OpenVirtQueue.

WriteVirtQueue: This is the mechanism in the VDD that lets us send information back to the WIN-OS/2 process. Again, we will use the DosRequestVDD service to call into our VDD. We will send a command that corresponds to our worker routine inside our VDD. Our worker routine will post a message to our internal queue, and in turn, post a message back to the Windows message queue. (See Sample Program 2.)  /****************************************************************************/ /*WriteVirtQueue                                                           */ /*                                                                         */ /*                                                                          */ /*A service routine that will allow us to send our message data to the VDD  */ /*                                                                         */ /*                                                                          */ /*                                                                          */ /****************************************************************************/

APIRET WriteVirtQueue(PVOID pvMessageToSend,                     ULONG ulCount,                      SGID  SessionId) {    APIRET apiRet;

if(apiRet = DosRequestVDD(HandleToVDD, SessionId, POST_MESSAGE, ulCount, pvMessageToSend, 0,                              NULL) ) {         return(apiRet); }    return(apiRet); }  Sample Program 2. WriteVirtQueue

ReadVirtQueue: This is our mechanism in the VDD that lets our thread block and wait for any message data. We will set up a protocol with the VDD, so that when the application calls into the VDD using the DosRequestVDD API, the VDD will block using the VDHWaitEventSem. When we have a message from the WIN-OS2 side, we clear the semaphore and copy the data into the buffer that was passed by the OS/2 application using DosOpenVDD. See Sample Program 3.  /****************************************************************************/ /*ReadVirtQueue                                                            */ /*                                                                         */ /*                                                                          */ /*This routine is set up as separate thread that will read messages,process */ /*them,and then continue to block until additional messages come in.The    */ /*block mechansim is accomplished through the semaphore in our VDD. */ /*                                                                         */ /*Note:                                                                     */ /*There is no error return. All error messages are handled before the      */ /*thread terminates. */ /*                                                                         */ /****************************************************************************/ VOID ReadVirtQueue(VOID) {    APIRET apiRet; PVOID pvMessage; CHAR  CommandToSend[SIZ_COMMAND_BUF];

do    { /*          *Block until we get any message *data */         if(apiRet = DosRequestVDD(HandleToVDD, 0,                                   READ_QUEUE, strlen(CommandToSend), CommandToSend, sizeof(MessageBuffer), &MessageBuffer) ) {              break; }         /*           *Do all of our message processing */          apiRet = ProcessMessageData(MessageBuffer);

}while(!apiRet); /*     *Handle any error that was encountered *during our polling */

/*     *Do any other clean up we need to do      *and get out */

_endthread; }  Sample Program 3. ReadVirtQueue

We now have a mechanism to call into our VDD, receive messages, and post messages. Our worker routines that do most of our work for us are within the VDD.

Stay tuned to the next issue where we'll discuss the internal routines that we will use in our VDD and our interfaces in the WINOS/2 process. Till then...