DDE COM Client v1.1.2

Introduction

This is a COM component to allow scripting of DDE Servers. It is an Automation Server (i.e. programmable via IDispatch) that houses a number of COM classes. The first is a DDE Client, which is a Singleton style object that can be used to make ad-hoc DDE queries and querying for running DDE servers. The second is for a specific DDE Conversation and can be used as an optimisation when you need to query many values from the same DDE service and topic.

At present it only supports the requesting of data in CF_TEXT format, which is then converted to UNICODE to pass back over the COM interface. Most of the DDE servers I have dealt with were ANSI based and given the nature of scripting I felt this was probably a good first order approximation. If there is a need to allow access in other formats (e.g. CF_UNICODETEXT, CF_DIF, etc) I will add them to the API as and when the need arises.

It supports the main synchronous transaction types, i.e. reading (XTYP_REQUEST), writing (XTYP_POKE) and executing (XTYP_EXECUTE). The main feature of DDE that this component is missing is the asynchronous updating of values via advise loops (a.k.a. DDE Links). These have not been implemented as I didn't know if it was possible to handle COM events from scripting languages such as VBScript, which is its intended audience.

The component is implemented in C++ using COM dual interfaces, so also supports early binding. However, if you're using C++ you might be better off using my underlying NCL library/DDE classes directly.


The DDE Client Class

The DDE Client class is essentially a Singleton object that provides simple access to DDE servers. If all you need to do is query for a few values from a DDE source or two you can do it with repeated calls to RequestTextItem(). It also provides access to the Wildcard style request (XTYP_WILDCONNECT) so that you can query for running servers and their topics.

Property: long DefaultTimeout

This property controls the default timeout (in milliseconds) used for DDE transactions that occur on new conversations after it has been changed. The default timeout is set to 30 secs (i.e 30,000 ms).

string[] RunningServers()

Finds out which DDE servers are currently running and returns a collection of the server names. The collection is an array of strings.

string[] GetServerTopics(string service)

Finds out what topics are supported by a running DDE server. The return value is an array of strings which are the topic names.

IDDEConversation OpenConversation(string service, string topic)

Open a DDE conversation for a specific Server and Topic.

See the DDE Conversation class for other ways to open a conversation.

IDDEConversations Conversations()

Get the collection of open DDE conversations. This collection is enumerable using the "for each" idiom or can be indexed directly using the Item() method.

Note: The Item() method follows the VBScript and WMI convention of being 0-based.

string RequestTextItem(string service, string topic, string item)

Request the current value for a single item from a named service and topic. The item is requested in CF_TEXT (i.e. ANSI) format and then converted to UNICODE to pass back via COM as a string.

void PokeTextItem(string service, string topic, string item, string value)

Set the value of a single item for a named service and topic. The item will be passed in CF_TEXT (i.e. ANSI) format, even though it's being passed to the COM server via a Unicode string.

void ExecuteTextCommand(string service, string topic, string command)

Send a command string to the DDE server to be executed.

Note: The command is always sent in CF_TEXT for an ANSI build and CF_UNICODETEXT for a Unicode build. This is to work around a bug in Windows/DDEML that shows up when sending a CF_TEXT format command from a Unicode DDE client to an ANSI DDE server.

The DDE Conversation Class

The DDE Conversation class is really an optimisation (and an excuse for me to implement a COM collection) that avoids the overhead of creating a conversation every time an item is requested. If you have many values to request from a single Service|Topic pair you can open an explicit conversation either from the DDE Clients' OpenConversation() method, or use the "ddelink:" moniker namespace and then use that to request the items.

Property: string Service

This property is the DDE service name. It can be read at any time, but only modified when the conversation is not open.

Property: string Topic

This property is the DDE topic name. It can be read at any time, but only modified when the conversation is not open.

Property: long Timeout

This property controls the timeout (in milliseconds) used for the DDE transactions that occur the conversation.

void Open()

Opens the conversation specified by the Service and Topic properties. If the conversation is already open, an error is returned.

This method allows a DDE conversation to be opened by creating an explicit DDE Conversation object, setting the Service and Topic names and then calling the Open() method.

bool IsOpen()

Checks if the conversation is currently open. Returns True or False.

