dde

DDE (Dynamic Data Exchange) is one Microsoft way of communicating between applications that support that (e.g. Word does, but Wordpad/Notepad don't). Messages are strings that express commands. It looks like for Word the commands have to be bracketed, so put braces around to prevent the Tcl parser from seeing them. Tcl (for Windows) of course has dde support - see the dde page in Tcl Help.

http://www.purl.org/tcl/home/man/tcl8.5/TclCmd/dde.htm

dde servername ?-force? ?-handler proc? ?--? ?topic?
dde execute ?-async? service topic data
dde poke service topic item data
dde request ?-binary? service topic item
dde services service topic
dde eval ?-async? topic cmd ?arg arg ...?

tclguy writes during March of 2002 on comp.lang.tcl:

"... I wanted to get time to answer this earlier, but you eventually hit on it yourself. DDE requires an event loop to be running to service requests. DDE is based on a serial request / response mechanism, which you can hang up when a process isn't responding. A Tcl process that sees no reason to have an event loop running will also not queue into the fact that DDE requests are waiting. This is why you see the problem in Tcl apps mostly, and not Tk apps (which need the event loop for the UI)."


BR 2003-10-28 - This leads directly to the biggest problem with DDE (IMO).

During connection setup, a straight-forward DDE client will send a broadcast (without a timeout) to all threads that have an event queue. (On Windows all threads that have ever processed events in the past have an event queue, all others don't have one.)

The problem: Any thread that has an event queue but is currently not servicing it for any reason will block the broadcast. This will block the client, and the effect can propagate to its clients. I have had very bad experiences with this in real life.

PT 11-Mar-2004: This issue has been resolved for Tcl 8.5 by using SendMessageTimeout to perform this step. This results in badly behaved applications not appearing in the list of available services but also not hanging the DDE client app.


More commentary: DDE's a funny creature. Deprecated since 199? [document], everyone knows it's preferable on all sorts of grounds to use a COM interface--but Tcl still, as of 2004, builds in the DDE package, but does not do the same for any COM facility.


From a posting by Bill Schongar, at http://groups.google.com/groups?oi=djq&ic=1&selm=an_490725194 , you can control Microsoft Word from the dde command. First: be sure Word is running, then try a command like the following to insert text into the current document:

     package require dde

look at CWind:but it does the work."

     dde execute -async Winword System {[Insert "Text from Tcl."]}

To quit Word, or close a document, use the following commands as a guide:

     dde execute Winword System {[FileExit 2]}

     dde execute Winword System {[FileClose 2]}

In these examples, 2 = close without saving, 1 = save first, 0 = prompt.

For documentation on accessing Word, see the "wrdbasic.hlp" file, which you can choose to install when you install Word.

Adrian Davis adds that

    dde execute Winword System {[AppMaximize 1]}

maximizes Word. - RS also found these to work:

 % dde execute  Winword System {[EndOfDocument]}
 % dde execute  Winword System {[StartOfDocument]}
 % dde execute  Winword System {[EditFind .Find="hello" .PatternMatch=1]}

See also Word Reaper.


Peter G. Baum presented this example for MATLAB:

 dde execute Matlab Engine "s = ones(5);"

CL's favorite DDE baby steps are with IE, because it's even more ubiquitous than Word or Excel. Moreover, the simplest possible IE example also illustrates a significant difficulty in working with DDE.

To open a web page in Internet Explorer:

    dde execute iexplore WWW_OpenURL "http://www.tcltk.com/"

See http://support.microsoft.com/support/kb/articles/Q160/9/57.ASP?LN=EN-US&SD=gn&FR=0 for various dde commands supported by internet explorer. It would be nice to transcribe some of them into Tcl for this page.

    dde execute iexplore WWW_ShowFile C:/Program%20Files/....

will open a local file (note that spaces and other odd characters must be converted to valid '%' codes).

    dde request iexplore WWW_GetWindowInfo 1

will return information about the current window which is open, although if multiple windows are open, I'm not sure how to control this more accurately.

    dde request iexplore WWW_ListWindows 0

seems to do something...

Vince.

(any help with above? In particular, it would be useful to be able to grab the URL of the foremost window.-)

All of the above experiments should "work" reliably, in that, from the user perspective, IE indeed acts as described. However, back in the Tcl process, [dde] typically throws a "remote server cannot handle this command" exception. That's because, in KBK's analysis, DDE gives no "way to distinguish 'result expected, but the server failed to provide it' from 'no result is expected'." The only way not to receive a DMLERR_NOTPROCESSED is to invoke "dde exec -async ..."

Using dde to get browser info - A little Web kiosk


Finally, Eudora seems to have some support:

   {Eudora DeleteMessageById} {Eudora PutMessageById}
   {Eudora GetMessageById} {Eudora GetNextMessageId}
   {Eudora ExpandNickname} {Eudora MAPI}
   {Eudora WWW_OpenURL}

But I'm not sure how to make use of them...


Controlling Windows explorer:

To open a folder on the desktop:

    dde execute Folders AppProperties{[ViewFolder "C:\Temp","C:\Temp",5)]}

you can also use 'ExploreFolder' instead.

To open the Find Folder dialog:

    dde execute Folders AppProperties {[FindFolder("","C:\Temp")]}

will open the Find Folder dialog.


Re MS Excel, George Petasis writes in comp.lang.tcl: Finally, I manage to find an example at msdn. My only problem now is how to select the chart type of the created chart. Acoording to the example:

 dde execute Excel $Topic \
 "\[Select(\"R1C1:R${rows}C1\;R1C5:R${rows}C$cols\")\]\[New(2,2)\]"

The chart is created with New(2,2), after the correct cells are selected. This chart is the vertical bars type and is placed in a new book. How can I select the scatter graph and place it in a new sheet of the first book (referenced by $Topic)? Where can I find documentation about this New dde function?


Helpful procedures (Vince) [Someone please annotate these]:

    proc windowsDdeExecute {service topic name args} {
        set cmd "\[$name\("
        set subcmds [list]
        foreach a $args {
            lappend subcmds "\"$a\""
        }
        append cmd [join $subcmds ","] "\)\]"
        puts stderr "dde execute $service $topic $cmd"
        dde execute $service $topic $cmd
    }

    proc windowsProgmanExecute {name args} {
        eval [list windowsDdeExecute PROGMAN PROGMAN $name] $args
    }

A recent posting in news:comp.lang.tcl says:

 Message-ID: <[email protected]>
 From: Bruce Hartweg <[email protected]>
 Newsgroups: comp.lang.tcl
 Subject: Re: Q: dde and Netscape
 References: <[email protected]> <[email protected]> <[email protected]> <[email protected]>
 Date: Wed, 23 May 2001 09:20:08 -0500
 Organization: Raytheon Company

 Neil Madden wrote:

 > "Donal K. Fellows" wrote:
 > >
 > > Bruce Hartweg wrote:
 > > > change wrote:
 > > >> However, if I use
 > > >>    dde execute -asynce netscape WWW_OpenURL http://www.yahoo.comm
 > > >> the netscape does nothing (no error message either). Could someone help me?
 > > [...]
 > > > It's been a while, so I am not sure (can't find my script where I used this)
 > > > but I think you need to use dde request  instead of dde execute.
 > > >
 > > > For reference of what DDE Topics Netscape supports see
 > > > http://developer.netscape.com/docs/manuals/communicator/DDE/contents.htm
 > >
 > > Hmm.  The following URL (hopefully not mangled by this newsreader) looks
 > > to be the key:
 > > http://developer.netscape.com/docs/manuals/communicator/DDE/ddevb.htm#www_openurl
 > >
 > > So, I *guess* that it should be something like this:
 > >
 > >   dde request netscape WWW_OpenURL http://www.yahoo.com 0 0
 >
 > Doesn't work for me on Win98, I get:
 > wrong # args: should be "dde request serviceName topicName value"
 >
 > Removing the "0 0" from the end and it works as expected.
 >

