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 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.
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).
Finds out which DDE servers are currently running and returns a collection of the server names. The collection is an array of strings.
Finds out what topics are supported by a running DDE server. The return value is an array of strings which are the topic names.
Open a DDE conversation for a specific Server and Topic.
See the DDE Conversation class for other ways to
open a conversation.
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.
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.
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.
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 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.
This property is the DDE service name. It can be read at any time, but only modified when the conversation is not open.
This property is the DDE topic name. It can be read at any time, but only modified when the conversation is not open.
This property controls the timeout (in milliseconds) used for the DDE transactions that occur the conversation.
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.
Checks if the conversation is currently open. Returns True
or False
.
Closes the DDE conversation. If the conversation is not open then the operation has no effect.
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.
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.
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.
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.
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.
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.
Dim oDDEClient Set oDDEClient = CreateObject("DDECOMClient.DDEClient")
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
Dim strValue strValue = oDDEClient.RequestTextItem("PROGMAN", "PROGMAN", "Accessories")
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()
Dim oDDEConv Set oDDEConv = GetObject("ddelink://PROGMAN|PROGMAN") Dim strValue1, strValue2, strValue3 strValue1 = oDDEConv.RequestTextItem("Accessories") strValue2 = oDDEConv.RequestTextItem("Games") strValue3 = oDDEConv.RequestTextItem("Startup")
Dim oDDEConv Set oDDEConv = GetObject("ddelink://Excel|[Book1]Sheet1") oDDEConv.PokeTextItem "R1C1", "a value"
Dim oDDEClient Set oDDEClient = CreateObject("DDECOMClient.DDEClient") oDDEClient.ExecuteTextCommand "Excel", "[Book1]Sheet1", "[App.Maximize()]"
This COM component is freeware - you get what you pay for, nothing more, nothing less.
The full source code (C++) is available from my web site listed below.
Email: gort@cix.co.uk
Web: www.chrisoldwood.com
Chris Oldwood
24th November 2022