Logo

Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.

DDEInitiate(), DDETerminate(), DDEAbortTrans(), DDEPoke(), DDERequest(), DDEExecute(), DDELastError(), DDESetOption(), DDEAdvise(), DDEEnabled(), DDESetService(), DDESetTopic()

Despite all the hoopla you’ve heard about the wonders of OLE, COM and ActiveX, Dynamic Data Exchange—one of COM’s forebears—is not yet dead. Getting on in years, yes. Dead, no, not just yet.

In most cases, we prefer to use the COM technologies when developing communications between applications. COM communications are more robust, support richer data types, and are more easily integrated into an OOP environment. But it takes two to tango. If the application on the other end of the phone speaks only DDE, then that is the language FoxPro must speak, too. Visual FoxPro supports the whole complement of DDE commands, providing capability as a client or a server.

In case you’re new to the DDE scene, let’s do a brief overview of the ways in which DDE can be used. DDE is often described as similar to a phone conversation. One party initiates the phone call, the two parties exchange information, and the parties terminate the call.

If VFP is the calling application—if we’re driving the conversation—we first DDEInitiate() the conversation with the other party. This other application is commonly referred to as the server, confusing the situation with client-server database technology (as well as with Web technology). The client in DDE is the one running the show, running the code, and the server is the application that responds. DDERequest() and DDEPoke() provide the verbs to get and set variable values. DDEExecute() passes a command to the server. DDETerminate() completes the conversation.

DDELastError() is the function used to get the details when things go wrong.

It may be that we don’t want to talk with the server as much as register an interest in the values it has in a particular document, and ask to be advised if these change. DDEAdvise() is the trick for this. Again, you DDEInitiate() and DDETerminate() a conversation, with the request for DDEAdvise() in the middle.

Finally, if your application must play the server and be called upon by other applications, DDESetService() defines the services available, DDESetTopic() sets (surprise!) the topics of the conversations, and DDEEnable() allows the server to toggle on and off DDE services while processing.

As we said before, DDE is never our first choice for interprocess communication mechanisms. But for those applications that support only DDE, or in situations of limited resources, Visual FoxPro is up to the task of communicating well using DDE. Visual FoxPro can carry on a variety of conversations, working as a client with cold, warm and hot links, as well as working as a DDE server.

If you’re really interested in the DDE server stuff, a trip down memory lane is required. The FoxPro development environment itself is not a DDE server, but rather the interface is in place for you to hook up your own application as a DDE server. Not much is documented in this version, but Mike Taylor of MicroMega wrote a cool application, FoxData, as a FoxPro DDE server. It was included with the sample code for FoxPro 2.x, and it demonstrated some neat little nooks and crannies in getting FoxPro to work. In addition, several excellent magazine articles and at least one book (listed in the references in the appendices) get into DDE in some depth.

Usage

nChannel = DDEInitiate( cAppDDEName, cTopic )
lReturn = DDETerminate( nChannel | cAppDDEName )

Parameter

Value

Meaning

cAppDDEName

Character

Also known as a "service name," this is the handle an application uses to identify itself via DDE, like a trucker's CB radio handle. Contrary to the Help file, Visual FoxPro uses "FoxPro." Excel uses "Excel." Word uses "WinWord." Ain't standards wonderful?

If cAppDDEName is supplied with DDETerminate, all channels with this server are closed at once.

cTopic

Character

The topic of your conversation. "System" is often a good icebreaker for speaking with an application you're trying to get acquainted with.

nChannel

-1

Indicates DDEInitiate could not establish a channel. Use DDELastError to determine why.

Positive Integer

The channel number that uniquely identifies this conversation for use with the other DDE functions.

lReturn

.F.

Indicates DDETerminate could not close a channel. The other application may have already terminated, or an invalid channel number might have been passed. Use DDELastError to determine why.

.T.

Successful termination.

DDEInitiate() is used to start a conversation between two applications, and DDETerminate() to finish it. Negative or false returns from these functions (and the ones below) are indicative of errors that should be checked out with DDELastError().

