PREVIOUS HEAD

19  DDE Server

(First implemented in 4.0J.) Expert and OEM users can now invoke TRUETEX previews from other applications via DDE (Windows Dynamic Data Exchange) ``execute strings.''2

The DDE service name is ``TrueTeX'', the topic is ``View'', and the items are the commands listed below. The TRUETEX DDE commands follow the standard syntax of Microsoft Excel and the Windows Program Manager.

19.1  DDE Commands Implemented

Here are the commands implemented:

You may give more than one argument to Open or Close, separated by commas; the previewer will interpret each in turn. The Show and SetPos execute strings require the prescribed arguments. Exit must not have any arguments.

19.2  DDE Argument Syntax

TRUETEX interprets backslash (\) as an escape character in DDE execute strings to allow you to put commas, quotation marks, white space, newlines, etc., into string arguments. TRUETEX interprets the character immediately following a backslash literally, instead of using the normal syntax rules. This is similar to the C language syntax, although octal escapes and special characters (\n, \r, etc.) are not supported. You can also insert double-quotation marks by putting them in pairs, as in the style of Microsoft Excel or Visual Basic.

Note that you must use double backslashes in an execute string to indicate each single backslash in directory paths. A C program giving a literal string for a file name in an execute string therefore needs four backslashes to indicate each single directory backslash!

Double quotes around arguments are optional, but must surround any argument containing unescaped white space.

You can concatenate DDE commands into a single DDE execute string. Syntax errors (unquoted argument, incorrect number of arguments, etc.) in a given execute string will cause the DDE server to ignore the command and proceed to the next square-bracketed command ([...]) in the same execute string. An unknown command (for example, misspelling ``Open'') will abort execution of the rest of the execute string.

Semantic errors (files not found, malformed DVI files, missing fonts, etc.) will cause the previewer to display message boxes, as if the commands were given on the program item command line, but this will not affect the status value returned to the client. TRUETEX does not support any protocol for returning result information to the DDE client application, other than success (bAppReturnCode = 0) or failure (bAppReturnCode = 0). ``Success'' merely indicates that the command passed syntactical tests; it does not indicate any semantic success such as a file being successfully opened or printed.

19.3  DDE Service Enabling/Disabling

The Options Expert DDE Server preference item controls whether TRUETEX previewer responds to DDE commands. Disabling the service prevents the previewer from responding, which may be useful in debugging unexpected behaviors.

A DDE client will also receive no response if the TRUETEX previewer is not running. That is, the client or the user, when expecting previewer DDE service, must ensure that the previewer has already been launched, either from the Windows Program Manager or from an application (perhaps the client application itself) via a WinExec() call. Unlike the more advanced OLE servers, the Windows system has no registry of DDE servers to launch when a client requests a given DDE topic.

19.4  DDE Platform Interoperability

Various permutations of the Windows version versus the WIN16 or WIN32 executable versions of the program affect whether DDE execute strings work. In general, DDE will certainly work when the TRUETEX DDE server and the host system both use the same API word-length (WIN16 versus WIN32). That is, DVIGDI32.EXE DDE service always responds on Windows 95 or Windows NT, without regard to whether the DDE client is WIN16 or WIN32; likewise DVIGDI16.EXE DDE service always responds on Windows 3.1. The exceptions are in the following cases: DVIGDI32.EXE on Windows 3.1 does not respond to any DDE clients, WIN16 or WIN32; and DVIGDI16.EXE on Windows 95 does not respond to WIN32 DDE clients, although it does respond to WIN16 DDE clients.

The following table lists all the possible permutations:

Will Host DDE Client TRUETEX
Respond System WIN16/WIN32 DDE Server
Yes Windows 3.1 WIN16 DVIGDI16.EXE
No Windows 3.1 + Win32s WIN16 DVIGDI32.EXE
Yes Windows 3.1 + Win32s WIN32 DVIGDI16.EXE
No Windows 3.1 + Win32s WIN32 DVIGDI32.EXE
Yes Windows 95 WIN16 DVIGDI16.EXE
Yes " WIN16 DVIGDI32.EXE
No " WIN32 DVIGDI16.EXE
Yes " WIN32 DVIGDI32.EXE
Yes Windows NT WIN16 DVIGDI16.EXE
Yes " WIN16 DVIGDI32.EXE
Yes " WIN32 DVIGDI16.EXE
Yes " WIN32 DVIGDI32.EXE

19.5  Programming a DDE Client

