File RXDDE.TXT                                   Ch. d'H. Nov 11, 1992
Copyright (C) 1992 Inventec Informatik AG
All rights reserved.



RXDDE  -  DDE Interface for REXX
================================

The dynamic link library RXDDE.DLL provides a REXX interface for
Dynamic Data Exchange (DDE). This enables an OS/2 REXX program to
communicate with other programs through DDE links.

RXDDE provides both DDE client and DDE server services. The DDE
functions REQUEST, POKE and EXECUTE are supported. The current
implementation does not support "hot-links" or "warm links"
(ADVISE/UNADVISE) and other data formats than text (DDEFMT_TEXT).

To use the DDE server services of RXDDE.DLL, the following REXX
DLLs are required in addition to RXDDE.DLL: RXSEM.DLL, RXVARP.DLL
and RXQUE.DLL.

RXDDE has been tested with the following application programs:

   - Borland Object-Vision 2.0 for OS/2.
   - Microsoft EXCEL V2.2 for OS/2.
   - Microsoft EXCEL V3.0 for Windows (running under WinOs2).



Registering the DLL with REXX
-----------------------------

Before the DLL can be used, it must be registered with the REXX
interpreter. The following REXX statements can be used to register
the DLL:

   if rxfuncquery("RXDDE")<>0 then do
      rc = rxfuncadd("RXDDE","RXDDE","RXDDE")
      if rc <> 0 then call abend "rxfuncadd of RXDDE failed, RC="rc
      end

If the file RXDDE.DLL is not in the current directory or in the
LIBPATH, the complete path name must be included.

The file IIDDEGW.EXE must be in the current directory or in the
PATH when RXDDE.DLL is used. IIDDEGW.EXE is a DDE gateway program
that runs in an invisible PM session. It is automatically
started in the background when a DDE link is opened though
RXDDE.DLL.

Refer to the text file RXDLLOS2.TXT for additional information
about using this DLL with REXX.



The <rxdde> function
--------------------

The DLL is called through the external REXX routine with the same
name, <rxdde>.

The <rxdde> routine can be called as a function or as a routine.
Example to call <rxdde> as a function:
   result = rxdde(statement)
Example to call <rxdde> as a routine:
   call rxdde statement

The <rxdde> routine has a single argument <statement> and returns
the value <result>. <result> contains either an error message,
or an empty string if no error has occured.

<result> must be tested after each call to <rxdde>. If <result> is
not the empty string, an error exit routine (i.e. <abend>) should be
called.
Example:
   call rxdde statement
   if result <> "" then call abend "rxdde failed," result



Statements that can be passed to <rxdde>
----------------------------------------

The following statements can be used as parameters to <rxdde>:

   open         open a DDE link
   close        close a DDE link
   request      get data from a DDE server (client link only)
   poke         send data to a DDE server (client link only)
   execute      send a command to a DDE server (client link only)

Before a DDE link can be used, it must be opened though <open>.
<Open> returns a link handle that is used to identify the DDE
link with further statements. Multiple DDE links can be open at
the same time.

When a DDE link is no longer used, <close> should be executed to
release all resources that where allocated to the link.
After <close> has been executed, the link handle associated with
the DDE link is no longer valid.



Notes to the statement syntax and parameters
--------------------------------------------

- The statements have parameters in the form "keyword=value".
  These keyword parameters can appear in any order.
- Most parameters have default values that will be assumed when
  the parameter is not specified.
- A parameter value must be enclosed in single or double quotes
  if it contains blanks or special characters or if it is an empty
  string.
- Keywords names are case-independent. They can be written in
  uppercase, lowercase or mixed-case characters.
- Boolean output values are returned according to REXX conventions
  as 1 for <true> or 0 for <false>.
  Boolean input parameters can be 1, "TRUE" or "YES" for <true>,
  or 0, "FALSE" or "NO" for <false> (case-independent).

The following sections describe each statement in detail.