DDEInitiate() attempts to start a conversation with another application by searching the list of DDE servers available in memory. By default, if the server isn’t running, FoxPro offers to start the server by popping a dialog up on the screen. Typically, this is an option you will not want to offer the end user. You probably want to start the application if it isn’t running. Use DDESetOption() and the RUN command to do this, as shown in the second example below.

Most of the tricks to using DDE involve finding out the names of the services and topics available, and the commands and syntax the application recognizes. Far too often these are poorly documented, if they’re documented at all.

Example

? DDEInitiate("WinWord","Document1")

= DDESetOption("Safety",.F.)  && Turn off dialogs
nChannel = -1
nTries = 0
DO WHILE nChannel = -1 and nTries < 3
  nChannel = DDEINITIATE("WinWord","System")
  IF nChannel = -1  && Command failed
    IF DDELASTERROR() = 16  && Connect failure
      nTries = nTries + 1
      * You'll need to fully qualify the path to
      * your server, below, or this will fail.
      RUN /N7 WINWORD.EXE
    ENDIF
  ENDIF
ENDDO
IF nTries = 3  && Three tries and you're out...
  WAIT WINDOW "Three strikes, you're out."
ELSE
  WAIT WINDOW "Good to go!"
ENDIF

Usage

lResult = DDEAbortTrans( nChannel )
uResult = DDEPoke( nChannel, cItemName, cData
                   [ , cFormat [ , cAsynchUDF ] ] )
uValue = DDERequest( nChannel, cItemName
                     [ , cFormat [ , cAsynchUDF ] ] )

Parameter

Value

Meaning

nChannel

Integer

Identifies the particular DDE conversation, set by DDEInitiate().

lResult

.T.

The attempt to abort the transaction succeeded.

.F.

Unable to abort the transaction. We haven't been able to produce this at will, but we suspect it would occur only when connection with the other party in the DDE conversation was lost.

cItemName

Character

The item whose data is either being requested or changed. For Excel, this could be a row/column address such as "R2C3".

cData

Character

The data to be placed in cItemName. All data must be passed as a character string and the receiving application must translate it to numeric, if necessary.

cFormat

Character

Dictates the format the data appears in, most often CF_TEXT, which is tab-separated fields.

cASynchUDF

Character

If an asynchronous connection is desired (usually because the answer might take some time), this parameter supplies the name of the function to run when the data is ready. The six pieces of data this function receives are spelled out in the Help file.

uResult

Logical

Reports whether the data was accepted by the application. Use DDELastError() to get information on the error if lResult is .F.

Integer

If an asynchronous transaction is selected, DDEPoke() returns the unique transaction number, which is the last parameter to the cAsynchUDF when the transaction is finished.

uValue

Character

The result of querying cItemName.

Integer

The transaction number, which is the last parameter to the cAsynchUDF when the transaction is finished.

DDERequest() and DDEPoke() are the read and write equivalents of transactions via DDE. DDERequest() requests a value from another application and DDEPoke() places a value in it.

In some cases, poking or requesting a value might cause the server application to have to go off and do some number crunching or other time-consuming processing. In this case, an “asynchronous” connection can be made, whereby the server app “calls back” Visual FoxPro when the deed is done, and passes back results by starting a User-Defined Function (UDF) within FoxPro and passing it several parameters. It is up to your application to monitor an asynchronous transaction, because the DDESetOption(“Timeout”) value does not apply. If an excessively long time has passed or other conditions make the transaction no longer necessary, the transaction can be halted with DDEAbortTrans().

Example

* get the value of cell 2,3
nValue = VAL(DDERequest(nExcel,"R2C3"))

Usage

lResult = DDEExecute( nChannel, cCommand )
nResult = DDEExecute( nChannel, cCommand, cAsynchUDF )

Parameter

Value

Meaning

nChannel

Integer

Identifies the particular DDE conversation, set by DDEInitiate().

cCommand

Character