Adding DDE client features to your C application requires a small amount of new code: a few extra lines in your message-handling switch, and some short functions. TRUETEX uses ``raw DDE'' and your DDE client will be most economical in doing likewise, rather than using the Windows DDEML (DDE Management Library DLL) or the ``standard DDE'' library found on the Microsoft Develeper Network. Raw DDE is appropriate, since DDE execute strings do not involve hot links, Clipboard data passing, or other advanced DDE features.

The paper titled, ``Raw DDE'' on the Microsoft Developer Network is an excellent reference on this programming subject. The sample application ``DDEPOP1'' in Petzold's book Programming Windows 3.1 gives a complete example of programming a DDE client. Note that this sample, however, does not provide for the WIN32 API.

Below are code excerpts from ddetst.c, a DDE client test application which compiles with Visual C++ (both WIN16 or WIN32 APIs, via a small amount of conditional compilation). This source code is available on DDETST.ZIP on the TRUETEX Setup disks (you must manually install this), or from our Web page (http://www.truetex.com). The excerpts below give only the code peculiar to raw DDE and omit the ``boilerplate'' Windows application code.

...
#include <dde.h>
...
HWND hServerWnd;                /* DDE server window                        */
...
/* We maintain a state variable to track the progress of a DDE execute      */
/* string conversation from initiation, to execution, to termination.       */
#define IDLE 1                  /* Idle, awaiting user input                */
#define INITIATING 2            /* Sent initiate, awaiting server ACK       */
#define EXECUTING 3             /* Sent execute, awaiting server ACK        */
#define TERMINATING 4           /* Got server's exec ACK, terminating       */
int ConversationState = IDLE;

    case WM_DDE_ACK:
            /* DDE server response.  The ACK for both the        */
            /* initiate and the execute arrive here, so we       */
            /* must take care to discriminate between them       */
            /* by examining the transaction state.               */

            /* The first ACK we might get is the server's        */
            /* response to our initiation.                       */
            if (ConversationState==INITIATING) {
                ConversationState = EXECUTING;
                hServerWnd = (HWND) wParam;
                DoDDE(
                        "[Open(\\\\kcc\\\\chk\\\\4192.dvi)]"
                        "[Open(\\\\kcc\\\\chk\\\\4196.dvi)]"
                        "[Open(\\\\kcc\\\\chk\\\\4197.dvi)]"
                        "[Open(\\\\kcc\\\\chk\\\\4198.dvi)]"
                    );
                ConversationState = TERMINATING;
                TermDDE();
                ConversationState = IDLE;
                }
            /* Otherwise we have the ACK for the execute;      */
            else if (ConversationState==EXECUTING) {
                /* Here we could examine the return status     */
                }
            /* This case is an error of synchronization        */
            else {
                }
            break;


void
IniDDE(void) {
    /* Initialize conversation with DDE server                 */
    ATOM app, topic;
    app = GlobalAddAtom("TrueTeX");
    topic = GlobalAddAtom("View");
    SendMessage(HWND_BROADCAST,WM_DDE_INITIATE,(WPARAM)hMainWnd,
            MAKELPARAM(app,topic));
    if (app) GlobalDeleteAtom(app);
    if (topic) GlobalDeleteAtom(topic);
    }

BOOL
DoDDE(char *cmds) {
    /* Send DDE commands to DDE server.  Return whether apparently */
    /* successful.                                                 */
    HGLOBAL hCmds;
    char FAR *p;
    if (hServerWnd==NULL) {
        MessageBox(hMainWnd,"Cannot connect to DDE server!",Title,MB_OK);
        return(FALSE);
        }
    hCmds = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE,strlen(cmds)+1);
    if (hCmds==NULL) {
        MessageBox(hMainWnd,"No memory for DDE!",Title,MB_OK);
        return(FALSE);
        }
    p = GlobalLock(hCmds);
    if (p==NULL) {
        GlobalFree(hCmds);
        MessageBox(hMainWnd,"No memory for DDE!",Title,MB_OK);
        return(FALSE);
        }
    lstrcpy(p,cmds);
    GlobalUnlock(hCmds);  p = NULL;
    if (!PostMessage(hServerWnd,WM_DDE_EXECUTE,(WPARAM)hMainWnd,
#if _WIN16
            MAKELPARAM(0,hCmds)
#elif _WIN32
            (LPARAM)hCmds
#endif
            )) {
        GlobalFree(hCmds);
        MessageBox(hMainWnd,"Cannot connect to DDE server!",Title,MB_OK);
        }
    return(TRUE);
    }

void
TermDDE(void) {
    /* Terminate conversation with DDE server                      */
    if (hServerWnd==NULL) return;
    PostMessage(hServerWnd,WM_DDE_TERMINATE,(WPARAM)hMainWnd,0L);
    hServerWnd = NULL;
    }


NEXT HEAD