Yes, (it's coming back) the value has to be a single string, if you want to pass other args to the service separate by commas & keep as single arg (add commas to skip items) so

    dde request netscape WWW_OpenURL "${url},,0"

will open the URL in a new window, and

    dde request netscape WWW_OpenURL "${url},,0xFFFFFFFF"

will open it in the last active window

Bruce


How, though, does one use -ddename? How, in particular, can one Tk instance spawn and control another? Jeffrey Hobbs posted an example in August 2001:

      exec wish83.exe myfile.tcl -ddename bar -mynameis foo &
      dde eval bar $command

Notice that the spawned interpreter can itself

      dde eval foo $command2

in this context.

As Jeff writes, "Also, to find existing names, you just do:

        dde services TclEval {}

and that is like 'winfo interps'."


Which applications are running?

      dde services "" ""

can tell you.


From news:comp.lang.tcl , Oleg Oleinick writes:

To take control over DDE, application can start DDE server in the slave interpreter.

Following example prevents application to start twice:

   package require Tk
   package require dde

   # dde dispatcher
   proc ddecommand {command} {
       switch -- $command {
           "raise" {
               raise .
               focus -force .
           }
       }
   }

   # Check for running instance
   if { [llength [dde services TclEval MYAPP]] } {
       # Application already run, raise it
       dde eval MYAPP catch raise
       exit 0
   }

   # Create slave intepreter 'ddeagent'
   if { [lsearch -exact [info loaded] {{} Dde}] >= 0 } {
       # Statically linked dde, we can use safe interpreter
       interp create -safe ddeagent
       interp invokehidden ddeagent load {} dde
   } else {
       interp create ddeagent
       interp eval ddeagent package require dde
   }

   # Create new command raise in slave interpreter
   interp alias ddeagent raise {} ddecommand raise

   # Create dde server in slave interpreter
   interp eval ddeagent dde servername MYAPP

   focus -force .

Oleg


Netscape provides a list [L1 ] of DDE things known to Navigator.


Iain B. Findleton has coded a DDE 2.0 he's currently using, although not yet released. Also, Iain has found that it's safest to connect only to active services; Win* seems to misbehave uncontrollably when one tries to connect to an absent service.


It is often useful to be able and use a context menu option to either launch a Tcl application on a file, or if the Tcl application is running have it handle the launched file. An example of registering the handler is shown in regContextHandler - TFW


Applications inaccessible through DDE often can be reached by way of COM and SendKeys. For even more power and flexibility, Stefano Porici advises, "If you're interested in sending key sequences and exchanging data through the clipboard (a powerful combination anyway), you can give a

http://www.interq.or.jp/japan/s-imai/tcltk/cwind.html

this is a loadable DLL with a very simple Tcl interface. A more powerful interaction model (allowing mouse movements) is provided by WinTclSend ..."


See send for a Windows emulation with DDE.


[We need a comparison with tcom, OpTcl, ...]


How to Restrict The List of Exposed Commands

By default the dde package, when used as a server, exposed all of the commands and variables available in the interpreter. This is not always what is desired. One solution might be to load the package into a safe slave interpreter and use [interp alias] to expose the required commands. Unfortunately the package doesn't support safe interpreters. A second solution is to build a modified dde package - see dde restriction for more. PT

Associating a Windows file type with a Tcl application

With an implementation of TIP #120 [L2 ] it is possible to have Windows Explorer launch a Tcl application for a file type. See ddeexec


How get data in and out of Excel

 # dde request Excel System SysItems 
 # dde request Excel System Topics
 # # Create a new book
 # dde execute Excel System {[NEW(1)]}
 # dde poke Excel {[Book1]Sheet1} "R1C1" 4
 # dde request Excel {[Book1]Sheet1} "R1C1"
 # # Select Row 1 Column1 to Row 20 Column 3
 # dde execute Excel System {[select("r1c1:r20c3")]}
 # # Sort Row 1 Column 1
 # dde execute Excel System {[sort(1,"R1C1",1)]} 
 # # Quit
 # dde execute Excel System {[quit()]}
 # # Get a list of books and sheets
 # dde services Excel {}

  # To load an existing .xls file in the current directory
  set cwd [pwd]
  regsub "/" $cwd "\\" cwd
  exec {C:/Program Files/Microsoft Office/Office/Excel.exe} &
  catch {dde execute Excel {[Book1]Sheet1} "\[open(\"$pwd\\$fileName.xls\")\]"}

 package require dde

 # bookAndSheet like {[Book1]Sheet1}

Starting at Row=1 and Column=1 and assuming data is a list of rows and each row is a list of values.

 proc excel_export {data bookAndSheet} {
    set row 1
    set col 1
    foreach d $data {
       foreach v $d {
          dde poke Excel $bookAndSheet  "R${row}C${col}" $v 
          incr col
       }
       incr row
       set col 1
    }
 }

 # bookAndSheet like {[Book1]Sheet1}

Imports a square of values from r1 (row 1) to r2 (row 2) and from c1 (column 1) to c2 (column 2). Returns a list of rows. Each row is a list of values.

 proc excel_import {r1 c1 r2 c2 bookAndSheet} {
    set ret {}
    set row $r1
    set col $c1
    for {} {$row <= $r2} {incr row} {
       for {set col $c1} {$col <= $c2} {incr col} {
          lappend rowData [string range [dde request Excel $bookAndSheet "R${row}C${col}"] 0 end-2]
          # puts "$row $col $rowData"
       }
       lappend ret $rowData
       set rowData {}
    }
    return $ret
 }

Earl Johnson ([email protected]) (Loading an existing xls file added by CF)


A little tip from Ro if you want to control Mozilla via DDE:

Mozilla registers the DDE application "Mozilla" and currently supports only (as of Sept 7th, 2002) the "WWW_OpenURL" topic.

Use the source @ http://lxr.mozilla.org/seamonkey/source/xpfe/bootstrap/nsNativeAppSupportWin.cpp

2012-09-18 hae Using Mozilla Firefox 14.0.1 the browser registers as 'FireFox'.

# Open a website in a new tab if firefox is already started.
dde request FireFox WWW_OpenURL http://www.google.com
# Always open a website in a new window
dde request FireFox WWW_OpenURL http://www.google.com,0,0

    package require dde
    puts "Currently displayed is '[lindex [dde request iexplore WWW_GetWindowInfo 0] 0]'."

Some examples of using dde with Microsoft Access - see also [L3 ]

   dde request MSAccess {c:\samples\NWIND.MDB} tablelist

gives a list of available tables

  • Reportlist - a list of reports
  • Querylist - a list of queries
  • Formlist - a list of forms
  • Macrolist - a list of macros

-To access data in a table

   dde request MSAccess {c:\samples\NWIND.MDB;TABLE tblBooks} All

gives all entries in table with field names

   dde request MSAccess {c:\samples\NWIND.MDB;TABLE tblBooks} Data

gives entries without field names

   dde request MSAccess {c:\samples\NWIND.MDB;TABLE tblBooks} FieldNames

gives just the field names

-To access data from a query

   dde request MSAccess {c:\samples\NWIND.MDB;QUERY qryCatagories} All

gives all rows of a query


It is interesting to see all that you can do with DDE in other applications, but may I suggest a list of ideas of what kind of services a Tcl application can make available to other (Tcl or not) applications through dde: Serving dde


While DDE has a notion of "Advise" [document], Tcl's standard DDE package does not provide a correlative for Advise.


ALX: Howto open PDF with Adobe Acrobat or Reader using DDE


MG I've just experimented a little in setting up a DDE server (to make a Tcl program the default telnet client, in my case, but you can use it for handling any file extension). Couldn't find a whole lot of info on what settings were needed in Windows itself, to make it work with the Tcl script, but with a little trial and error I think I've got it figured out, and thought I'd share.

The first thing I did was set up a really simple script, using Tcl 8.5:

  package require Tcl 8.5
  package require dde 1.3

  dde servername -handler myHandler -- myApp

  proc myHandler {args} {
    puts $args
  }

  console show

Then made it into a Starpack (see How to create my first Starpack) and placed it as c:\ddetest.exe

Then it's just a case of setting up the file extension settings in Windows. In any folder, go to Tools -> Folder Options -> File Types, select the file type/extension you want, and change it's "open" action to use:

  Application used to perform the action:
  c:\ddetest.exe
  Use DDE:
  Yes
  DDE Message:
  %1
  Application:
  TclEval
  DDE Application Not Running:
  (leave blank)
  Topic:
  myApp

That's all I did, and it worked for me. The first time I tried to access a Telnet url, it launched the exec and printed the URL to the console. Subsequent attempts printed the URL in the same console, without relaunching the app. You could probably avoid having to make a Starpack if you changed the "Application used to perform the action" to be something like

  "c:\path\to\wish.exe" -- "c:\path\to\tclscript.tcl" 

though I've not tried it.

Updated a bit later: it seems I didn't notice that Windows (XP, at least) sends the dde request immediately after launching the "Application used to perform the action", when it's not already running, so if you pass the info on the command line and check for it there, too, you end up processing it twice.


Using the Tcl DDE package on a PC with XP system ( having codepage 1252 ) for tranmitting 8 bit characters (e.g. c4) results in 2 hex values on the receiving side (e.g. c3 84) (it's a dymo labelprinter software) . Does the DDE package any code transformations by itself ? I believe cp1252 is unicode (?), there should be no need for converting anything. Is there any hind ? Thanks

RS cp1252 is almost the same as iso8859-1, an 8-bit encoding, with ASCII and Western European letters. Before you send out the string, convert it to cp1252 with

 set string [encoding convertto cp1252 $string]