The command for the server to execute. Each DDE server is unique in the commands, syntax and format it understands.

cAsynchUDF

Character

If an asynchronous connection is desired (usually because the answer might take some time), this parameter supplies the name of the function to run when the data is ready. The six pieces of data this function receives are spelled out in the Help file.

lResult

.T.

Synchronous command completed successfully.

.F.

Synchronous command failed. Test using DDELastError().

nResult

-1

Asynchronous command failed. Test using DDELastError().

Integer

The transaction number to be returned as the last parameter of the cAsynchUDF specified above.

DDEExecute() allows Visual FoxPro to pass commands to the DDE Server, which the server then executes.

Example

* Send the command to WinWord to print the current document
=DDEExecute(nWord,"[FilePrint]")

Usage

nValue = DDELastError()

Parameter

Value

Meaning

nValue

0

Last command did not generate an error.

Integer

See table of errors in Help.

Use DDELastError() to detect problems that DDE is having. Rather than firing the global error handler set in ON ERROR, this function gives you the option of handling the error locally.

Example

IF DDELastError() = 0  && no error, continue processing

Usage

lValue = DDESetOption( "Safety" [, lSafetyOn ] )
lValue | nValue  = DDESetOption( "Timeout" [, nTimeout ] )

Parameter

Value

Meaning

lSafetyOn

Logical

Determines whether warning dialogs appear (.T.) or not (.F.). See example in DDEInitiate(), above.

Omitted

Returns present setting of safety in lValue.

lValue

.T.

Function completed successfully.

.F.

Function failed.

nTimeout

Numeric

Amount of time FoxPro waits for a response from the other end of a DDE conversation, expressed in milliseconds. Default is 2 seconds. Legal values from 1 to 594 billion or so—too long for us to wait!

Omitted

Returns current timeout setting in nValue.

nValue

Numeric

Current setting for timeout.

This function is the DDE equivalent of the SET command and matching SET() function. Without the second parameter, it returns the present setting; with the second parameter, it sets the specified setting to the passed values. With Safety set to .F., our recommended setting, you will need to do more error checking, but your users will not be presented with dialogs allowing them to mangle the DDE portion of your application. As for Timeout, we expect that you might need to raise it for slower systems or systems with a lot going on, but we’ve never had to mess with the settings ourselves.

Example

? DDESetOption("Timeout") && Display the current timeout

Usage

lValue = DDEAdvise( nChannel, cItemName, cUDFName, nTypeLink )

Parameter

Value

Meaning

nChannel

Integer

Identifies the particular DDE conversation, set by DDEInitiate().

cItemName

Character

The name of the item the application is to monitor. The server alerts Visual FoxPro if the data changes. For Excel, this might be a cell address.

cUDFName

Character

The name of the routine to run when a change is made to the item identified above.

nTypeLink

0

Manual or "cold" link. Turns off a warm or hot link.

1

Notify or "warm" link.

2

Automatic or "hot" link.

lValue

.T.

Function completed successfully.

.F.

Function failed.

This function allows the creation of “warm” and “hot” links between Visual FoxPro and another application. Both cause a Visual FoxPro routine to run when a change occurs in their environment. The difference between the two link types is that the warm link only notifies Visual FoxPro of the change, while the hot link passes the changed data as well.

The routine you indicate receives six items, similar to the AsynchUDF in DDEPoke() and DDERequest() above. Your Visual FoxPro application can then determine what action to take based on this new data.

Example

* This command sets up a hot link between an open Excel DDE
* conversation and FoxPro. If the operator or a function
* changes the value of the contents of row 2, column 2, the
* FoxPro routine will run.
= DDEAdvise(nExcel, "R2C2", "MyUDF", 2)

Usage