Statement:  open  -  open a DDE link
------------------------------------

<open> opens a DDE link. It provides a link handle that can be
used to further identify the DDE link. When the DDE link is no
longer used, <close> should be called.

For a DDE server link, <open> also opens a variable pool, a
queue, and a semaphore associated with the queue. Incomming
REQUEST and POKE transactions are directed to the variable pool.
Incomming EXECUTE transactions are directed to the queue.

Syntax:
   open type=id appl_name=id topic_name=id timeout=nr
        link_handle_var=rexx_var_name
        varp_handle_var=rexx_var_name queue_handle_var=rexx_var_name
        queue_sem_handle_var=rexx_var_name

Parameters:
   type  -  link type
      <type> must be "client" for a client link or "server" for a
      server link.
      If <type> is "client", rxdde tries to initiate a DDE
      conversation to a DDE server application. The server
      application must be running. If no server application
      responds to the DDE initiation request, the link cannot be
      opened and an error message is returned. If more than one
      server responds to the same initiation request, the link is
      not opened and an error message is returned. (The current
      implementation of RXDDE does not support one-to-many
      connections through a single DDE client link).
      If <type> is "server", an unconnected server link is opened
      that is ready for a client to connect. Multiple clients can
      connect to the same server link concurrently.
      The default value for <type> is "client".
   appl_name  -  DDE application name
      <appl_name> specifies the DDE application name for the link.
      The following table lists the DDE application names used
      by some products:
         VISION   Borland Object-Vision
         EXCEL    Microsoft Excel (OS/2 and Windows-Version)
         WINWORD  Microsoft Word for Windows
   topic_name  -  DDE topic name
      <topic_name> specifies the DDE topic name for the link.
      For applications that operate on file-based documents,
      topics are usually file names.
      Examples for topic names:
         for Object-Vision:    TEST1.OVD
         for Excel:            SHEET1
   timeout  -  DDE transaction timeout in milliseconds
      <timeout> specifies the maximum time (in milliseconds) the
      DDE interface will wait for a response from a DDE server
      during a REQUEST, POKE or EXECUTE DDE transaction.
      The default value for <timeout> is 60000 (one minute).
   link_handle_var  -  name of REXX variable for link handle
      <link_handle_var> specifies the name of a REXX variable
      where the link handle will be stored. The link handle is an
      integer number that is used to uniquely identify the DDE
      link.
   varp_handle_var  -  name of REXX var for variable pool handle
      <varp_handle_var> specifies the name of a REXX variable
      where the variable pool handle for a DDE server link is to
      be stored.
      Refer to RXVARP.TXT for information about how to access the
      variable pool.
      This parameter is only used when <type> is "server".
   queue_handle_var  -  name of REXX variable for queue handle
      <queue_handle_var> specifies the name of a REXX variable
      where the queue handle for a DDE server link is to be stored.
      Refer to RXQUE.TXT for information about how to access the
      queue.
      This parameter is only used when <type> is "server".
   queue_sem_handle_var  -  name of REXX variable for sem handle
      <queue_sem_handle_var> specifies the name of a REXX variable
      where the semaphore handle associated with the queue for
      a DDE server link is to be stored.
      Refer to RXQUE.TXT and RXSEM.TXT for more information.
      This parameter is only used when <type> is "server".

Example:
   call rxdde "open type=client appl_name=EXCEL topic_name=SHEET1",
        "link_handle_var=link1"
   if result <> "" then call abend result
   say "the DDE link handle is" link1



Statement:  close  -  close a DDE link
--------------------------------------

<close> closes a DDE link. All resources that have been allocated
to the link are released. After <close> has been executed, the
link handle associated with the DDE link is no longer valid and
must not be used any more.

Syntax:
   close link_handle=nr

Parameters:
   link_handle  -  link handle
      <link_handle> is the link handle associated with the DDE link
      to be closed.

Example:
   call rxdde "close link_handle="link1
   if result <> "" then call abend result



