Herman Rodent
Microsoft Developer Network Technology Group
Created: November 16, 1992
Click to open or copy the files in the DDEExec sample application for this technical article.
The dynamic data exchange (DDE) protocol includes a feature designed to allow a DDE client application to send commands to a DDE server. The syntax of these execute requests and the way they should be handled by servers is not well documented and, consequently, varies slightly with different implementations.
The current DDE protocol has no provision for returning result information from an execute request, so a client application can only tell that the request failed, not why it failed.
This article proposes a consistent way to handle DDE execute command requests and a mechanism that allows result information to be returned. This article covers the following points:
DDEExec, the sample DDE server application included with this article, implements the proposals detailed here. DDEExec uses a single module of code that you can include directly in your own server application. The glue module (STDDDE.C) provides all the System topic support, and includes a complete execute command parser and a set of functions to radically simplify the creation of a DDE server. The sample uses the dynamic data exchange management library (DDEML) to provide the basic DDE protocol support. DDEML provides all the support a DDE application needs, eliminating the need to implement the DDE message protocol directly.
The DDE protocol includes a feature called DDE execute that is designed to allow a DDE client application to send commands to a DDE server. This feature is supported by many full DDE servers such as Microsoft® Excel. It is also supported by applications such as the Microsoft® Windows™ operating system Program Manager, even though the Program Manager is not a regular DDE server. The sample code provided with this article makes it easy to provide execute command support in a DDE server. The DDEExec sample uses a single module of code to implement the basic server and includes full support for the System topic and for execute commands. This implementation uses a list to describe which commands are supported and an application programming interface (API) to allow commands to be added to and removed from the list, making it easy to implement the command set you need. For more information about the System topic, see the "Supporting the DDE System Topic" technical article on the Microsoft Developer Network CD (Technical Articles, Windows Articles, OLE and DDE Articles).
Those applications that support DDE execute commands typically have slight variations in how they interpret the command strings and use different techniques for allowing the inclusion of special characters, such as quotation marks, in the argument strings. This article and its associated sample application propose a consistent way of parsing the commands and support a flexible method for including special characters in execute command strings.
When a server fails to execute a command for whatever reason, it currently has no way to return error information to the calling client. This article also includes a proposal for an extension to the DDE protocol called the "Execute Control 1" protocol. This protocol provides a way for a DDE server to return an information string in response to executing a command.
The syntax of the commands placed in DDE execute requests has evolved over time, with a large part of the definition coming from the earlier versions of Microsoft Excel. The DDE protocol itself has only a simplistic syntax definition for commands. More recently, other applications such as the Windows Program Manager have taken to supporting a limited set of DDE execute commands, and these applications have typically used a syntax similar to the one adopted by Microsoft Excel. The sample code with this article supports and enforces the use of the Microsoft Excel syntax.
Table 1 shows the syntax of the execute command strings. This is intended as an aid to understanding the syntax, rather than as a formal definition. Please note that unless your application uses the sample code included with this article, all parsing of the execute strings is done by your code to your own conventions. The format of these strings is a convention only and is not enforced by any feature of the system.
Table 1. The Syntax of Execute Command Strings
Name | Definition | Comment |
execute_string | : command+ | An execute request consists of one or more commands. |
command | : '[' command_name ']' | '[' command_name args ']' |
Each command is enclosed in square brackets. A command may have an optional argument list. |
command_name | : grchar+ | A valid command name consists of one or more alphanumeric characters. |
grchar | : [a-zA-Z0-9!#$%^&()-_{}~] | The set of legal MS-DOS® file system characters. |
args | : '(' arglist ')' | Command arguments are contained within parentheses. |
arglist | : arg | arg ',' arglist | |
An argument list consists of zero or more arguments separated by commas. |
arg | : '"' string '"' | string | |
An argument consists of a string or a string enclosed in double quotation marks. The argument may be empty. |
string | : grchar* | A series of valid string characters. |
The following set of examples are all valid command strings:
[pen(red)]
[pen("red")]
[text(5,8,"Hi there Herman!")]
[Erase()]
[Erase]
Note that when the command has no arguments (the Erase example above), it is valid to use only the command name or the command name with an empty set of parentheses. Since all arguments are treated as strings, using the empty set of parentheses results in the command being parsed as though it has one argument that consists of a string of zero length. This case is easily handled by the code that handles this command.
Arguments may be simply omitted by supplying a zero-length string. The following example shows a command that takes three arguments, and the second argument has been omitted:
[ThreeArgCommand("arg 1",,"arg 3")]
Commands may be concatenated to form a single execute-request string. The commands will be executed in the order they occur in the request. For example, the following sequence of commands might be used to make a selection and copy it to the Clipboard:
[Select(all)][Copy]
Characters that are not alphanumeric—quotation marks, tabs, and so on—can be embedded in the argument strings by using the backslash character (\) as an escape code. Quotation marks can also be included by inserting them as pairs. Both Microsoft Excel and Microsoft Visual Basic™ support inclusion of quotation marks as pairs. The backslash escape mechanism (as used in C strings) gives complete flexibility. The examples in Table 2 show the command string and resulting text.
Table 2. Examples of Embedding Special Characters in Argument Strings
Argument | Resultant string |
"""Woof"", said the big dog." |
"Woof", said the big dog. |
"\"Woof\", said the big dog." |
"Woof", said the big dog. |
The sample code supports both of the techniques illustrated in Table 2. When a function's arguments are parsed, the enclosing quotation marks (if used) are stripped off and any escape codes are removed before the argument is passed to the appropriate function.
Note Because the escape characters are stripped from the strings, you cannot include the \0 (null) character in a string. Doing so effectively truncates the string.
The existing DDE protocol provides no way for a server to return information in response to a request to execute a command. Because of this, it is very difficult for a DDE client to determine why an execute request has failed. This section proposes a protocol to allow result information to be returned to the client.
Because this is only a proposal for a protocol, it has not been fully implemented in the sample source code. The sample code has a reduced protocol that shows the principle of the protocol, but it has several major flaws. It doesn't take conversation instances into account, so it is possible for one client to execute a command and the result string to be sent to a different client. It also does not keep the Result item active so it can have multiple results concatenated to it.
This section describes the proposed protocol for returning error information from execute requests.
When the client wants to be able to retrieve result information from the server, it tells the server that it would like to have this information saved in a named DDE item. It does this by sending a special execute command naming the Result item, followed by the command for which it wants to retrieve the result information. For example, let's say we are about to make a request to open a file and want to know if it succeeds or, if not, why it failed. To do this, we would send the following DDE execute command:
[Result(OpenResults)][Open(bogus.dat)]
The Result command tells the server to create a special temporary DDE item, called OpenResults, under the current conversation. When the Open command is executed, its result information is copied to the OpenResults item. When the command request is complete, the client can ask the server for the contents of the OpenResults item. The server will return the string returned by the Open command, and then delete the special temporary OpenResults item. The Visual Basic sample, EXEC, demonstrates this process. Try entering this command:
[pen(bogus)]
Observe that the server returns a result string informing the client that the selected color is invalid. EXEC supports the protocol to the limited extent discussed in the "A Warning About the Sample Code" section.
For the returning of execute information to be as efficient as possible, the protocol should accommodate multiple commands in a single request and allow the server to return all the result information in one string. This makes for much less DDE traffic and simplifies things at the client end of the conversation. In addition, it helps if there is some defined format to the returned data so that the client can parse it in a simple and consistent way. Follow these rules when implementing the complete protocol.
1.When a conversation is initiated, the Result item is set to NULL, and result reporting is disabled for the topic.
2.When a client issues a Result command for the topic, any existing Result item data is freed, and the previous item is deleted. A client may disable result reporting by issuing the Result command with no item name:
[Result]
or
[Result()]
If the name of the item given in the Result command is the same as an existing item for the topic, the existing item should be suspended while the Result item is active. When result reporting is turned off, the original item is reactivated. Suspending an existing item is a choice that the client makes. The client can enumerate the current list of items for the topic, so there is no reason for it to override an existing item without being aware that it is doing so.
3.Once a Result command is active, it remains active until the conversation is ended or a new Result command is issued.
4.Whenever the server needs to return data in response to an execute command, it concatenates the new data onto the end of any existing Result item data. Each item is terminated by a fixed sequence. The default termination sequence is \r\n (carriage return, line feed).
5.The set of characters allowed in result strings is limited to printable characters plus \r, \n, \t, and space. The server should support the SetResultDelimiter command to allow a different delimiting sequence to be set, and GetResultDelimiter to allow the current sequence to be read.
6.Result strings that are returning error information should default to the form:
ERROR:#### --- <text>
This allows all clients to simply parse a result string and detect that an error occurred. The format of the result string is by default "ERROR:%d --- %s" (in printf style) and may be changed if the server supports a SetErrorResultFormat command. The server may also support a GetErrorResultFormat command to allow interrogation of the current format string.
7.The result string is reset to empty whenever the Result item is advised or requested by a client.
8.Result links can be updated by the server after each command is executed or after the entire string of commands is executed. Note that in raw DDE applications this can mean that the server posts one or more WM_DDE_DATA messages (containing the result information) before the WM_DDE_ACK for the execute command. Applications using raw DDE need to be coded carefully to cope with this. Applications using DDEML will not see this problem.
9.Clients should ensure that they close any links established with Result items before changing the Result item. This will ensure that the client cannot receive result information from an existing item that was temporarily overridden by a Result item of the same name.
Any server that supports returning information with the protocol described above should also support the Protocols item under the System topic by returning the "Execute Control 1" protocol string. DDEExec includes this support. Note that this is implemented in the sample code by providing it in the System topic Protocols item by default. If you decide to provide your own support for the Protocols item, you must include the "Execute Control 1" string as one of the supported protocols. In addition, the graphic command set supported by the sample is in itself a protocol of the server, and a name for this protocol should also be included in the server Protocols item. A typical name for this protocol might be "Microsoft DDEServ Graphic Protocol 1." Please refer to the source code in STDDDE.C for more details about how the Protocols item is implemented.
Once again I want to emphasize that this is a proposal for a protocol and nothing more. Currently no applications support this protocol.
Applications that register a DDE window class that is Unicode must use Unicode execute strings when communicating with other Unicode DDE windows. They must use ASCII (OEM codepage) execute strings when communicating with non-Unicode DDE windows. Any window's state can be determined by calling IsWindowUnicode. This convention places the burden of backward compatibility on Unicode DDE applications.
Unicode compatibility is only a problem for raw DDE applications because DDEML automatically translates execute strings. DDEML bases its translation decision on how DDEML was called—either by DdeInitiateA or DdeInitiateW—when the application first initialized the conversation.
This technical article includes a number of sample applications.
The DDEExec sample is a DDE server implemented using the STDDDE.C module to do most of the work and DDEML to provide the DDE protocol implementation. The server supports a graphic command set, which allows a client to draw simple pictures. Table 3 lists the commands supported by the server.
Table 3. The Command Set Supported by DDEExec
Command | Description |
Pen(color) | Select the pen and text color. |
Brush(color) | Select the brush and text background color. |
Line(x1,y1,x2,y2) | Draw a line from (x1,y1) to (x2,y2) using the current pen color. |
Ellipse(x1,y1,x2,y2) | Draw an ellipse bounded by the rectangle whose top-left corner is (x1,y1) and whose bottom-right corner is (x2,y2). The ellipse is drawn with the current pen color and filled with the current brush color. |
Rectangle(x1,y1,x2,y2) | Draw a rectangle whose top-left corner is (x1,y1) and whose bottom-right corner is (x2,y2). The rectangle is drawn with the current pen color and filled with the current brush color. |
Erase() | Erase the drawing area. |
Text(x,y,text) | Write text starting at (x,y) using the current pen color for the text color and the current brush color for the enclosing rectangle. |
Valid colors are red, blue, green, black, white, yellow, cyan, magenta, and gray.
The command set in Table 3 is in itself a protocol and, therefore, should really be listed under the System topic Protocols item. The Windows version 3.1 registry keeps track of which protocols have been registered by applications. A discussion of the registration database is outside the scope of this article.
BUNNY is a Visual Basic 2.0 program that draws a picture of a bunny. Gripping stuff, eh? WBUNNY.DOC is a Word for Windows version 2.0 document that uses the WBUNNY.DOT template to draw another bunny, this time in a kayak. XLART.XLM is a sample Microsoft Excel version 4.0 macro that draws a rather boring picture. EXEC is a Visual Basic 2.0 program that allows you to type a DDE command and send it to the server. EXEC shows the result text sent back by the server in response to the command. Try using EXEC to send some of the following commands to see what effect they have:
[Erase][pen(red)][line(0,0,50,50)]
[snarfbuggle]
[pen(bogus)]
[pen(]
STDDDE.C is a code module you can include in your own application to transform it into a DDE server. STDDDE.H is a header file that defines all the structures used and the functions supported by STDDDE.C. Look for the "Quick and Easy DDE Server" technical article, which describes how this module is used to create a DDE server, on the Microsoft Developer Network CD (Technical Articles, Windows Articles, OLE and DDE Articles)—or just browse the DDEExec sources to see how STDDDE is used.
Because this article is a mixture of DDE, DDEML, proposals, source examples, and so on that can be confusing, let me try to summarize it all for you.
DDE is a protocol based on a special set of Windows-based messages that allows applications to exchange data and, in the case of this article, commands. DDEML is a dynamic-link library that implements the DDE protocol and provides a programming interface to applications to greatly simplify the addition of DDE features to an application. Any application written from today on should use DDEML rather than implement the DDE protocol itself.
The code in the STDDDE.C module of the samples with this and other DDE articles is my own rendition of what I think a decent DDE server should be like. No hard and fast rules govern this—only convention. What I'm trying to do with the sample code is make it easier for you to do a good job. I'm not interested in restricting what you want to do. I want to encourage you to provide what I consider to be the minimum level of support for a DDE server and to do it in a way that looks consistent to client applications.
This article and other related articles on the Developer Network CD make certain proposals about how DDE might be further developed. These are proposals only. The purpose of the proposals is to help the creators of DDE servers in the world work toward providing better, more consistent services in their applications.