lValue = DDEEnabled( [ nChannel ]  [ , lOnOff ] )
</tr> </table> You have to pass at least one of the two parameters, and you can pass both. `DDEEnabled()` applies only when FoxPro is working as a server. DDE, by default, is turned on globally, but is not enabled for individual channels until the channels are initialized. `DDETerminate()` also disables the local channel. When the channel is enabled, it's possible for incoming DDE traffic to disrupt the middle of a process, so use `DDEEnabled()` to "mute" the channel for a few moments until the function can complete. ### Example ```foxpro =DDEEnable( 5, .T. ) && re-enable DDE channel 5 ``` ### Usage ```foxpro lResult = DDESetService( cName, cAction [, cFormat | lSwitch ] ) ```

Parameter

Value

Meaning

nChannel </td>

Integer

Identifies the particular DDE conversation, set by DDEInitiate().

Omitted

If no channel is specified, the command applies globally to all channels.

lOnOff

Logical

Turns on or off either the specified channel or all channels of DDE while processing a time-critical function.

Omitted

Reports whether the specified channel or all channels currently have DDE enabled.

lValue

.T.

Indicates either that a channel or global DDE processing is enabled, or that the last change to the status was accepted, depending on parameters supplied.

.F.

Channel or global DDE processing is disabled, or the function failed to complete successfully.

Parameter

Value

Meaning

cName

Character

Name of the service to maintain.

cAction

"Advise"

Switch to set notification of changes on or off. The server side of warm and hot links, like DDEAdvise().

"Define"

Defines the name of a new service.

"Execute"

Switch to enable or disable the execution of commands by the service.

"Formats"

Specifies which formats data may be transferred in.

"Poke"

Enables or disables a client's ability to poke data to this service.

"Release"

Releases this service name.

"Request"

Enables or disables DDERequest() messages.

cFormat

Character

Specifies the format in which data can be transferred, using Microsoft shorthand like CF_TEXT (tab-delimited ASCII).

lSwitch

Logical

For Advise, Execute, Poke and Request above, determines whether the feature should be enabled.

Omitted

If both cFormat and lSwitch are omitted, returns the current state of the specified action.

lResult

.T.

If requesting the status of a feature, it's enabled. If attempting to change an item, the change was successful.

.F.

If requesting the status of a feature, it's disabled. If attempting to change an item, the change failed.

Did you ever wonder how DDE servers got their service names? This is how: A good server, like FoxPro, has the ability to programmatically set its own service names and change them on the fly. This is a one-stop-does-it-all command for using FoxPro as a DDE server—it defines all of the service names, the functions they support, and the formats they accept. ### Example ```foxpro * Create a new service, called Queries, which only * accepts requests, and transfers all data as tab-delimited text = DDESetService( "Queries", "Define" ) = DDESetService( "Queries", "Advise", .F. ) = DDESetService( "Queries", "Execute", .F. ) = DDESetService( "Queries", "Poke", .F. ) = DDESetService( "Queries", "Request", .T. ) = DDESetService( "Queries", "Formats", "CF_TEXT" ) ``` ### Usage ```foxpro lValue = DDESetTopic( cService, cTopic [, cUDFtoRun ] ) ```

Parameter

Value

Meaning

cService

Character

A service name, defined with DDESetService(), above.

cTopic

Character

A name for the individual topic.

Empty string

Indicates that the UDF name that follows should be run for all topic names that don't have a UDF defined for them.

cUDFtoRun

Character

The name of the routine to be run when this service and topic are accessed. This UDF receives six parameters, as described in the Help, to indicate what it is to do.

Omitted

If no UDF is specified, the topic name is released.

lValue

.T.

Topic name successfully created or released.

.F.

Command failed. Use DDELastError() to determine why.

`DDESetTopic()` sets the individual topics within the services, and defines which routines should run when a DDE client accesses a topic. ### Example ```foxpro * Define a topic Tables under the Queries service name * which runs the procedure TablProc when called. =DDESetTopic( "Queries", "Tables", "TablProc" ) ``` ### See Also [GetObject()](/section4/s4g297.html), [OLEControl](/section4/s4g518.html), [OLEBoundControl](/section4/s4g518.html), [Run](/section4/s4g230.html)