    Using Dynamic Data Exchange in Windows AutoCAD Rel 12
			 1/18/93
		      By Phil Ford
		     Autodesk, Inc.


     This document is meant as a supplement to the "Using
AutoCAD for Windows" manual.  It contains more detailed
information about or corrections to the manual.


                          DDE Dialog


     The DDE dialog appears in the Edit menu, DDE item, as
"Dialog...". It is also invoked with the (ddedialog) function in
AutoLISP.  The purpose of this dialog is to store the path, work
file name, hot link setting, and startup command line for
starting up a DDE link automatically.  The work file may need to
be the full path name of the spreadsheet file and may need to be
included on the command line.

	DDE Application:  123w
	Work file:        d:\123w\mysheet.wks
	Command line:     d:\123w\123w d:\123w\mysheet.wks

If the work file is the default spreadsheet, such as Sheet1 for
Excel, then it shouldn't have a path and shouldn't be included
on the command line.

	DDE Application:  Excel
	Work file:        Sheet1
	Command line:     d:\excel\excel

     The defaults for the dialog are based on the Windows
Registration Database.  When any of the DDE applications start, a
table is built by searching the Windows Registration Database
for Microsoft Excel, Lotus 123w, and Borland Quattro Pro (QPW). 
You can run the Windows REGEDIT program with a /v option to see
details of applications registered in Windows.  If information
is found and previous settings were not saved with the DDE
dialog, this information will be used as the default
application, path, topic, and file open command.  For C
programmers, the code to handle this is in ddewin.c (see
ddeApps).  If the DDE dialog is used, the settings are saved in
INI files in the Windows directory:

	DDELISP.INI for the (ddedialog) AutoLISP function
	DDEADS.INI  for Edit-DDE menu
	SHAFT.INI.  for the Shaft demo
	


     For example, here are few typical DDELISP.INI files:
________________________________

[AutoCAD DDE]
DDE App=Excel
DDE Topic=Sheet1
DDE Path=D:\EXCEL\EXCEL.EXE
DDE Advise=1

________________________________

[AutoCAD DDE]
DDE App=QPW
DDE Topic=d:\qpw\Notebk1.wb1
DDE Path=D:\QPW\QPW.EXE
DDE Advise=1

________________________________


Here is some of the default data that will be corrected if an
application is found in the Registration database (excerpt from
ddewin.c).


App    RegDB Name           Path 	  Default       Type
					  file		    Open command

"Excel""ExcelWorksheet"     "c:\\excel\\" "Sheet1"      SSEXCEL 
							    "[OPEN(\"%1\")]" 

"123w" "123w"               "c:\\123w\\"  "Untitled"    SSLOTUS

"QPW"  "QuattroProNotebook" "c:\\qpw\\"   "Notebk1.wb1" SSLOTUS	
							    "{FileOpen \"%1\"}"


     The AutoLISP DDE function to start a DDE channel, INITIATE,
only works if the application with which we want to converse is
already running and the work file is open.  The AutoLISP
functions (appinit) and (ddeprompt) help automate this.




                     DDE Sample Programs

     The following sample programs are provided for ADS and LISP
programmers.

Dde.lsp (uses ADS program ddelisp.exe built with ddelisp.c
	and the ADS DDE library)

        Sample DDE program (in AutoLISP) that sends 
	AutoCAD data in several formats to a spreadsheet, 
	reads it back in, and modifies the drawing if any data 
	has changed.  This program demonstrates the use of the
 	DDE AutoLisp functions documented in the Using AutoCAD
	for Windows manual.

	NOTE: The AutoLISP function, reqmod, requires that the
	number of rows requested from a spreadsheet be at least 
	as long as the entity of interest + 2.

DdeAds.c (uses ddeads.exe built with ddeads.c and the ADS DDE
	library)

        Small ADS program that uses the DDE library to
	perform similar functions as dde.lsp.  It is used 
	in the DDE submenu of the acadwin.mnu Edit menu.


Shaft.exe (uses shaft.c and the ADS DDE library)

	Parametric design example using a spreadsheet. 