Statement:  request  -  get data from a DDE server
--------------------------------------------------

<request> is used with a client link to get a data item value from
the linked DDE server.

Syntax:
   request link_handle=nr item_name=id data_var=rexx_var_name

Parameters:
   link_handle  -  link handle
      <link_handle> is the link handle associated with the DDE link.
   item_name  -  DDE item name
      <item_name> specifies the name of the data item to be
      requested.
      For Object-Vision, <item_name> specifies the name of a field
      variable, i.e. "Address1".
      For Excel, <item_name> specifies the coordinates of a cell or
      of a cell range, i.e. "R2C3" for the cell at row 2 column 3,
      or "R1C1:R4C4" for the rectangle of cells from row 1 column 1
      to row 4 column 4.
   data_var  -  name of REXX variable for received data
      <data_var> specifies the name of a REXX variable where the
      data value is to be stored.
      The data is stored in an application dependent format.
      Excel inserts TAB characters to separate columns and CR/LF
      character pairs at the end of each row.

Example:
   call rxdde "request link_handle="link1 "item_name=R1C3 data_var=s"
   if result <> "" then call abend result
   say "The value at row 1 column 3 is" s



Statement:  poke  -  send data to a DDE server
----------------------------------------------

<poke> is used with a client link to send a data item value to
the linked DDE server.

Syntax:
   poke link_handle=nr item_name=id data_var=rexx_var_name

Parameters:
   link_handle  -  link handle
      <link_handle> is the link handle associated with the DDE link.
   item_name  -  DDE item name
      <item_name> specifies the name of the data item to be sent.
      Refer to the description of the <request> statement for more
      information.
   data_var  -  name of REXX variable that contains the data value
      <data_var> specifies the name of a REXX variable that contains
      the data value to be sent. Refer to the description of the
      <request> statement for more information.

Example:
   s = "Hello"
   call rxdde "poke link_handle="link1 "item_name=R1C1 data_var=s"
   if result <> "" then call abend result



Statement:  execute  -  send a command string to the DDE server
---------------------------------------------------------------

<execute> is used with a client link to send a command string to
the linked DDE server.

Syntax:
   execute link_handle=nr cmd_var=rexx_var_name

Parameters:
   link_handle  -  link handle
      <link_handle> is the link handle associated with the DDE link.
   cmd_var  -  name of REXX variable that contains the command string
      <cmd_var> specifies the name of a REXX variable that contains
      the command string to be sent. The contents of the command
      string is application dependent. For some applications the
      command statements must be enclosed in brackets. Multiple
      command statements can be sent at once.
      Example for Excel: "[app.minimize()]"
      Example for Object-Vision:
         '[@FIELDCLEAR("Address1")][@MESSAGE("DDE command completed")]'

Example:
   s = "[app.maximize()]"
   call rxdde "execute link_handle="link1 "cmd_var=s"
   if result <> "" then call abend result



Sample program SAMPDDE1.CMD  -  DDE client link to Microsoft EXCEL
------------------------------------------------------------------

/* SAMPDDE1.CMD  -  test program for RXDDE.DLL                         */
/* Demonstrates a client link to EXCEL.                                */
/* Excel must be running and SHEET1 must be open.                      */

signal on failure name rexx_exception
signal on novalue name rexx_exception

parse source os . mod_name
select
   when os = "OS/2" then do
      "@echo off"
      parse arg mode main_parms
      if mode <> "INDIRECT" then do
         "cmd /c" mod_name "INDIRECT" arg(1); exit rc; end
      end
   otherwise parse arg main_parms
   end

sheet_name = "SHEET1"

call init
call open_dde_link
call demo
call close_dde_link

return

demo:
   call set_cell "R2C2", "Hello"
   say 'Cell at row 1 column 1 = "'get_cell("R1C1")'"'
   say 'Cell at row 1 column 2 = "'get_cell("R1C2")'"'
   return