void Close()

Closes the DDE conversation. If the conversation is not open then the operation has no effect.

string RequestTextItem(string item)

Request the current value for a single item. The item is requested in CF_TEXT (i.e. ANSI) format and then converted to UNICODE to pass back via COM as a string.

void PokeTextItem(string item, string value)

Set the value of a single item. The item will be passed in CF_TEXT (i.e. ANSI) format, even though it's being passed to the COM server via a Unicode string.

void ExecuteTextCommand(string command)

Send a command string to the DDE server to be executed.

Note: The command is always sent in CF_TEXT for an ANSI build and CF_UNICODETEXT for a Unicode build. This is to work around a bug in Windows/DDEML that shows up when sending a CF_TEXT format command from a Unicode DDE client to an ANSI DDE server.

The DDE Conversation Moniker Namespace

WMI uses a moniker namespace of "winmgmts" to support simpler creation of WMI queries by encoding the query as a moniker item, which in VBScript you pass to GetObject(). You can achieve a similar effect with DDE Conversations by using the "ddelink" namespace. The full moniker consists of the "ddelink:" prefix, a "//" separator and the moniker item is the Service and Topic name in DDE Link format, e.g.

ddelink://SERVICE|TOPIC

This helpfully gives the link a URL like syntax, and when support for true links are added the syntax can be updated to allow a "!Item" style suffix.

Error Handling

All the COM objects support IErrorInfo, so you will receive textual error messages along with the result code. If the underlying error comes from DDE itself, then the symbolic name for the error code will be included (e.g. DMLERR_BUSY) which you can look up in the DDE SDK.


Examples

The following set of examples should cover the most common scenarios. They are written in VBScript as scripting was the reason I created the component. The initial example shows how to create the DDE Client, which is then assumed in the subsequent code snippets.

Creating the DDE Client
Dim oDDEClient
Set oDDEClient = CreateObject("DDECOMClient.DDEClient")
Listing the Running Servers and Topics
Dim astrServers, astrTopics
Dim i, j

astrServers = oDDEClient.RunningServers()

For i = LBound(astrServers) To UBound(astrServers)

    WScript.Echo "Server: " & astrServers(i)

    astrTopics = oDDEClient.GetServerTopics(astrServers(i))

    For j = LBound(astrTopics) To UBound(astrTopics)

        WScript.Echo "    Topic: " & astrTopics(j)

    Next

Next
Requesting a Single Item
Dim strValue
strValue = oDDEClient.RequestTextItem("PROGMAN", "PROGMAN", "Accessories")
Opening a Conversation
Dim oDDEConv
Set oDDEConv = oDDEClient.OpenConversation("PROGMAN", "PROGMAN")

or

Dim oDDEConv
Set oDDEConv = GetObject("ddelink://PROGMAN|PROGMAN")

or

Dim oDDEConv
Set oDDEConv = CreateObject("DDECOMClient.DDEConversation")

With oDDEConv
    .Service = "PROGMAN"
    .Topic   = "PROGMAN"
End With

oDDEConv.Open()
Requesting Items From One Conversation
Dim oDDEConv
Set oDDEConv = GetObject("ddelink://PROGMAN|PROGMAN")

Dim strValue1, strValue2, strValue3
strValue1 = oDDEConv.RequestTextItem("Accessories")
strValue2 = oDDEConv.RequestTextItem("Games")
strValue3 = oDDEConv.RequestTextItem("Startup")
Setting the Value For an Item On a Conversation
Dim oDDEConv
Set oDDEConv = GetObject("ddelink://Excel|[Book1]Sheet1")

oDDEConv.PokeTextItem "R1C1", "a value"
Sending a Command to a Running DDE Server
Dim oDDEClient
Set oDDEClient = CreateObject("DDECOMClient.DDEClient")

oDDEClient.ExecuteTextCommand "Excel", "[Book1]Sheet1", "[App.Maximize()]"

License & Warranty

This COM component is freeware - you get what you pay for, nothing more, nothing less.

Source Code

The full source code (C++) is available from my web site listed below.

Contact Details

Email: gort@cix.co.uk
Web: www.chrisoldwood.com

Chris Oldwood
24th November 2022