DdeBlank.C (uses ddeblank.c and the ADS DDE library)

        Example program from which to start a new DDE 
	or ADS windowing program.


ADS DDE Library (dde.lib)
	The modules that comprise the ADS DDE library are:

	SPREADSH.C - High level spread sheet functions 

	DDECONV.C - Various data conversion routines for AutoCAD
	entities, Spreadsheets, and DDE.  Send and receive 
	and modify drawings, tags and attributes, tables, 
	selection sets, etc. 

	DDEWIN.C - Low Level DDE Manager Dialog, dialog

	WINUTIL.C - Low level routines: linked list, memory
	allocation, etc.



		      ADS Module Hierarchy


        dde.lsp        
      	   |
	(ddelisp.exe)
	ddelisp.c      ddeads.c   shaft.c
	   |		 |	      |
	   |_____________|____________|
	   	   |	 |
		   |	 |------
		   |	       |
		spreadsh.c   ddeconv.c		
        DDE.LIB    |           |
		ddewin.c       |
		   |	       |
		winutil.c    ADS Library
			     winadsxx.lib


	

                           DDE.LSP

     DDE.LSP is a sample AutoLISP program that exercises the
AutoLISP DDE functions.  It calls functions implemented in
DdeLisp.c.  These functions make it possible to store drawing
data in a spreadsheet or database program and retrieve the data,
updating the drawing with any changes made.  One group of
functions is similar to Excel's DDE macros.  They are the basic
DDE transfer functions.  Another group contains the higher level
functions that transfer entire drawings, selection sets, tables,
or drawing tags and attributes.  


                        "send" command

     To test DDE.LSP out with Excel, copy DDE.LSP and 
DDELISP.EXE to the AutoCAD directory.  Start Excel.   Then start
AutoCAD and create a new drawing.  Draw a few lines.  Enter:

     (load "dde")
     send

     When the "send" command is completed, drawing data will 
appear in the spreadsheet starting with the tables.  The order 
of the tables, for those that are present in a drawing, is view 
port, line type, layer, style, view, ucs, blocks, and dim
styles.  After the tables there are some blank lines and then
the drawing entity data. This data is similar to the "DXF"
format.
	

                         "get" command

     Try changing some data in the drawing section.  Don't change