set_cell:
   parse arg cell_position, cell_value
   call rxdde "poke link_handle="link1 "item_name="cell_position,
         "data_var=cell_value"
   if result <> "" then call abend result
   return;

get_cell:
   parse arg cell_position
   call rxdde "request link_handle="link1 "item_name="cell_position,
         "data_var=cell_value"
   if result <> "" then call abend result
   if right(cell_value,2) == '0D0A'X then
      cell_value = left(cell_value,length(cell_value)-2)
   return cell_value

open_dde_link:
   call rxdde "open type=client appl_name=EXCEL topic_name="sheet_name,
         "link_handle_var=link1"
   if result <> "" then call abend result
   say "DDE link opened, link_handle="link1
   return

close_dde_link:
   call rxdde "close link_handle="link1
   if result <> "" then call abend result
   say "DDE link closed"
   return

init:
   if rxfuncquery("RXDDE")<>0 then do
      rc = rxfuncadd("RXDDE","RXDDE","RXDDE")
      if rc <> 0 then call abend "rxfuncadd of RXDDE failed, RC="rc
      end
   return

/*--- exit routines ---------------------------------------------------*/

rexx_exception:
   parse source . . mod_name
   call error_exit 'REXX' condition('C') 'exception in module' mod_name,
         'line' sigl', statement = "'sourceline(sigl)'"'

abend:
   parse arg err_msg
   parse source . . mod_name
   s = 'Error in module' mod_name 'line' sigl
   if err_msg <> '' then s = s',' err_msg
   call error_exit s

error_exit:
   parse arg err_msg
   if err_msg <> '' then say err_msg
   exit 99



Sample program SAMPDDE2.CMD  -  DDE client link to Borland Object-Vision
------------------------------------------------------------------------

/* SAMPDDE2.CMD  -  test program for RXDDE.DLL                         */
/* Demonstrates a client link to Object-Vision.                        */
/* Object-Vision must be running with application SAMPDDE2.OVD.        */

signal on failure name rexx_exception
signal on novalue name rexx_exception

parse source os . mod_name
select
   when os = "OS/2" then do
      "@echo off"
      parse arg mode main_parms
      if mode <> "INDIRECT" then do
         "cmd /c" mod_name "INDIRECT" arg(1); exit rc; end
      end
   otherwise parse arg main_parms
   end

ov_appl_name = "SAMPDDE2.OVD"

call init
call open_dde_link
call demo
call close_dde_link

return

demo:
   call set_ov_field "test1", "Hello"
   say "sel1 =" get_ov_field("sel1")
   say "sel2 =" get_ov_field("sel2")
   call exec_ov_cmd "[@ASSIGN(test2,@NOW)]"
   return

set_ov_field:
   parse arg field_name, field_value
   call rxdde "poke link_handle="link1 "item_name="field_name,
         "data_var=field_value"
   if result <> "" then call abend result
   return;

get_ov_field:
   parse arg field_name
   call rxdde "request link_handle="link1 "item_name="field_name,
         "data_var=field_value"
   if result <> "" then call abend result
   return field_value

exec_ov_cmd:
   parse arg cmd
   call rxdde "execute link_handle="link1 "cmd_var=cmd"
   if result <> "" then call abend result
   return

open_dde_link:
   call rxdde "open type=client appl_name=VISION",
         "topic_name="ov_appl_name "link_handle_var=link1"
   if result <> "" then call abend result
   say "DDE link opened, link_handle="link1
   return

close_dde_link:
   call rxdde "close link_handle="link1
   if result <> "" then call abend result
   say "DDE link closed"
   return

init:
   if rxfuncquery("RXDDE")<>0 then do
      rc = rxfuncadd("RXDDE","RXDDE","RXDDE")
      if rc <> 0 then call abend "rxfuncadd of RXDDE failed, RC="rc
      end
   return

/*--- exit routines ---------------------------------------------------*/

rexx_exception:
   parse source . . mod_name
   call error_exit 'REXX' condition('C') 'exception in module' mod_name,
         'line' sigl', statement = "'sourceline(sigl)'"'