a row starting with -1, 0, or 5, though, since those rows contain
the entity "name", type, or handle.  Try a row starting with 10 
(vector X value), column B, to change the starting point of a 
vector.  In AutoCAD type "get" to scan the whole spreadsheet and
modify any changed entities.  Or type "(mod)" for a faster 
update.  The "mod" function limits the range scanned to the area
of interest (entity that changed).  After typing "(mod)", answer
with the row number of the start of the entity in the 
spreadsheet (the -1 row if  one exists, or the 0 row if there is
a handle.  For the "rows" prompt, enter "30" or so.  (The number
of rows needs only to be large enough to include the entity.)  
The drawing will change to reflect the spreadsheet change.


                      "(sendset)" function

     The "sendset" function prompts the user to select part of
the drawing, and sends the selection set data to the remote DDE 
application:

     (sendset)

		     "(ddedialog)" function

     To change the DDE application name, work file, etc., run
the DDE dialog by entering "(ddedialog)".



               Windows, ADS, and C Programming

     For programming in C with the AutoCAD Development System, 
the DDE sample C programs provide examples of how to use DDE
with AutoCAD for parametric design and how to create your own
custom dialogs to enhance an ADS program's interface.


                      Parametric Design

     The "Shaft" sample program is an example of parametric 
design using AutoCAD and spreadsheet programs such as Excel. See
the "Using AutoCAD for Windows" manual for more on this.  Shaft
uses these functions from DdeWin.C:

DdeInit - Intialize DDE system 
DdeDefaults - In case there is no info in windows\shaft.ini yet, 
	use these defaults
CellRange - Create a DDE "item" describing the range of cells in 
  	which we're interested.
DdeGetProfile - See if we have information in windows\shaft.ini
	about the default DDE application, topic, hot link
	option, etc.
DdeQuit - Free up memory used by DDE
PokeRealArray - send an array of points to Excel
RequestRealArray - request an array of points from Excel
DdeSetAdvise - Set up hot link item, command to AutoCAD, and 
	function to send the command.
DdeAdviseCur - Start or stop hot link in current channel, 
	using info from DdeSetAdvise.
DdeFindChnl - See if we have a channel with default app, topic
DdeDlgStart - Try to create DDE conversation, put up dialog if unable


One note on Excel: to toggle between showing formulas and 
data, use the pulldown "Options", then "Display", then toggle 
"Formulas".  Formulas must be off when AutoCAD imports the 
data.


           Create a New AutoCAD DDE Application

     To create a program that uses DDE with AutoCAD, copy ddeblank.*
to newname.*, and modify the ADS functions in newname.c, which will
contain the bare minimum to link to AutoCAD and initialize the DDE
system.  Use your editor to find and replace "DdeBlank" with the new
name.  Edit the make file, DDE, and newname.DEF to include newname. 
Your new ADS program can now use all the functions in ddewin.c,
ddeconv.c, spreadsh.c, and winutil.c.  Look over the global functions
in those modules to see what's available.  For example, to send a 2
dimensional array of reals (double floats) to a spreadsheet, use
PokeRealArray in spreadsh.c.  To build a linked list of strings, see
AddList in winutil.c  To send the list to a spreadsheet see PokeList
in spreadsh.c.  To set up a "hot link", see winads.c and ddewin.c and
search for the word "advise".  For example, see DdeAdvise in
ddewin.c.  Ddeblank.c also illustrates an implementation of DDE server
mode.


		      ADS as DDE Server

     The functions in ddewin.c, spreadsh.c, and so on are mainly on
the DDE client side.  For an illustration of implementing DDE server
functions, see the DdeCallback function in ddeblank.c.  Since AutoCAD
normally initiates ADS functions, it is more natural to have ADS
functions act as DDE clients.  However, ADS can act as a DDE server. 
If a DDE client app requested data from AutoCAD, however, it would be
difficult for ADS to return the data to the client.  The only way the
ADS app can get AutoCAD's attention is to send it the WM_ACAD message
to execute a function in the ADS application.  Since the DdeCallback
function will return before AutoCAD calls the ADS function, the ADS
function can only store a value for later use.  For example, we might
like to call ads_getvar in our ADS function and store the value so it
could be returned to the DDE client.  The client would have to call a
2nd time to get such a stored value.

	DdeCallback()
	    Request for data from client app
	    Send WM_ACAD command to AutoCAD to execute function f1()
	    returns before f1() is called.

        f1()
	    store value returned from ads_getvar

However, there might well be other uses of the server mode for ADS
applications.



                   Building the DDE Programs


     To build the DDE programs with Microsoft C/C++ 7.0 and the
Microsoft utility, NMAKE.EXE, move to the WIN directory and type

			"nmake dde".  
			
The DDE make file will create several sample applications. The
samples should be copied to the AutoCAD work directory.  The
make file "DDE" assumes that the ADS .h and .lib files are one
subdirectory below the WIN directory.  To change the ADS library
and header file directory used by the DDE make file, just edit
the file DDE and change the "ADSDIR" directory definition:

                         ADSDIR = ..


     See README.ADS for details on building the DDE sample
programs with other compilers.



                       DDE Manager

     DdeWin.C contains a list of functions to make dynamic data 
exchanges easy.  For example, to execute a command in Excel,
just call DdeExec with a command such as:
 "[SELECT(\"R2C5\")][FORMULA(\"AutoCAD Graphics Data\")]"

     This example will put the text "AutoCAD Graphics Data" in
the row 2, column 5 cell.  To put data in the spread sheet cells
directly, use DdePoke with a DDE "item" such as "R2C5".  The 
function names in this module start with "Dde".


____________________________________________________________




                     ADS DDE Functions

/* Global Function Prototypes for spreadsh.c */

/* Specify Excel or Lotus cell specification style */
void
AppType(int ss_type);


/* Get an ads_real (double) request data in pReal, which
   could be a pointer to an array of ads_reals.   Request 
   spreadsheet data starting at Row, Col.  nRow is the
   number of rows, nCol the number of columns. 
*/
int RequestRealArray(PDDE pdde, ads_real *pReal, UINT Row, UINT Col,
		 UINT nRow, UINT nCol);


/* Get ads_real (double) request data in pReal. 
*/
int ReqCellData(PDDE pdde, ads_real *pReal, UINT Row, UINT Col);


/* Send out a range of ads_reals (doubles), in array starting
   at pReal.  
*/
int PokeRealArray(PDDE pdde, ads_real *pReal, UINT Row, UINT Col,
		  UINT nRow, UINT nCol);


/* Send out a list of strings to a range in a spreadsheet.
   TextLink is the first PLINK in a list. 
*/
int PokeList(PDDE pdde, PLINK TextLink, UINT Row, UINT Col,
	      UINT nRow, UINT nCol);


/* Send out a ads_real (double). 
*/
int PokeReal(PDDE pdde, ads_real Real, UINT Row, UINT Col);


/* For inserting formulas into spreadsheets:
   build a cell reference string to Row, Col
   (1 based).  E.g., row 3, col 1:  "=A3" 
*/
VOID CellRef(char *CellRefStr, UINT Row, UINT Col);


/* Build DDE Item name, such as "R2C5:R11C10", from start 
   row, col, number of rows, cols. 
*/
VOID CellRange(char *CellItem, UINT Row, UINT Col,
	       UINT nRow, UINT nCol);


/* For DDE Item field: build "R2C5" from Row 2, Col 5, into your
   passed string, CellItemName. 
*/
VOID CellItemName(char *CellItem, UINT Row, UINT Col);


/* Get the row, col, number of rows, number of cols expressed in 
   the RxCx:RyCy string, CellItem.  Returns TRUE if the ":" was
   found, indicating a range of cells. 
*/
int GetCellRange(char *CellItem, UINT *Row, UINT *Col,
		 UINT *nRow, UINT *nCol);


/* Get Row and col from "R3C1" cell item string.  0 means
   current row or col 
*/
VOID GetCellNum(char *CellStr, UINT *Row, UINT *Col);


/* Find number of rows and col's in data returned
   by a cell range, such as "R3C1:R12C2" 
*/
VOID GetCellDim(PHDATA DataStrm, UINT *nRow, UINT *nCol);





/* Global Function Prototypes for ddeconv.c */

/* Set the DDE/AutoCAD format:
	ENAME_MODE  0
	HANDLE_MODE 1
	ATTR_MODE   2
	ATTR_MODE2  3
*/
void SetDdeFormat(int format);


/* Get the current DDE/AutoCAD format */

int GetDdeFormat(void);


/*


		AutoCAD Drawing Entity DDE Transfers



   Send all drawing tables and entities to spreadsheet, starting
   at row 
*/
UINT PokeDrawing(PDDE pdde, UINT sRow, UINT sCol);


/* Request data from remote DDE app and modify drawing
   with it, starting at row, limit of NumRows.	 Returns 
   number of entities modified. 
*/
UINT ModDrawing(PDDE pdde, UINT row, UINT col, UINT NumRows);


/* Send all the table data to remote DDE spreadsheet, starting
   at sRow, sCol.   Return the number of rows taken.  
*/
UINT PokeAllTables(PDDE pdde, UINT sRow, UINT sCol);


/* Send data from one table to DDE app. 
   TableNum is VPORT, VIEW, etc. 
*/
UINT PokeTable(PDDE pdde, short TableNum, char *item, UINT sRow,
		 UINT sCol, int WriteEnd);


/* Send selection set data to DDE app, according to
   format, Frmt.  Caller must still call ads_ssfree(). 
*/
UINT PokeSet(PDDE pdde, ads_name ename, char *item, UINT sRow, UINT sCol);


/* Poke selection set of attribute data */

int PokeAttrSet(CONVDATA *ConvData);


/* Create ConvData->textlist and poke the list. */

int PokeAttr(CONVDATA *ConvData);


/* Create linked list of strings until SEQEND found.  Returns TRUE 
   if ename is valid.  "ename" cannot be NULL.  Check ConvData->textlist
   to see if an attribute was added to list. */

int CreateAttrList(CONVDATA *ConvData);


/* Look up constant attribute associated with this INSERT, if any, 
   and add text to the linked list of text items.  Returns array 
   length (MINSERT) or 1 for normal INSERT
*/
int ConstList(Tlink **ptlist, RESBUF *pEnt);


/* Create a list of block names and the constant attribute value
   in the symbol table.  Pass in address of pointer to BlkList that was 
   initialized to NULL.  blkList will be filled in with the
   block name and constant value found, in pairs.  Call FreeConstList
   to free the whole list.

   BLOCK1 - CONSTANT VALUE 1 -- VALUE 2 -- VALUE 3
     |
     |
   BLOCK2 - CONSTANT VALUE 1 -- VALUE 2
     |
     |
   BLOCK3 - CONSTANT VALUE 1 -- VALUE 2  -- VALUE 3
     |

   Eg,
   BlkList *blkList = NULL;
   CreateConstList(&blkList);
*/
CreateConstList(BlkList **blkList);


/* Free Constant Attribute list 
*/
FreeConstList(BlkList **blkList);


/* Poke the constant definitions into the first row */

PokeConstList(PDDE pdde, BlkList *blkList);


/* Add filtered entity string to linked list */

int AddEntList(CONVDATA *ConvData, RESBUF *pEnt, int grpcode);


/* Poke a horizontal row of strings from the list based on ConvData
   values: pdde, textlist, row, & col1. */

int PokeConvList(CONVDATA *ConvData);


/* Put a list of entities into spreadsheet cell at row and col.
   Returns number of rows (length of list). 
*/
UINT PokeEntList(PDDE pdde, UINT Row, UINT Col, ads_name ename, short etype);


/* Put an entity into spreadsheet cell at row and col.
   Returns number of rows (length of list) 
*/
UINT EntPokeCell(PDDE pdde, UINT Row, UINT Col, RESBUF *pEnt, short etype);


/* Put an entity into remote app using DDE item of ItemStr.
   Find number of rows with GetEntLen. 
   "etype" is ET_RB, ET_NORM, ET_SSET, or ET_TBL. 
*/
UINT EntPoke(PDDE pdde, char *ItemStr, RESBUF *pEnt, short etype);


/* Request data from spreadsheet Item, and update
   entities with it that have changed.	Returns the
   number entities modified.  RowCnt gets the number
   of rows (sub-entities) used.   The row counter can
   be bumped by this amount for next call.
*/
UINT ReqMod(PDDE pdde, char *Item, UINT *RowCnt, int *more);



/* Format a list of entities into DDE or AutoCAD 
   text display, including all links.  
   FldSepStr and RecSepStr and used to separate items.
     -1 <FldSepStr> x60000048 <FldSepStr> 160000345 <FldSepStr> 160023456 <RecSepStr>
      8 <FldSepStr> "LAYER" <RecSepStr>
     etc.
   Use free() to free the pointer returned.   
*/
char *EntListFormat(RESBUF *pEnt, short etype);


/* Get basic C language type from AutoCAD dxf group code (RTREAL, 
   RTANG are doubles, RTPOINT double[2], RT3DPOINT double[3], 
   RTENAME long[2].  The "etype" is one of the ET_ definitions. 
   Returns RTNONE if grpcode isn't one of the known group codes. 
*/
short dxftype(short grpcode, short etype);


/* Free a list of entities created with the upper level
   heap calls, instead of by ADS.  Pass address of first 
   node.  
*/
void EntFree(RESBUF **pEntDde);


/* Modify conversion strings for converting entities to
   text streams and back.  Set any args to NULL to accept
   default.  E.g., for 6 digit accuracy,  .001 tolerance (defaults):
        ConvertInit(6, 0.001, "\t", "\r\n"); 
   When entities are converted from strings back to reals,
   if the change is greater than Tolerance, EntCompare says
   they're different (so entmod can be called to change 
   them in AutoCAD).   Depends on digits of accuracy.  
*/
void ConvPrecision(int precision, ads_real Tolerance);


/* Set field record separators for text formatted 
   AutoCAD drawing entities.  Default is TAB field separator, 
   CRLF record separator. 
*/
void ConvSeparators(char *FldSeparator, char *RecSeparator);


/* Call after a drawing has been opened (ads RQXLOAD request)
   to set the local flatland variable, so dxf point data
   (group code 10, 11,	etc.) will be interpreted correctly. 
*/
short SetFlatLand(void);


/* Turn on handle mode for AutoCAD drawing.  Since handles
   are valid between AutoCAD sessions, and names aren't (they're
   memory load dependent), data can be saved and used later
   with a drawing.  Necessary with Frmt = 2, although the 
   drawing could already have handles on.  Returns previous
   state, TRUE if on. 
*/
BOOL SetHandles(void);


/* Get number of rows taken by all the tables and blocks 
*/
ULONG LenAllTable(void);


/* Get number of rows taken by a table or all the blocks 
*/
UINT LenTable(short TableNum);


/* Use AutoCAD to prompt for an application and topic name,
   initiate a DDE channel with the names, and return the 
   channel number if successful, 0 if not.  "app" and "topic"
   need to be able to store MAXNAME+1 characters.  Use DdeDlgStart
   if you'd rather use a Windows dialog.
*/
PDDE AdsChnl(char *app, char *topic);


/* Display a list of DDE channels in AutoCAD, such as:
		1 Excel Sheet1
		2 AutoCAD Columbia
*/
short AcPrintChnlList(void);


/* Build a list of DDE channels for display
   for dialogs or whatever. 
		1 Excel Sheet1
		2 AutoCAD Columbia
*/
short BuildChnlList(PLINK *list, char *fldsep);


/* Use AutoCAD to input a string.  Show prompt, with 
   <default>:, get user input in result.  Set default to 
   NULL if none.  Negative return means failure. 
   E.g.  
        AcPrompt("DDE Application", "Excel", TestApp); 

		     would look like:

	   DDE Application <Excel>: 
*/
int AcPrompt(char *prompt, char *DefString, char *result);


/* Display any text linked list, using the Separator string
   to separate one item from the next. 
*/
void AcPrintList(PLINK TextList, char *Separator);


/* Display string conversion value of resbuf in AutoCAD.  If 
   it's an entity type (RESBUF), set EntityFlag = TRUE. 
*/
void AcPrintRb(RESBUF *pEnt, short etype);


/* Equivalent of AutoLISP assoc function. 
   Find the sub-entity with a certain group code, return that
   pointer to sub-entity. 
*/
RESBUF *assoc(RESBUF *Ent, short type);


/* Is this entity an "ATTRIB", "BLOCK", etc ("entstr") 
	Eg:
        IsEntType(pEnt, "ATTRIB");
*/

int IsEntType(RESBUF *pEnt, char *entstr);


/* Search a list of entity data, find handle, and use it
   to get the entity from AutoCAD.  Call ads_relrb  later
   if return is non NULL.
*/
RESBUF *GetEntFromHandle(RESBUF *pEntDde);


/* Format Hex handle to decimal for database ID.  Put decimal string
   in result, return value. */

long DecimalHandle(char *hexstr, char *result);


/* Is this selection set entity the start of a sequence of attributes
   or polylines?  Returns ATTRS_FOLLOW, SEQ_ATTR, SEQ_PLINE, or 0. */

int IsSequence(RESBUF *pEnt);


/* Use ads_ssget to get all INSERTS.  Set skip_const to select only those
   with attributes following (<66 . 1>). */

int GetInserts(ads_name ss, int skip_const);


/* Get AutoCAD function arguments.  Send array of 
   ARGLIST structures preloaded with
   expected types, such as [RTSHORT, RTSTR, RTENAME],
   and number of args.	Loads pointers to arguments
   in array.  Returns true if all argument types are correct.	  
   Handles error messages to AutoCAD otherwise.
*/
int GetArgList(ARGLIST *arglist, char *funcname);


/* See if this insert was a MINSERT, return cols * rows 
   Return 1 if not an array. */

int InsertArrayCnt(RESBUF *pEnt);






/* Global Function Prototypes for ddewin.c */

/* Initialize the Dde Manager.	 Pointer to structure
   of DDE global data is returned.
   ProgName is our name, used by other apps to get our
   attention; e.g. "AutoCAD" or "DdeApp".
   "idinst" is 0 if DdeInitialize hasn't been called elsewhere;
   otherwise, it's the value.
   Call DdeQuit() to send TERMINATE msg to apps,
   destroy DDE channel windows, and free all memory. 
*/
DDEDATA *DdeInit(char *ProgName, HANDLE hInst, DWORD idinst);


/* Program about to quit.  Unregister all services, free memory */

void
DdeQuit(void);


/* Set defaults for DDE conversation init dialog.  The appexe is
   the command line used to start the app and have it load a 
   work file.  For the default work file, no command line should
   follow the exe name.  For work files in the current directory,
   no path is needed for the work file.  Eg., the first 2 of
   the following would attempt to start Excel and use the default
   work file, "Sheet1.xls".

        "Excel"         (Excel directory on PATH)
        "c:\\excel\\excel"     (NOT on PATH)
        "c:\\excel\\excel shaft.xls"     (xls file in current dir)
        "c:\\excel\\excel c:\\excel\\shaft.xls"
   
*/

void DdeDefaults(char *app, char *topic, char *appexe);


/* Try to link to default application and work file.  If fail,
   put up a dialog asking for app and topic name, try to 
   start app and link up with DDE.  Set forcedlg TRUE to enter
   the dialog immediately without the attempt to find a 
   channel */

PDDE DdeDlgStart(HWND hwnd, int forcedlg);


/* Start up an application and have it load a work file.  Initialize
   a DDE channel to the app and topic.	Returns a pointer to the DDE 
   channel object.  AppPath is the application name. 
   E.g: 
   pdde = DdeAppInit("Excel", "Sheet1", "c:\\excel\\excel");
*/
PDDE DdeAppInit(char *AppName, char *TopicName, char *AppExe);


/* See if the application is running but the work file is 
   not loaded.  Tell app to open work file or put up message 
   box telling user to open work file. 
*/
int DdeInitSystem(char *AppName, char *TopicName);


/* Check if a channel to app and topic is already open.  If not, 
   try to start one.  Copies app and topic into the defaults
   for the initiate dialog.
*/
PDDE DdeChannel(char *app, char *topic);


/* Start an application */

int StartApp(char *appname, char *commline);


/* Initiate a DDE link on application and topic names.
   Sets channel number to be used on subsequent DDE
   functions.  0 return means not enough memory, or other
   window creation problem.  When done with this channel, 
   call DdeTerminate().  
*/
PDDE DdeInitiate(char *AppName, char *TopicName);


/* Terminate any open DDE channels with remote processes.
   Set SendTermFlag TRUE to actually send a terminate message, FALSE
   to just remove our link in the DDE Manager list. 
*/
int DdeRemoveAll(int SendTermFlag);


/* Get the current channel data structure and verify conversation
   still alive */
PDDE DdeGetCurChnl(void);


/* Is this conversation still alive? */
int 
DdeCheckConv(HCONV hConv);


/* Build DDE text data from a linked list of text, where
   NumCol is the number of columns (can be 1).	 Call FreeFar
   when done with the returned pointer.  E.g., in 2 Cols,
                "First Text",
                "Text 2",
                "Text 3", 
                "Text 4"

		First Text<TAB>Text 2<CR><LF>
		Text 3<TAB>Text 4<CR><LF>
		NULL
   Globals FldSepStr and RecSepStr can be changed before calling
   this, if different separators are desired.
*/
PHDATA DdeFormatList(PLINK TextList, int NumCol, ULONG *DataLength);


/* Terminate a DDE channel.  Inform the remote app, remove
   link from DdeData.ChnlList.	Fix globals DdeData.CurChnl and 
   DdeData.channel. 
*/
void DdeTerminate(PDDE pdde, int SendTermFlag);


/* Request string data on item.  Returns TRUE if data is received.
   Data pointer is pdde->pData.  Call DdeFreeData to free it.
*/
DWORD DdeReqString(PDDE pdde, char *item);


/* Request data on item.  Returns TRUE if data is received.
   Data pointer is pdde->pData.  Call DdeFreeData to free it.
*/
DWORD DdeRequest(PDDE pdde, char *item, int wFmt, ULONG WaitTime);


/* Force string data on server.  Returns DDE_FACK if OK. 
*/
DWORD DdePokeString(PDDE pdde, char *item, char *pString);


/* Force data on server.
*/
DWORD DdePoke(PDDE pdde, char *item, ULONG DataLen, char *pData, int wFmt);


/* Execute a command in the remote program. 
*/
DWORD DdeExec(PDDE pdde, char *pString);


/* Send the TERMINATE message--all done with channel. hConv
   is NULL in this case only.  Returns TERMOK if remote returned
   TERMINATE msg, 0 otherwise. 
*/
DWORD DdeSendTerm(PDDE pdde);


/* Set advise hot link function and AutoCAD command for when hot 
   link data comes in.	
   DdeSetAdvise(pdde, "R3C4:R13C6", "[(moddrawing 3 20000) ] ", advfunc);
*/
void DdeSetAdvise(PDDE pdde, char *item, char *command, ADVISEFUNC advfunc);


/* Start or stop hot link in current channel, using info from
   DdeSetAdvise.
*/
void DdeAdviseCur(PDDE pdde, int yes, int linktype);


/* Set up hot link.  Set NoData TRUE if you want only notice of 
   data change, not data itself.  Set item to NULL to use last
   item used for a POKE message on this channel.  The "advcommand"
   is the string to send to AutoCAD when new data comes in, such 
   as "[import]".
*/
DWORD DdeAdvise(PDDE pdde, char *item, int wFmt, int linktype);


/* Remove hot link.  Send NULL item to use the one stored in pdde->item
*/
DWORD DdeUnAdvise(PDDE pdde, char *item, int wFmt);


/*
    Send: Execute  (Xtype = XTYP_EXECUTE)
	  Poke	   (Xtype = XTYP_POKE)
	  Request  (Xtype = XTYP_REQUEST)
	  Advise   (Xtype = XTYP_ADVREQ)
	  Unadvise (Xtype = XTYP_ADVSTOP)
*/

DWORD DdeSend(PDDE pdde, char *ItemName, int Xtype, ULONG DataLen,
	      char *pData, WORD wFmt, ULONG WaitTime);


/* Is this channel number a valid DDE channel? 
*/
int IsValidChannel(int Chnl);


/* Is this PDDE a valid DDE channel? 
*/
int IsValidPdde(PDDE pdde);


/* Find Dde channel object by conversation handle
*/
PDDE DdeFindConv(HCONV hConv);


/* Find dde channel object by channel number. 
*/
PDDE DdeFindChnlNum(int channel);


/* Find Dde channel object by application name, topic name. 
*/
PDDE DdeFindChnl(char * app, char * topic);


/* Is this DDE object a channel that hasn't been terminated
   by remote or locally 
*/
int IsActiveChnl(PDDE pdde);


/* This function is called by the DDE manager DLL and passes control onto
   the apropriate function pointed to by the global topic and item arrays.
   It handles all DDE interaction generated by external events.  */

HDDEDATA
/*FCN*/DdeCB(PXFERINFO info);


/* Free the shared data sent to us.  Update DDE structure data. */
void DdeFreeData(PDDE pdde);


void strhandle_msg(char *name);

/* Switch channels using a 0 based list index, for 
   list boxes. 
*/
int SwitchChnlIndex(int ChnlIdx);


/* Switch DDE channel, return 0 if not valid. */
int DdeSwitchChnl(int ChnlNum);


/* Run the DDE dialog */

int DdeDialog(HWND hwnd);


/* Save and restore user options from INI file */

void DdeWriteProfile(char *profFile);


int DdeGetProfile(char *profFile);