abend:
   parse arg err_msg
   parse source . . mod_name
   s = 'Error in module' mod_name 'line' sigl
   if err_msg <> '' then s = s',' err_msg
   call error_exit s

error_exit:
   parse arg err_msg
   if err_msg <> '' then say err_msg
   exit 99



Sample program SAMPDDE3  -  DDE server link
-------------------------------------------

/* SAMPDDE3.CMD  -  sample program for RXDDE.DLL                       */

/* This program demonstrates how RXDDE.DLL can be used with a DDE      */
/* server link. It opens a server link and waits 3 times until         */
/* a DDE EXECUTE command arrives through the queue and displays the    */
/* contents of a variable of the variable pool.                        */
/* The Object-Vision application SAMPDDE3.OVD can be used in           */
/* conjunction with this sample REXX program.                          */

signal on failure name rexx_exception
signal on novalue name rexx_exception

parse source os . mod_name
select
   when os = "OS/2" then do
      "@echo off"
      parse arg mode main_parms
      if mode <> "INDIRECT" then do
         "cmd /c" mod_name "INDIRECT" arg(1); exit rc; end
      end
   otherwise parse arg main_parms
   end

dde_appl_name = "SAMPDDE3"
dde_topic_name = "TEST"

call init
call open_dde_link
call main
call close_dde_link

return

main:
   var1 = "original contents of var1"
   call rxvarp "set_var varp_handle="varp1 "var_name=var1"
   if result <> "" then call abend result
   do i=1 to 3
      call rxsem "wait sem_handle="sem1
      if result <> "" then call abend result
      call rxque "read queue_handle="queue1 "data_var=cmd"
      if result <> "" then call abend result
      say 'EXECUTE data: "'cmd'"'
      call rxvarp "get_var varp_handle="varp1 "var_name=var1"
      if result <> "" then call abend result
      say 'contents of var1: "'var1'"'
      end
   return

open_dde_link:
   call rxdde "open type=server appl_name="dde_appl_name,
         "topic_name="dde_topic_name "link_handle_var=link1",
         "varp_handle_var=varp1 queue_handle_var=queue1",
         "queue_sem_handle_var=sem1"
   if result <> "" then call abend result
   say "DDE link opened, link_handle="link1
   return

close_dde_link:
   call rxdde "close link_handle="link1
   if result <> "" then call abend result
   say "DDE link closed"
   return

init:
   if rxfuncquery("RXDDE")<>0 then do
      rc = rxfuncadd("RXDDE","RXDDE","RXDDE")
      if rc <> 0 then call abend "rxfuncadd of RXDDE failed, RC="rc
      end
   if rxfuncquery("RXVARP")<>0 then do
      rc = rxfuncadd("RXVARP","RXVARP","RXVARP")
      if rc <> 0 then call abend "rxfuncadd of RXVARP failed, RC="rc
      end
   if rxfuncquery("RXQUE")<>0 then do
      rc = rxfuncadd("RXQUE","RXQUE","RXQUE")
      if rc <> 0 then call abend "rxfuncadd of RXQUE failed, RC="rc
      end
   if rxfuncquery("RXSEM")<>0 then do
      rc = rxfuncadd("RXSEM","RXSEM","RXSEM")
      if rc <> 0 then call abend "rxfuncadd of RXSEM failed, RC="rc
      end
   return

/*--- exit routines ---------------------------------------------------*/

rexx_exception:
   parse source . . mod_name
   call error_exit 'REXX' condition('C') 'exception in module' mod_name,
         'line' sigl', statement = "'sourceline(sigl)'"'

abend:
   parse arg err_msg
   parse source . . mod_name
   s = 'Error in module' mod_name 'line' sigl
   if err_msg <> '' then s = s',' err_msg
   call error_exit s

error_exit:
   parse arg err_msg
   if err_msg <> '' then say err_msg
   exit 99
