PMGuide - Dynamic Data Exchange: Difference between revisions
(5 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
{{IBM-Reprint}} | |||
{{PMGuide}} | |||
The Dynamic Data Exchange (DDE) protocol uses messages to communicate between applications that share data and uses shared memory as the means of exchanging data between applications. Applications can use DDE for one-time data transfers and for ongoing exchanges in which the applications send updates to each other as new data becomes available. This chapter explains how to use DDE in PM applications. | The Dynamic Data Exchange (DDE) protocol uses messages to communicate between applications that share data and uses shared memory as the means of exchanging data between applications. Applications can use DDE for one-time data transfers and for ongoing exchanges in which the applications send updates to each other as new data becomes available. This chapter explains how to use DDE in PM applications. | ||
Line 11: | Line 13: | ||
===DDE System Example=== | ===DDE System Example=== | ||
DDE has many potential uses in real-time data acquisition applications. Consider the example of a DDE-based, real-time system for tracking portfolios. Two hypothetical PM applications cooperate in this example. One application, named the collector, is a specialized interface that draws data from an online data service. The other application is a spreadsheet. Both applications use the DDE protocol. The following figure shows the sample spreadsheet layout: | DDE has many potential uses in real-time data acquisition applications. Consider the example of a DDE-based, real-time system for tracking portfolios. Two hypothetical PM applications cooperate in this example. One application, named the collector, is a specialized interface that draws data from an online data service. The other application is a spreadsheet. Both applications use the DDE protocol. The following figure shows the sample spreadsheet layout: | ||
{| class="wikitable" | |||
|+ Stock Portfolio | |||
|- | |||
! !! A !! B !! C !! D | |||
|- | |||
! 1 || Stock || Shares || Price || Extension | |||
|- | |||
! 2 || ABCD || 1000 || 148 || 148000 | |||
|- | |||
! 3 || EFGH || 2000 || 26 || 52000 | |||
|- | |||
! 4 || IJKL || 200 || 24 || 4800 | |||
|- | |||
! 5 || MNOP || 2000 || 93 || 186000 | |||
|- | |||
! 6 || || || || 390800 | |||
|} | |||
Without DDE, this spreadsheet could be updated by using the clipboard to manually copy numbers from the screen display of the collector application to the spreadsheet. This would require screen sharing or switching between applications. The user also would have to pay close attention to the price data, then undertake the data exchange personally whenever the price data changes. | Without DDE, this spreadsheet could be updated by using the clipboard to manually copy numbers from the screen display of the collector application to the spreadsheet. This would require screen sharing or switching between applications. The user also would have to pay close attention to the price data, then undertake the data exchange personally whenever the price data changes. | ||
Line 45: | Line 51: | ||
. | . | ||
Calls WinDdeInitiate which . | Calls WinDdeInitiate which . | ||
sends WM_DDE_INITIATE | sends WM_DDE_INITIATE ─────────────── . | ||
. | . | ||
. | . | ||
Line 51: | Line 57: | ||
. WinDdeRespond | . WinDdeRespond | ||
. with positive | . with positive | ||
. | . ────────────────── WM_DDE_INITIATEACK | ||
. | . | ||
. | . | ||
Calls WinDdePostMsg which . | Calls WinDdePostMsg which . | ||
posts WM_ADVISE | posts WM_ADVISE ───────────────────── . | ||
. | . | ||
Request to send info each time . | Request to send info each time . | ||
Line 65: | Line 71: | ||
. database and calls | . database and calls | ||
. WinDdePostMsg which posts | . WinDdePostMsg which posts | ||
. | . ────────────────────────── WM_DDE_ACK | ||
. | . | ||
. | . | ||
Line 71: | Line 77: | ||
. for item ABCD, calls | . for item ABCD, calls | ||
. WinDdePostMsg which posts | . WinDdePostMsg which posts | ||
. | . ───────────────────────── WM_DDE_DATA | ||
. | . | ||
Retrieves information from . | Retrieves information from . | ||
Line 82: | Line 88: | ||
When ready to end updates, . | When ready to end updates, . | ||
calls WinDdePostMsg which posts . | calls WinDdePostMsg which posts . | ||
WM_DDE_UNADVISE | WM_DDE_UNADVISE ───────────────────── . | ||
. | . | ||
. Removes request from | . Removes request from | ||
. database and calls | . database and calls | ||
. WinDdePostMsg which posts | . WinDdePostMsg which posts | ||
. | . ────────────────────────── WM_DDE_ACK | ||
. | . | ||
To end DDE transaction, . | To end DDE transaction, . | ||
calls WinDdePostMsg which posts . | calls WinDdePostMsg which posts . | ||
WM_DDE_TERMINATE | WM_DDE_TERMINATE ──────────────────── . | ||
. | . | ||
. Responds by calling | . Responds by calling | ||
. WinDdePostMsg which posts | . WinDdePostMsg which posts | ||
. | . ──────────────────── WM_DDE_TERMINATE | ||
</pre> | </pre> | ||
Line 110: | Line 116: | ||
Finally, unless the spreadsheet initiates other data exchanges under this same topic, it posts a WM_DDE_TERMINATE message to the collector application, indicating the end of the DDE transaction. The collector application responds with a WM_DDE_TERMINATE message. | Finally, unless the spreadsheet initiates other data exchanges under this same topic, it posts a WM_DDE_TERMINATE message to the collector application, indicating the end of the DDE transaction. The collector application responds with a WM_DDE_TERMINATE message. | ||
Note: At any time during the transaction, both the spreadsheet and collector are free to post a WM_DDE_TERMINATE message to the other application. | Note: At any time during the transaction, both the spreadsheet and collector are free to post a WM_DDE_TERMINATE message to the other application. | ||
===Applications, Topics, and Items=== | ===Applications, Topics, and Items=== | ||
DDE uses the three-level hierarchy-application, topic, and item-to uniquely identify a unit of data. An application is the name of the server from which the data is desired. A topic is a logical data context. For applications that operate on file-based documents, topics are usually file names; for other applications, they are other application-specific strings. An item is a data object that can be passed in a DDE transaction. For example, an item might be a single integer, a string, several paragraphs of text, or a bit map. In the collector and spreadsheet model described in the previous section, the application name is collector, the topic name is STOCKS, and the item name is ABCD. | DDE uses the three-level hierarchy-application, topic, and item-to uniquely identify a unit of data. An application is the name of the server from which the data is desired. A topic is a logical data context. For applications that operate on file-based documents, topics are usually file names; for other applications, they are other application-specific strings. An item is a data object that can be passed in a DDE transaction. For example, an item might be a single integer, a string, several paragraphs of text, or a bit map. In the collector and spreadsheet model described in the previous section, the application name is collector, the topic name is STOCKS, and the item name is ABCD. | ||
Line 119: | Line 126: | ||
The system topic must support the items in the following table as well as any other items the application uses: | The system topic must support the items in the following table as well as any other items the application uses: | ||
{|class="wikitable" | |||
!Item||Description | |||
|- | |||
|SZDDESYS_ITEM_FORMATS||A list of strings equivalent to CF_CONSTANTS with the CF_ prefix removed. For example, CF_TEXT = TEXT. | |||
|- | |||
|SZDDESYS_ITEM_HELP||A text description of the server's DDE services. | |||
|- | |||
|SZDDESYS_ITEM_PROTOCOLS||A list of protocol names the server supports. A protocol is a set of DDE execute commands, each having a standard meaning. | |||
|- | |||
|SZDDESYS_ITEM_RESTART||A string that a client can pass to DosExecPgm to invoke a server that is not running. | |||
|- | |||
|SZDDESYS_ITEM_RTNMSG||Supporting detail for the most recently issued WM_DDE_ACK message. This is useful when more than 8 bits of application-specific return code are required. | |||
|- | |||
|SZDDESYS_ITEM_SECURITY||A security-sensitive server application. Any client can initiate a conversation with a security-sensitive server, but the server responds only to the Security topic. Typically, the server requires a password from the client before any further data exchange can take place. | |||
|- | |||
|SZDDESYS_ITEM_STATUS||An indication of the current status of the server: "Ready" or "Busy". | |||
|- | |||
|SZDDESYS_ITEM_SYSITEMS||A list of the items supported under the system topic by this server. | |||
|- | |||
|SZDDESYS_ITEM_TOPICS||A list of the topics currently supported by the application. This can vary from moment to moment. | |||
|} | |||
Individual elements of lists should be delimited by tabs, as in the DDEFMT_TEXT format. | |||
===DDE Initiation=== | ===DDE Initiation=== | ||
A client application initiates a DDE conversation by calling WinDdeInitiate, specifying the server application-name string and the topic-name string. WinDdeInitiate fills a DDEINIT data structure with the specified strings, then sends a WM_DDE_INITIATE message to all frame windows that have HWND_DESKTOP as their parent. The message contains the handle of the client application and a pointer to the DDEINIT data structure. The following figure illustrates the DDEINIT data structure: | A client application initiates a DDE conversation by calling WinDdeInitiate, specifying the server application-name string and the topic-name string. WinDdeInitiate fills a DDEINIT data structure with the specified strings, then sends a WM_DDE_INITIATE message to all frame windows that have HWND_DESKTOP as their parent. The message contains the handle of the client application and a pointer to the DDEINIT data structure. The following figure illustrates the DDEINIT data structure: | ||
Line 219: | Line 200: | ||
===Transaction Status Flags=== | ===Transaction Status Flags=== | ||
DDE client and server applications can specify status flags in the DDESTRUCT data structure. These flags are constant values that applications use to control various aspects of a DDE transaction. They can be combined in the fsStatus word of the DDESTRUCT data structure by using the OR operator. The following table lists the DDE status flags: | DDE client and server applications can specify status flags in the DDESTRUCT data structure. These flags are constant values that applications use to control various aspects of a DDE transaction. They can be combined in the fsStatus word of the DDESTRUCT data structure by using the OR operator. The following table lists the DDE status flags: | ||
{|class="wikitable" | |||
!Flag Name||Description | |||
|- | |||
|DDE_FACK||Indicates a positive acknowledgment. | |||
|- | |||
|DDE_FACKREQ||Requests an acknowledgment from the receiving application. | |||
|- | |||
|DDE_FAPPSTATUS||Indicates that the upper 8 bits of the status word are used for application-specific data. | |||
|- | |||
|DDE_FBUSY||Indicates that the application received a request but cannot respond because it is busy filling an earlier request. | |||
|- | |||
|DDE_FNODATA||Indicates that no data is to be transferred in response to the WM_DDE_ADVISE message. | |||
|- | |||
|DDE_FRESERVED||Reserved; must be 0. | |||
|- | |||
|DDE_FRESPONSE||Indicates a response to a WM_DDE_REQUEST message. | |||
|- | |||
|DDE_NOTPROCESSED||Indicates that the message received is not supported. | |||
|} | |||
===Transaction and Response Messages=== | ===Transaction and Response Messages=== | ||
DDE applications use WinDdePostMsg to communicate during data-exchange transactions. A client application posts transaction messages to a server, which responds by posting acknowledgment messages to the client. Transaction and acknowledgment messages have the same data structure. The first message parameter contains the handle of the sending window; the second contains a pointer to the shared-memory object that contains message information. | DDE applications use WinDdePostMsg to communicate during data-exchange transactions. A client application posts transaction messages to a server, which responds by posting acknowledgment messages to the client. Transaction and acknowledgment messages have the same data structure. The first message parameter contains the handle of the sending window; the second contains a pointer to the shared-memory object that contains message information. | ||
Line 327: | Line 298: | ||
===Unique Data Formats=== | ===Unique Data Formats=== | ||
Whenever an application exchanges data using the DDE protocol, it must specify the format of the data in the usFormat field of the DDESTRUCT data structure. The system-defined standard format for exchanging text data is DDEFMT_TEXT. Applications can also use constant names to specify the format of data to be exchanged listed in the following table: | Whenever an application exchanges data using the DDE protocol, it must specify the format of the data in the usFormat field of the DDESTRUCT data structure. The system-defined standard format for exchanging text data is DDEFMT_TEXT. Applications can also use constant names to specify the format of data to be exchanged listed in the following table: | ||
{|class="wikitable" | |||
!Data Format Name||Description | |||
|- | |||
|SZFMT_BITMAP||Specifies that the data is a bit map. | |||
|- | |||
|SZFMT_CPTEXT||Specifies text whose format is defined by a CPTEXT data structure. Applications can use this format to pass multiple-language strings without changing the conversation context. | |||
|- | |||
|SZFMT_DIF||Specifies that the data is in Data Image Format (DIF). | |||
|- | |||
|SZFMT_DSPBITMAP||Specifies that the data is a bit-map representation of a private data format. | |||
|- | |||
|SZFMT_DSPMETAFILE||Specifies that the data is a metafile representation of a private data format. | |||
|- | |||
|SZFMT_DSPMETAFILEPICT||Specifies that the data is a metafile picture representation of a private data format. | |||
|- | |||
|SZFMT_DSPTEXT||Specifies that the data is a text representation of a private data format. | |||
|- | |||
|SZFMT_LINK||Specifies that the data is in link-file format. | |||
|- | |||
|SZFMT_METAFILE||Specifies that the data is a metafile. | |||
|- | |||
|SZFMT_METAFILEPICT||Specifies that the data is a metafile picture defined by an MFP data structure. | |||
|- | |||
|SZFMT_OEMTEXT||Specifies that the data is in OEM Text format. | |||
|- | |||
|SZFMT_PALETTE||Specifies that the data is in palette format. | |||
|- | |||
|SZFMT_SYLK||Specifies that the data is in Synchronous Link format. | |||
|- | |||
|SZFMT_TEXT||Specifies that the data is an array of text characters. These characters can include new-line characters to indicate linebreaks. The zero-length character indicates the end of the text data. | |||
|- | |||
|SZFMT_TIFF||Specifies that the data is in Tag Image File Format (TIFF). | |||
|} | |||
Applications can define their own data formats. However, each nonstandard DDE format must have a unique identification number. To receive an identification number for a nonstardard format, the application must register the name of the format in the system atom table. Other applications that have the name of the format can then query the system atom table for the format's identification number. This method ensures that all applications use the same atom to identify a format. | |||
===Synchronization Rules=== | ===Synchronization Rules=== | ||
A window processing DDE requests from another window must process them strictly in the order in which the requests were received. | A window processing DDE requests from another window must process them strictly in the order in which the requests were received. |
Latest revision as of 05:04, 20 April 2025
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
The Dynamic Data Exchange (DDE) protocol uses messages to communicate between applications that share data and uses shared memory as the means of exchanging data between applications. Applications can use DDE for one-time data transfers and for ongoing exchanges in which the applications send updates to each other as new data becomes available. This chapter explains how to use DDE in PM applications.
About Dynamic Data Exchange
DDE is different from the clipboard data-transfer component that is also part of this operating system. The clipboard is almost always used as a one-time response to a specific action by the user, such as choosing Paste from a menu. DDE, on the other hand, is often initiated by a user but typically continues without the user's further involvement. DDE is separate from and does not use the clipboard.
DDE always takes place between two applications: a client application and a server application. The client initiates the exchange by requesting that the server perform a particular action, such as supply data. The client's request to the server is called a transaction. If it is able, the server responds by performing the requested action. The important distinction between a client and a server is that the client always initiates DDE transactions.
A server can have many clients simultaneously, and a client can request data from multiple servers. An application can be both a client and a server at the same time. For example, one application could receive data from another application as a client and then act as a server by passing the data to yet another application.
Client and Server Interaction
A DDE conversation actually takes place between two windows: one for each of the participating applications. Applications open a window for each conversation in which they engage. Because a window is identified by its handle, these windows are not necessarily visible. The window belonging to the server application is the server window. The window belonging to the client application is the client window.
DDE System Example
DDE has many potential uses in real-time data acquisition applications. Consider the example of a DDE-based, real-time system for tracking portfolios. Two hypothetical PM applications cooperate in this example. One application, named the collector, is a specialized interface that draws data from an online data service. The other application is a spreadsheet. Both applications use the DDE protocol. The following figure shows the sample spreadsheet layout:
A | B | C | D | |
---|---|---|---|---|
1 | Stock | Shares | Price | Extension |
2 | ABCD | 1000 | 148 | 148000 |
3 | EFGH | 2000 | 26 | 52000 |
4 | IJKL | 200 | 24 | 4800 |
5 | MNOP | 2000 | 93 | 186000 |
6 | 390800 |
Without DDE, this spreadsheet could be updated by using the clipboard to manually copy numbers from the screen display of the collector application to the spreadsheet. This would require screen sharing or switching between applications. The user also would have to pay close attention to the price data, then undertake the data exchange personally whenever the price data changes.
With DDE, this system could be much more automatic, providing the spreadsheet with the current values for multiple data items, without user intervention. DDE enables the user to set up an exchange between the two applications that updates the spreadsheet whenever a change occurs in the value of specified stocks. After this connection is established, the cell values in the spreadsheet always reflect the most current data available from the collector. This system facilitates the timely analysis of real-time data.
The usefulness of the DDE protocol is not restricted to specialized real-time data-acquisition applications. Productivity software, in general, can benefit significantly from the protocol. For example, a monthly report is prepared using word processor, and the report includes graphs generated in a separate business-graphics package. Without DDE, someone must manually copy and paste each month's new graphs into each month's report. With DDE, the word processor can establish a permanent link to the graphics application so that any changes made to the graphs are reflected in the word-processing document, either automatically or on request.
The following diagram shows a detailed view of the workings of the DDE protocol and describes the collector and spreadsheet interaction and illustrates the forwarding of stock quotes from the collector application to the spreadsheet. For simplicity, this example is limited to the exchange of quotes for a single stock, ABCD.
┌──────────────────────────────────────────────────────────────────────────────┐ │ Spreadsheet Collector │ │ (Client) (Server) │ └──────────────────────────────────────────────────────────────────────────────┘ . Start application . Start application . . Load stock-portfolio document . . Calls WinDdeInitiate which . sends WM_DDE_INITIATE ─────────────── . . . . Accepts and calls . WinDdeRespond . with positive . ────────────────── WM_DDE_INITIATEACK . . Calls WinDdePostMsg which . posts WM_ADVISE ───────────────────── . . Request to send info each time . data item ABCD changes; send . in format DDEFMT_TEXT . . . . Records request in . database and calls . WinDdePostMsg which posts . ────────────────────────── WM_DDE_ACK . . . Whenever data changes . for item ABCD, calls . WinDdePostMsg which posts . ───────────────────────── WM_DDE_DATA . Retrieves information from . shared-memory object indicated . by pointer in WM_DDE_DATA . . Updates value of ABCD in . spreadsheet . . When ready to end updates, . calls WinDdePostMsg which posts . WM_DDE_UNADVISE ───────────────────── . . . Removes request from . database and calls . WinDdePostMsg which posts . ────────────────────────── WM_DDE_ACK . To end DDE transaction, . calls WinDdePostMsg which posts . WM_DDE_TERMINATE ──────────────────── . . . Responds by calling . WinDdePostMsg which posts . ──────────────────── WM_DDE_TERMINATE
The collector DDE server application is started first. Typically, applications designed to operate as dedicated DDE servers have a user interface for initialization, and then run minimized. As part of the initialization process, the collector DDE server application performs the necessary tasks (such as entering passwords and testing) to ensure that it can provide data to clients.
The spreadsheet is started next, and the stock-portfolio document is loaded. At this time, the spreadsheet calls WinDdeInitiate, which sends a WM_DDE_INITIATE message to all top-level frame windows, that is, frame windows that have HWND_DESKTOP as their parent. The WM_DDE_INITIATE message is a request to initiate an exchange with an application on a specified topic-in this case, STOCKS. An application can accept this message by responding with a positive WM_DDE_INITIATEACK message, or decline the message by passing the message on to WinDefWindowProc. If no application accepts the request, the spreadsheet assigns an error value to the external reference and its DDE activity concludes.
If the collector acknowledges the request, the spreadsheet can use the newly established exchange to request that the collector provide continuous updates on a specified data item. To make this request, the spreadsheet posts a WM_DDE_ADVISE message to the collector (actually, to a window within the collector that acts as the message recipient for DDE messages), indicating that updates must be sent every time there is a new value available for the data item, ABCD, and that the updates should be in a particular format-for example, DDEFMT_TEXT.
Upon receiving this message, the collector application records the request in its database and posts a WM_DDE_ACK message to the spreadsheet. From then on, whenever the collector receives a new ABCD stock quote, it posts a WM_DDE_DATA message to the window in the spreadsheet that initiated the exchange. Each of these messages carries a pointer to a shared-memory object that contains the data, rendered in the requested format. When the spreadsheet receives such a message, it retrieves the data from the referenced memory object and uses the data to update the value of the cell containing the external reference.
The periodic updates continue until the spreadsheet document is closed. At that point, the spreadsheet application posts a WM_DDE_UNADVISE message to the collector application, indicating that further updating is unnecessary. Upon receipt of this message, the collector application removes the corresponding data request from its database and posts a positive WM_DDE_ACK message back to the spreadsheet.
Finally, unless the spreadsheet initiates other data exchanges under this same topic, it posts a WM_DDE_TERMINATE message to the collector application, indicating the end of the DDE transaction. The collector application responds with a WM_DDE_TERMINATE message.
Note: At any time during the transaction, both the spreadsheet and collector are free to post a WM_DDE_TERMINATE message to the other application.
Applications, Topics, and Items
DDE uses the three-level hierarchy-application, topic, and item-to uniquely identify a unit of data. An application is the name of the server from which the data is desired. A topic is a logical data context. For applications that operate on file-based documents, topics are usually file names; for other applications, they are other application-specific strings. An item is a data object that can be passed in a DDE transaction. For example, an item might be a single integer, a string, several paragraphs of text, or a bit map. In the collector and spreadsheet model described in the previous section, the application name is collector, the topic name is STOCKS, and the item name is ABCD.
The System Topic
The system topic provides a context for information that might be of general interest to any partners in a DDE transaction. Server applications are encouraged to support the system topic at all times. The string used for the system topic is defined in the PM header files as SZDDESYS_TOPIC.
DDE applications should request an exchange on the system topic with a zero-length application name when they start up, to find out what kinds of information other DDE-capable programs can provide.
The system topic must support the items in the following table as well as any other items the application uses:
Item | Description |
---|---|
SZDDESYS_ITEM_FORMATS | A list of strings equivalent to CF_CONSTANTS with the CF_ prefix removed. For example, CF_TEXT = TEXT. |
SZDDESYS_ITEM_HELP | A text description of the server's DDE services. |
SZDDESYS_ITEM_PROTOCOLS | A list of protocol names the server supports. A protocol is a set of DDE execute commands, each having a standard meaning. |
SZDDESYS_ITEM_RESTART | A string that a client can pass to DosExecPgm to invoke a server that is not running. |
SZDDESYS_ITEM_RTNMSG | Supporting detail for the most recently issued WM_DDE_ACK message. This is useful when more than 8 bits of application-specific return code are required. |
SZDDESYS_ITEM_SECURITY | A security-sensitive server application. Any client can initiate a conversation with a security-sensitive server, but the server responds only to the Security topic. Typically, the server requires a password from the client before any further data exchange can take place. |
SZDDESYS_ITEM_STATUS | An indication of the current status of the server: "Ready" or "Busy". |
SZDDESYS_ITEM_SYSITEMS | A list of the items supported under the system topic by this server. |
SZDDESYS_ITEM_TOPICS | A list of the topics currently supported by the application. This can vary from moment to moment. |
Individual elements of lists should be delimited by tabs, as in the DDEFMT_TEXT format.
DDE Initiation
A client application initiates a DDE conversation by calling WinDdeInitiate, specifying the server application-name string and the topic-name string. WinDdeInitiate fills a DDEINIT data structure with the specified strings, then sends a WM_DDE_INITIATE message to all frame windows that have HWND_DESKTOP as their parent. The message contains the handle of the client application and a pointer to the DDEINIT data structure. The following figure illustrates the DDEINIT data structure:
typedef struct _DDEINIT { ULONG cb; PSZ pszAppName; PSZ pszTopic; USHORT usConvContext; } DDEINIT;
Because the message is sent rather than posted, WinDdeInitiate requires a response from all recipients of the message before it returns control to the client application.
Any potential server must contain a server window, a top-level frame window that has been subclassed to receive and process WM_DDE_INITIATE messages. When a server window receives WM_DDE_INITIATE, it examines the application-name and topic-name strings in the DDEINIT data structure. If the application-name string matches and the server supports the requested topic, the server acknowledges the client's request.
Either the application-name or topic-name string can be zero-length. If the application-name string is zero-length, all servers check the topic-name string. Each server that supports the topic sends a separate acknowledgment to the client. If the topic-name string is zero-length, the server sends an acknowledgment for each supported topic. Using zero-length strings, a client can obtain the names of all the active servers in the system or the names of all the topics a server supports.
The following pseudocode shows how servers respond to WM_DDE_INITIATE messages:
If (specific app requested and server is instance of app) or (specific app not requested) { If (specific topic requested) If (server can support topic) acknowledge the requested topic else acknowledge each supported topic }
A server acknowledges its support of a specific topic by calling WinDdeRespond, specifying the handle of its server window, its application name, and the name of the supported topic. WinDdeRespond fills a DDEINIT data structure with the specified strings, then sends a WM_DDE_INITIATEACK message to the client. The message contains the handle of the server window and a pointer to the DDEINIT data structure. The client examines the topic-name string in the DDEINIT data structure and decides whether to begin a transaction on the topic.
If two applications agree on some unspecified protocol and can exchange window handles by some means, they can use DDE messages on those window handles without going through an initiate sequence.
An application does not need to fill in a DDEINIT data structure; WinDdeInitiate and WinDdeRespond automatically fill the data structure. However, applications must extract the application name and topic name from the DDEINIT data structure when receiving a WM_DDE_INITIATE or WM_DDE_INITIATEACK message.
After initiating a conversation, the client interacts with the server by issuing transactions. A transaction is a client's request that the server perform a particular action.
To issue a transaction, the client allocates a shared-memory object, writes data about its request to the object using a DDESTRUCT data structure, then calls WinDdePostMsg to post a transaction message to the server. The transaction message contains the client-window handle and a pointer to the shared-memory object. When the server receives the message, it uses the pointer to access the shared-memory object.
The server responds by allocating a shared-memory object, writing its response to the object using a DDESTRUCT data structure, then calling WinDdePostMsg to post a response message to the client. The response message contains the server-window handle and a pointer to the shared-memory object.
A DDESTRUCT data structure occupies the first part of the memory object. Next comes the item-name string, followed by the actual data being exchanged. The offset fields of the DDESTRUCT data structure must be set to point to the name string and the beginning of the data. The cbData field also must be set to indicate the number of bytes of data.
The sender of a DDE transaction message must allocate a shared-memory object using DosAllocSharedMem, then call DosGiveSharedMem to share the object with the receiving application. To share an object, the sender must know the process identifier of the recipient. The process identifier can be obtained by calling WinQueryWindowProcess for the recipient's window handle. WinDdePostMsg also gives the memory object.
The sender should not try to access the object after sending it to the recipient in a DDE message. After posting a transaction message, WinDdePostMsg automatically frees the shared-memory object from the sender's virtual address space. An application need not call DosFreeMem for this purpose. However, the recipient must call DosFreeMem when it is finished using the object.
Transaction Status Flags
DDE client and server applications can specify status flags in the DDESTRUCT data structure. These flags are constant values that applications use to control various aspects of a DDE transaction. They can be combined in the fsStatus word of the DDESTRUCT data structure by using the OR operator. The following table lists the DDE status flags:
Flag Name | Description |
---|---|
DDE_FACK | Indicates a positive acknowledgment. |
DDE_FACKREQ | Requests an acknowledgment from the receiving application. |
DDE_FAPPSTATUS | Indicates that the upper 8 bits of the status word are used for application-specific data. |
DDE_FBUSY | Indicates that the application received a request but cannot respond because it is busy filling an earlier request. |
DDE_FNODATA | Indicates that no data is to be transferred in response to the WM_DDE_ADVISE message. |
DDE_FRESERVED | Reserved; must be 0. |
DDE_FRESPONSE | Indicates a response to a WM_DDE_REQUEST message. |
DDE_NOTPROCESSED | Indicates that the message received is not supported. |
Transaction and Response Messages
DDE applications use WinDdePostMsg to communicate during data-exchange transactions. A client application posts transaction messages to a server, which responds by posting acknowledgment messages to the client. Transaction and acknowledgment messages have the same data structure. The first message parameter contains the handle of the sending window; the second contains a pointer to the shared-memory object that contains message information.
The DDE protocol defines five transaction types:
Advise Unadvise Request Poke Execute
These transactions are permitted only within an exchange begun by using the WM_DDE_INITIATE message. Each transaction type has a corresponding message that a client uses to initiate the transaction with a server:
WM_DDE_ADVISE WM_DDE_UNADVISE WM_DDE_REQUEST WM_DDE_POKE WM_DDE_EXECUTE
A server acknowledges a transaction message by posting a WM_DDE_ACK message to the client. The client must examine the status field of the DDESTRUCT data structure to determine whether the response is positive or negative.
A server application posts a WM_DDE_DATA message to the client to indicate that requested data is available. If the status bit of the DDESTRUCT structure has the DDE_FACKREQ flag set, the client must acknowledge receipt of the data by sending a WM_DDE_ACK message to the server.
The fifth parameter of WinDdePostMsg is a flag used to specify whether to try to post a message again if the first attempt failed because the destination queue was full (server returns the DDE_FBUSY flag). If the retry flag is set, WinDdePostMsg posts the message at 1-second intervals until the message is posted successfully.
The following sections explain the five basic types of DDE transactions and the messages involved with each. These messages are posted with WinDdePostMsg, which automatically builds and fills a DDEINIT data structure.
Request and Poke Transactions
A client application can use the DDE protocol to obtain a data item from a server (WM_DDE_REQUEST) or to submit a data item to a server (WM_DDE_POKE).
The client posts a WM_DDE_REQUEST message to the server, specifying an item and format by allocating a shared-memory object, filling in a DDESTRUCT data structure, and passing the data structure to WinDdePostMsg.
If the server is unable to satisfy the request, it sends the client a negative WM_DDE_ACK message. If the server can satisfy the request, it renders the item in the requested format, includes it with a DDESTRUCT data structure in a shared-memory object, and posts a WM_DDE_DATA message to the client.
Upon receiving a WM_DDE_DATA message, the client processes the data item. At the beginning of the shared-memory object, the DDESTRUCT data structure contains a status word indicating whether the sender requested an acknowledgment message. If the DDE_FACKREQ bit of the status word is set, the client must send the server a positive WM_DDE_ACK message.
Upon receiving a negative WM_DDE_ACK message, the client can ask for the same item again, specifying a different DDE format. Typically, a client first asks for the most complex format it can support, then steps down, if necessary, through progressively simpler formats, until it finds one the server can provide.
Advise and Unadvise Transactions
A client application can use DDE to establish a link to an item in a server application. When such a link is established, the server sends periodic updates about the linked item to the client (typically, whenever the data associated with the item in the server application has changed). A permanent data stream is established between the two applications and remains in place until it is explicitly disconnected.
The client sends the server a WM_DDE_ADVISE message to set up the data link. The advise message contains a shared-memory pointer containing a DDESTRUCT data structure with the item name, format information, and status information.
If the server has access to the requested item and can render it in the desired format, the server records the new link, then sends the client a positive WM_DDE_ACK message. Until the client issues a WM_DDE_UNADVISE message, the server sends data messages to the client every time a change occurs in the source data associated with the item in the server application.
If the server is unable to satisfy the request, it sends the client a negative WM_DDE_ACK message.
When a link is established with the DDE_FNODATA status bit cleared, the client is sent the data each time the data changes. In such cases, the server renders the new version of the item in the previously specified format and posts a WM_DDE_DATA message to the client.
When the client receives a WM_DDE_DATA message, it extracts data from the shared-memory object by using the DDESTRUCT data structure at the beginning of the object. If the DDE_FACKREQ status bit in the status word of the DDESTRUCT data structure is set, the client must post a positive WM_DDE_ACK message to the server.
When a link is established with the DDE_FNODATA status flag set, a notification, not the data itself, is posted to the client each time the data changes. In this case, the server does not render the new version of the item when the source data changes, but simply posts a WM_DDE_DATA message with 0 bytes of data and the DDE_FNODATA status flag set.
The client can request the latest version of the data by performing a regular one-time WM_DDE_REQUEST transaction, or it can simply ignore the data-change notice from the server. In either case, if the DDE_FACKREQ status bit is set, the client should send a positive WM_DDE_ACK message to the server.
When a client sends a WM_DDE_ADVISE message on a topic/item pair that is already engaged in an advise loop but has a different format specified, the server interprets this as a request to add an advise loop with the given format requested. Therefore, several advise loops can exist for a given topic/item pair. If a server does not support this extent of advise loops, it rejects the advise request.
Correspondingly, when a server receives a WM_DDE_UNADVISE message, the server must compare the format field with the current format of the advise loop. Only if the specified format is 0, meaning all advise loops, or matches an active advise loop does the server stop the advise loop and return a positive acknowledgment.
To terminate a specific item link, the client posts a WM_DDE_UNADVISE message to the server. The server ensures that the client currently has a link to the specified item in this exchange. If the link exists, the server sends a positive WM_DDE_ACK message to the client and no longer sends updates on the item in this exchange. If the server has no such link, it sends a negative WM_DDE_ACK message.
To terminate all links for a particular exchange, the client application posts a WM_DDE_UNADVISE message with a zero-length item name to the server. The server ensures that the exchange has at least one link currently established. If so, the server posts a positive WM_DDE_ACK message to the client, and no longer sends any updates in the exchange. If the server has no links in the exchange, it posts a negative WM_DDE_ACK message.
Execute Transaction
A PM application can use the DDE protocol to cause commands to be executed in another application. Such remote executions are performed by the WM_DDE_EXECUTE transaction.
To execute a remote command, the client application posts to the server a WM_DDE_EXECUTE message containing a pointer to a shared-memory object that contains a DDESTRUCT data structure and a command string.
The server attempts to execute the specified string according to some agreed-upon protocol. If successful, the server posts a positive WM_DDE_ACK message to the client. If unsuccessful, a negative WM_DDE_ACK message is posted.
DDE Termination
At any time, either the client or the server may terminate an exchange by issuing a WM_DDE_TERMINATE message. Similarly, both the client application and server application must be able to receive a WM_DDE_TERMINATE message at any time.
An application must end its exchanges before terminating. The application posts a WM_DDE_TERMINATE message with a zero-length shared-memory pointer. A WM_DDE_TERMINATE message stops all transactions for a given exchange.
The WM_DDE_TERMINATE message means that the sender sends no further messages in that exchange and that the recipient can destroy its DDE window. The recipient must always send a WM_DDE_TERMINATE message promptly in response; it is not permissible to send a negative, busy, or positive WM_DDE_ACK message instead.
If the original sender of the termination request receives any other message before the WM_DDE_TERMINATE message arrives from the recipient of the request, it should not respond, because the sender of the other message might have already destroyed the window to which the response would be sent.
Unique Data Formats
Whenever an application exchanges data using the DDE protocol, it must specify the format of the data in the usFormat field of the DDESTRUCT data structure. The system-defined standard format for exchanging text data is DDEFMT_TEXT. Applications can also use constant names to specify the format of data to be exchanged listed in the following table:
Data Format Name | Description |
---|---|
SZFMT_BITMAP | Specifies that the data is a bit map. |
SZFMT_CPTEXT | Specifies text whose format is defined by a CPTEXT data structure. Applications can use this format to pass multiple-language strings without changing the conversation context. |
SZFMT_DIF | Specifies that the data is in Data Image Format (DIF). |
SZFMT_DSPBITMAP | Specifies that the data is a bit-map representation of a private data format. |
SZFMT_DSPMETAFILE | Specifies that the data is a metafile representation of a private data format. |
SZFMT_DSPMETAFILEPICT | Specifies that the data is a metafile picture representation of a private data format. |
SZFMT_DSPTEXT | Specifies that the data is a text representation of a private data format. |
SZFMT_LINK | Specifies that the data is in link-file format. |
SZFMT_METAFILE | Specifies that the data is a metafile. |
SZFMT_METAFILEPICT | Specifies that the data is a metafile picture defined by an MFP data structure. |
SZFMT_OEMTEXT | Specifies that the data is in OEM Text format. |
SZFMT_PALETTE | Specifies that the data is in palette format. |
SZFMT_SYLK | Specifies that the data is in Synchronous Link format. |
SZFMT_TEXT | Specifies that the data is an array of text characters. These characters can include new-line characters to indicate linebreaks. The zero-length character indicates the end of the text data. |
SZFMT_TIFF | Specifies that the data is in Tag Image File Format (TIFF). |
Applications can define their own data formats. However, each nonstandard DDE format must have a unique identification number. To receive an identification number for a nonstardard format, the application must register the name of the format in the system atom table. Other applications that have the name of the format can then query the system atom table for the format's identification number. This method ensures that all applications use the same atom to identify a format.
Synchronization Rules
A window processing DDE requests from another window must process them strictly in the order in which the requests were received.
A window does not need to apply this first-in first-out (FIFO) rule between requests from different windows-that is, it may provide asynchronous support for multiple processes. For example, a window might have the following requests in its queue:
Request message from window x Request message from window y Request message from window x
The window must process request message 1 before request message 3, but it does not have to process request message 2 before request message 3. If y has a lower priority than x, the window follows the order 1, 3, 2.
If a server is unable to process an incoming request because it is waiting for an external process, it must post a busy WM_DDE_ACK message to the client, to prevent deadlock. A busy WM_DDE_ACK message can also be sent if the server is unable to process an incoming request quickly.
Language-Sensitive DDE Applications
DDE applications written for the international market must be able to exchange data in several different languages. The CONVCONTEXT data structure, along with WinDdeInitiate and WinDdeRespond, provide this support.
A language-sensitive DDE application defines the context of a conversation by filling a CONVCONTEXT data structure with the appropriate country code and code-page identifiers. The CONVCONTEXT data structure also contains a context flag. If this flag is set to DDECTXT_CASESENSITIVE, applications must compare strings in a case-sensitive manner. Language-sensitive DDE applications use WinDdeInitiate and WinDdeRespond to establish a DDE conversation. These functions pass a pointer to a CONVCONTEXT data structure.
Using Dynamic Data Exchange
This section explains how to perform the following tasks:
- Initiate a DDE conversation
- Create a shared-memory object for DDE
- Send positive acknowledgment messages
- Send negative acknowledgment messages
- Perform a one-time data transfer
- Establish a permanent data link
- Execute commands in a remote application
- Terminate a DDE conversation
Note: Most of the sample code in this section is part of complete programs for either a client application or a server application. Both programs are illustrated in "Sample Code for Dynamic Data Exchange".
Initiating a DDE Conversation
The client application initiates a DDE conversation by calling WinDdeInitiate, specifying the server application-name string and the topic-name string.
The sample client application in "Sample Code for Dynamic Data Exchange" allows the user to initiate a DDE conversation from a context menu. The following code fragment shows how the client application processes that request:
/* User starts DDE conversation */ case IDM_POLL: WinPostMsg(hListWnd, LM_DELETEALL, 0, 0); ShowMessage("Polling..."); context.cb = sizeof(CONVCONTEXT); context.fsContext = 0; WinDdeInitiate(hwnd, szApp, szTopic, &context); ShowMessage("Polling complete."); break;
The following sample code shows how the server application determines whether to send a positive or negative acknowledgment to the WinDdeInitiate call:
/*********************************************************************/ /* Check incoming poll - if the App and Topic match, */ /* we must acknowledge. If both are zero-length, the client is */ /* searching for anyone to talk to - send our names */ /*********************************************************************/ szClientApp = pDDEinit->pszAppName; szClientTopic = pDDEinit->pszTopic; ShowMessage(szClientApp); ShowMessage(szClientTopic); if (!strcmpi(szClientApp, szApp) || !strcmpi(szClientApp, NULL)) { if (!strcmpi(szClientTopic, szTopic) || !strcmpi(szClientTopic, NULL)) { context.cb = sizeof(CONVCONTEXT); context.fsContext = 0; WinDdeRespond(hClientWnd, hwnd, szApp, szTopic, &context); } } break;
The following code fragment shows how to create a shared-memory object for a DDE transaction. The parameters include the destination window for the DDE message, item name for the transaction, status word, format of the data, actual data to be transferred (if any), and the length of the data. The allocated object must be big enough to hold the DDESTRUCT data structure, item name, and the actual data to be transferred. The sample returns a pointer (PDDESTRUCT) to a shared-memory object that is ready to post as part of a DDE message.
/* Get some sharable memory */ DosAllocSharedMem((PVOID)&mem, NULL, sizeof(DDESTRUCT)+21, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_GIVEABLE); /* Get the server's ID and give it access to the */ /* shared memory */ WinQueryWindowProcess(hServerWnd, &pid, &tid); DosGiveSharedMem(&mem, pid, PAG_READ | PAG_WRITE); /* Setup DDE data structures */ /* (11 byte name length, 10 plus NULL, 10 byte data length) */ pDDEdata = (PDDESTRUCT)mem; pDDEdata->cbData = 10; /* Data length */ pDDEdata->fsStatus = 0; /* Status */ pDDEdata->usFormat = DDEFMT_TEXT; /* Text format */ /* Go past end of data structure for the name */ pDDEdata->offszItemName = sizeof(DDESTRUCT); /* Go past end of structure (plus past the name) */ /* for the data */ pDDEdata->offabData = sizeof(DDESTRUCT)+11; strcpy((BYTE *)(pDDEdata+(pDDEdata->offszItemName)), "STATUS"); /* Post our request to the server program */ WinDdePostMsg(hServerWnd, hwnd, WM_DDE_REQUEST, pDDEdata, DDEPM_RETRY);
Sending a Positive Acknowledgment
You can send a positive acknowledgment by posting a WM_DDE_ACK message with the DDE_FACK and DDE_FRESPONSE flags set in the status word of the shared-memory data structure. The following code fragment shows how to do so:
/* Specify the status flags, when allocating shared memory */ pDDEdata->fstatus = DDE_FACK | DDE_FRESPONSE; . . . /* Post the message */ WinDdePostMsg(hwndDest, /* Handle of destination */ hwndSource, /* Handle of source */ WM_DDE_ACK, /* Message */ pDDEdata, /* Shared-memory pointer */ DDEPM_RETRY); /* Retry */
Sending a Negative Acknowledgment
You can send a negative acknowledgment by posting a WM_DDE_ACK message with the DDE_NOTPROCESSED flag set in the status word of the shared-memory data structure. By not specifying DDE_FACK, it is legal to specify DDE_NOTPROCESSED, but only if the message is not supported, such as WM_DDE_POKE for the specified item. DDE_NOTPROCESSED is not the negative respond. The following code fragment shows how to do so:
/* Specify the status flag, when allocating shared memory */ pDDEdata->fstatus &= 0; . . . /* Post the message */ WinDdePostMsg(hwndDest, /* Handle of destination */ hwndSource, /* Handle of source */ WM_DDE_ACK, /* Message */ pDDEdata, /* Shared-memory pointer */ DDEPM_RETRY); /* Retry */
If an application is busy when it receives a DDE message, it can post a WM_DDE_ACK message with the DDE_FBUSY flag set.
Performing a One-Time Data Transfer
A client application posts a WM_DDE_REQUEST or WM_DDE_POKE message to perform a one-time data transfer with a server application. The item-name portion of the shared-memory object passed with the message contains the name of the desired item. When the client posts a WM_DDE_POKE message, the data portion of the shared-memory object contains the data being sent to the server.
If the server can satisfy the request, it renders the item in the requested format and includes it, with a DDESTRUCT data structure, in a shared-memory object and posts a WM_DDE_DATA message to the client, as shown in the following code fragment:
/* The DDE data structure is passed, and */ /* the client should have shared it with us */ pDDEdata = (PDDESTRUCT)mp2; szReqItem = (BYTE *)(pDDEdata+(pDDEdata->offszItemName)); ShowMessage(szReqItem); /* We support item status, but not anything else */ if (!strcmpi(szReqItem, szItem)) { ShowMessage("sending..."); /* Get some sharable memory */ DosAllocSharedMem((PVOID)&mem, NULL, sizeof(DDESTRUCT)+21, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_GIVEABLE); /* Get the server's id and give it access to the */ /* shared memory */ WinQueryWindowProcess(hClientWnd, &pid, &tid); DosGiveSharedMem(&mem, pid, PAG_READ | PAG_WRITE); /* Setup DDE data structures */ /* (11 byte name length, 10 plus NULL, 10 byte data length) */ pDDEdata = (PDDESTRUCT)mem; pDDEdata->cbData = 10; /* Data length */ pDDEdata->fsStatus = 0; /* Status */ pDDEdata->usFormat = DDEFMT_TEXT; /* Text format */ /* Go past end of structure for the name */ pDDEdata->offszItemName = sizeof(DDESTRUCT); /* Go past end of structure (and name) for the data */ pDDEdata->offabData = sizeof(DDESTRUCT)+11; strcpy((BYTE *)(pDDEdata+(pDDEdata->offabData)), szStatus); WinDdePostMsg(hClientWnd, hwnd, WM_DDE_DATA, pDDEdata, DDEPM_RETRY); } else { ShowMessage("rejecting..."); pDDEdata->cbData = 0; /* Data length */ pDDEdata->fsStatus = DDE_NOTPROCESSED; /* Status */ pDDEdata->usFormat = DDEFMT_TEXT; /* Text format */ WinDdePostMsg(hClientWnd, hwnd, WM_DDE_ACK, pDDEdata, DDEPM_RETRY); } ShowMessage("sent...");
Establishing a Permanent Data Link
The client posts a WM_DDE_ADVISE message to the server to set up a permanent data link. The advise message contains a shared-memory pointer containing a DDESTRUCT data structure with the item name, format information, and status information. The following sample code shows how to establish a link:
WinDdePostMsg(hwndServer, /* Handle of server */ hwndClient, /* Handle of client */ WM_DDE_ADVISE, /* Message */ pddeStruct, /* Shared-memory pointer */ DDEPM_RETRY); /* Retry */
When a link is established with the DDE_FNODATA status flag set, a notification, not the data itself, is posted to the client each time the data changes. In this case, the server does not render the new version of the item when the source data changes, but simply posts a WM_DDE_DATA message with 0 bytes of data and the DDE_FNODATA status flag set, as shown in the following code fragment:
/* Specify the data length and status flag, */ /* when allocating shared memory */ pDDEdata->cdData = 0; pDDEdata->fstatus = DDE_FNODATA; . . . /* Post the message */ WinDdePostMsg(hwndClient, /* Handle of client */ hwndServer, /* Handle of server */ WM_DDE_DATA, /* Message */ pddeStruct, /* Shared-memory pointer */ DDEPM_RETRY); /* Retry */
Terminating a Permanant Link
The client terminates a data link by posting a WM_DDE_UNADVISE message to the server, as shown in the following code fragment:
WinDdePostMsg(hwndServer, /* Handle of server */ hwndClient, /* Handle of client */ WM_DDE_UNADVISE, /* Message */ pddeStruct, /* Shared-memory pointer */ DDEPM_RETRY); /* Retry */
Executing Commands in a Remote Application
To execute a remote command, the client application posts to the server a WM_DDE_EXECUTE message containing a pointer to a shared-memory object that contains a DDESTRUCT data structure and a command string, as shown in the following code fragment:
WinDdePostMsg(hwndServer, /* Handle of server */ hwndClient, /* Handle of client */ WM_DDE_EXECUTE, /* Message */ pddeStruct, /* Shared-memory pointer */ DDEPM_RETRY); /* Retry */
Terminating a DDE Conversation
At any time, either the client or the server may terminate a DDE conversation by posting a WM_DDE_TERMINATE message, as shown in the following code fragment:
WinDdePostMsg(hwndDest, /* Handle of destination */ hwndSource, /* Handle of source */ WM_DDE_TERMINATE, /* Message */ NULL, /* No shared-memory pointer */ DDEPM_RETRY); /* Retry */
Sample Code for Dynamic Data Exchange
This section illustrates a complete sample program for both client and server applications involved in dynamic data exchange (DDE). Several parts of this program are explained in "Using Dynamic Data Exchange".
Client Application Sample Code
The client application includes the following files:
DDEC.C DDEC.RC DDEC.H DDEC.DEF DDEC.LNK DDEC.MAK
The following sample shows the client application code:
===============DDEC.C=============== #define INCL_WIN #define INCL_DOS #include <os2.h> #include <stdio.h> #include "ddec.h" #pragma linkage (main,optlink) INT main(VOID); void ShowMessage(PSZ); /***********************************************************************/ /* Main() - program entry point. */ /***********************************************************************/ MRESULT EXPENTRY LocalWndProc(HWND, ULONG, MPARAM, MPARAM); HAB hab; HWND hFrameWnd, hListWnd, hServerWnd; PFNWP SysWndProc; INT main (VOID) { HMQ hmq; FRAMECDATA fcd; QMSG qmsg; if (!(hab = WinInitialize(0))) return FALSE; if (!(hmq = WinCreateMsgQueue(hab, 0))) return FALSE; /***********************************************************************/ /* Setup the frame control data for the frame window. */ /***********************************************************************/ fcd.cb = sizeof(FRAMECDATA); fcd.flCreateFlags = FCF_TITLEBAR | FCF_SYSMENU | FCF_MENU | FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_MINMAX | FCF_TASKLIST; fcd.hmodResources = NULLHANDLE; /***********************************************************************/ /* Set our resource key (so PM can find menus, icons, etc). */ /***********************************************************************/ fcd.idResources = DDEC; /***********************************************************************/ /* Create the frame - it will hold the container control. */ /***********************************************************************/ hFrameWnd = WinCreateWindow(HWND_DESKTOP, WC_FRAME, "DDE Client", 0, 0, 0, 0, 0, NULLHANDLE, HWND_TOP, DDEC, &fcd, NULL); /***********************************************************************/ /* Verify that the frame was created; otherwise, stop. */ /***********************************************************************/ if (!hFrameWnd) return FALSE; /***********************************************************************/ /* Set an icon for the frame window. */ /***********************************************************************/ WinSendMsg(hFrameWnd, WM_SETICON, (MPARAM)WinQuerySysPointer(HWND_DESKTOP, SPTR_FOLDER, FALSE), NULL); /***********************************************************************/ /* Create a list window child. */ /***********************************************************************/ hListWnd = WinCreateWindow(hFrameWnd, WC_LISTBOX, NULL, LS_HORZSCROLL, 0, 0, 0, 0, hFrameWnd, HWND_BOTTOM, FID_CLIENT, NULL, NULL); /***********************************************************************/ /* We must intercept the frame window's messages */ /* (to capture any input from the container control). */ /* We save the return value (the current WndProc), */ /* so we can pass it all the other messages the frame gets. */ /***********************************************************************/ SysWndProc = WinSubclassWindow(hFrameWnd, (PFNWP)LocalWndProc); WinShowWindow(hFrameWnd, TRUE); /***********************************************************************/ /* Standard PM message loop - get it, dispatch i.t */ /***********************************************************************/ while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0)) { WinDispatchMsg(hab, &qmsg); } /***********************************************************************/ /* Clean up on the way out. */ /***********************************************************************/ WinDestroyMsgQueue(hmq); WinTerminate(hab); return TRUE; } /***********************************************************************/ /* LocalWndProc() - window procedure for the frame window. */ /* Called by PM whenever a message is sent to the frame. */ /***********************************************************************/ MRESULT EXPENTRY LocalWndProc(HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2) { PSZ szData; /* DDE strings */ PSZ szApp = "DDEdemo", szTopic = "System"; PSZ szInApp, szInTopic; /* System-defined DDE structures */ CONVCONTEXT context; PDDEINIT pDDEinit; PDDESTRUCT pDDEdata; /* Server process and thread IDs */ PID pid; TID tid; /* Pointer to memory we'll allocate */ ULONG mem; switch(msg) { /* All answers to the WinDDEInitate call arrive here */ case WM_DDE_INITIATEACK: pDDEinit = (PDDEINIT)mp2; szInApp = pDDEinit->pszAppName; szInTopic = pDDEinit->pszTopic; ShowMessage("server answered..."); hServerWnd = (HWND)mp1; break; /* All answers to DDE requests arrive here */ case WM_DDE_DATA: ShowMessage("data in"); pDDEdata = (PDDESTRUCT)mp2; DosGetSharedMem(pDDEdata, PAG_READ | PAG_WRITE); szData = (BYTE *)(pDDEdata+(pDDEdata->offabData)); ShowMessage(szData); break; /* Menu item processing */ case WM_COMMAND: switch (SHORT1FROMMP(mp1)) { /* User starts DDE conversation */ case IDM_POLL: WinPostMsg(hListWnd, LM_DELETEALL, 0, 0); ShowMessage("Polling..."); context.cb = sizeof(CONVCONTEXT); context.fsContext = 0; WinDdeInitiate(hwnd, szApp, szTopic, &context); ShowMessage("Polling complete."); break; /* User requests data from the server */ case IDM_DATA: /* Get some sharable memory */ DosAllocSharedMem((PVOID)&mem, NULL, sizeof(DDESTRUCT)+21, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_GIVEABLE); /* Get the server's ID and give it access */ /* to the shared memory */ WinQueryWindowProcess(hServerWnd, &pid, &tid); DosGiveSharedMem(&mem, pid, PAG_READ | PAG_WRITE); /* Setup DDE data structures */ /* (11 byte name length, 10 plus NULL, */ /* 10 byte data length) */ pDDEdata = (PDDESTRUCT)mem; pDDEdata->cbData = 10; /* Data length */ pDDEdata->fsStatus = 0; /* Status */ pDDEdata->usFormat = DDEFMT_TEXT; /* Text format */ /* Go past end of structure for the name */ pDDEdata->offszItemName = sizeof(DDESTRUCT); /* Go past end of data structure */ /* (plus past the name) for the data */ pDDEdata->offabData = sizeof(DDESTRUCT)+11; strcpy((BYTE *)(pDDEdata+(pDDEdata->offszItemName)), "STATUS"); /* Post our request to the server program */ WinDdePostMsg(hServerWnd, hwnd, WM_DDE_REQUEST, pDDEdata, DDEPM_RETRY); break; /* User terminates the conversation */ case IDM_CLOSE: WinDdePostMsg(hServerWnd, hwnd, WM_DDE_TERMINATE, NULL, DDEPM_RETRY); break; /* User closes the window */ case IDM_EXIT: WinPostMsg(hwnd, WM_CLOSE, 0, 0); break; } break; /* Send the message to the usual WC_FRAME WndProc */ default: return (*SysWndProc)(hwnd, msg, mp1, mp2); break; } return FALSE; } /***********************************************************************/ /* ShowMessage(). */ /***********************************************************************/ void ShowMessage(PSZ szText) { WinPostMsg(hListWnd, LM_INSERTITEM, MPFROMSHORT(LIT_END), szText); } =============== DDEC.RC =============== #include <os2.h> #include "ddec.h" MENU DDEC BEGIN SUBMENU "Commands", IDM_MENU BEGIN MENUITEM "Initiate", IDM_POLL MENUITEM "Data", IDM_DATA MENUITEM "Close", IDM_CLOSE MENUITEM "Exit", IDM_EXIT END END =============== DDEC.H =============== #define DDEC 100 #define IDM_MENU 101 #define IDM_POLL 102 #define IDM_INITIATE 103 #define IDM_DATA 104 #define IDM_CLOSE 105 #define IDM_EXIT 106 =============== DDEC.DEF =============== NAME DDEC WINDOWAPI DESCRIPTION 'PM DDE Client Sample' CODE MOVEABLE DATA MOVEABLE MULTIPLE STACKSIZE 24576 HEAPSIZE 10240 PROTMODE =============== DDEC.LNK =============== ddec.obj ddec.exe ddec.map ddec.def =============== DDEC.MAK =============== CC = icc /c /Ge /Gd- /Se /Re /ss /Gm+ LINK = link386 HEADERS = ddec.h #------------------------------------------------------------------- # A list of all of the object files. #------------------------------------------------------------------- ALL_OBJ1 = ddec.obj all: ddec.exe ddec.res: ddec.rc ddec.h ddec.obj: ddec.c $(HEADERS) ddec.exe: $(ALL_OBJ1) ddec.def ddec.lnk ddec.res $(LINK) @ddec.lnk rc -p -x ddec.res ddec.exe
Server Application Sample Code
The server application includes the following files:
DDES.C DDES.RC DDES.H DDES.DEF DDES.LNK DDES.MAK
The following sample shows the server application code:
===============DDES.C=============== #define INCL_WIN #define INCL_WINDDE #define INCL_DOS #include <os2.h> #include <stdio.h> #include <string.h> #include "ddes.h" #pragma linkage (main, optlink) INT main(VOID); void ShowMessage(PSZ); /***********************************************************************/ /* Main() - program entry point. */ /***********************************************************************/ MRESULT EXPENTRY LocalWndProc(HWND, ULONG, MPARAM, MPARAM); HAB hab; HWND hFrameWnd, hListWnd, hClientWnd; PFNWP SysWndProc; INT main (VOID) { HMQ hmq; FRAMECDATA fcd; QMSG qmsg; if (!(hab = WinInitialize(0))) return FALSE; if (!(hmq = WinCreateMsgQueue(hab, 0))) return FALSE; /***********************************************************************/ /* Setup the frame control data for the frame window. */ /***********************************************************************/ fcd.cb = sizeof(FRAMECDATA); fcd.flCreateFlags = FCF_TITLEBAR | FCF_SYSMENU | FCF_MENU | FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_MINMAX | FCF_TASKLIST; fcd.hmodResources = NULLHANDLE; /***********************************************************************/ /* Set our resource key (so PM can find menus, icons, etc). */ /***********************************************************************/ fcd.idResources = DDES; /***********************************************************************/ /* Create the frame window. */ /***********************************************************************/ hFrameWnd = WinCreateWindow(HWND_DESKTOP, WC_FRAME, "DDE Server", 0, 0, 0, 0, 0, NULLHANDLE, HWND_TOP, DDES, &fcd, NULL); /***********************************************************************/ /* Verify that the frame was created; otherwise, stop. */ /***********************************************************************/ if (!hFrameWnd) return FALSE; /***********************************************************************/ /* Set an icon for the frame window. */ /***********************************************************************/ WinSendMsg(hFrameWnd, WM_SETICON, (MPARAM)WinQuerySysPointer(HWND_DESKTOP, SPTR_FOLDER, FALSE), NULL); /***********************************************************************/ /* Create a list window child. */ /***********************************************************************/ hListWnd = WinCreateWindow(hFrameWnd, WC_LISTBOX, NULL, LS_HORZSCROLL, 0, 0, 0, 0, hFrameWnd, HWND_BOTTOM, FID_CLIENT, NULL, NULL); /***********************************************************************/ /* We must intercept the frame window's messages */ /* (to capture any input from the container control). */ /* We save the return value (the current WndProc), */ /* so we can pass it all the other messages the frame gets. */ /***********************************************************************/ SysWndProc = WinSubclassWindow(hFrameWnd, (PFNWP)LocalWndProc); WinShowWindow(hFrameWnd, TRUE); /***********************************************************************/ /* Standard PM message loop - get it, dispatch it. */ /***********************************************************************/ while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0)) { WinDispatchMsg(hab, &qmsg); } /***********************************************************************/ /* Clean up on the way out. */ /***********************************************************************/ WinDestroyMsgQueue(hmq); WinTerminate(hab); return TRUE; } /***********************************************************************/ /* LocalWndProc() - window procedure for the frame window. */ /* Called by PM whenever a message is sent to the frame. */ /***********************************************************************/ MRESULT EXPENTRY LocalWndProc(HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2) { /* Our inbound DDE stuff */ PSZ szClientApp; PSZ szClientTopic; PSZ szReqItem; /* Our supported DDE stuff */ PSZ szApp = "DDEdemo"; PSZ szTopic = "System"; PSZ szItem = "Status"; PSZ szStatus = "RUNNING"; /* System DDE structures */ CONVCONTEXT context; PDDEINIT pDDEinit; PDDESTRUCT pDDEdata; /* Miscellaneous */ PID pid; TID tid; PVOID mem; switch(msg) { /* All WinDDEInitate calls arrive here */ case WM_DDE_INITIATE: ShowMessage("init"); hClientWnd = (HWND)mp1; pDDEinit = (PDDEINIT)mp2; /* Check incoming poll - if the App and Topic match, */ /* we must acknowledge. If both are NULL, the client is */ /* searching for anyone - send our names */ szClientApp = pDDEinit->pszAppName; szClientTopic = pDDEinit->pszTopic; ShowMessage(szClientApp); ShowMessage(szClientTopic); if (!strcmpi(szClientApp, szApp) || !strcmpi(szClientApp, NULL)) { if (!strcmpi(szClientTopic, szTopic) || !strcmpi(szClientTopic, NULL) ) { context.cb = sizeof(CONVCONTEXT); context.fsContext = 0; WinDdeRespond(hClientWnd, hwnd, szApp, szTopic, &context); } } break; /* Incoming DDE request - get the item name, send the data out. */ case WM_DDE_REQUEST: ShowMessage("request in..."); hClientWnd = (HWND)mp1; /* The DDE structure is passed, and */ /* the client should have shared it with us */ pDDEdata = (PDDESTRUCT)mp2; szReqItem = (BYTE *)(pDDEdata+(pDDEdata->offszItemName)); ShowMessage(szReqItem); /* We support item status, but not anything else */ if (!strcmpi(szReqItem, szItem)) { ShowMessage("sending..."); /* Get some sharable memory */ DosAllocSharedMem((PVOID)&mem, NULL, sizeof(DDESTRUCT)+21, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_GIVEABLE); /* Get the server's id and give it access */ /* to the shared memory */ WinQueryWindowProcess(hClientWnd, &pid, &tid); DosGiveSharedMem(&mem, pid, PAG_READ | PAG_WRITE); /* Setup DDE data structures */ /* (11 byte name length, 10 plus NULL, */ /* 10 byte data length) */ pDDEdata = (PDDESTRUCT)mem; pDDEdata->cbData = 10; /* Data length */ pDDEdata->fsStatus = 0; /* Status */ pDDEdata->usFormat = DDEFMT_TEXT; /* Text format */ /* Go past end of structure for the name */ pDDEdata->offszItemName = sizeof(DDESTRUCT); /* Go past end of structure (and name) for the data */ pDDEdata->offabData = sizeof(DDESTRUCT)+11; strcpy((BYTE *)(pDDEdata+(pDDEdata->offabData)), szStatus); WinDdePostMsg(hClientWnd, hwnd, WM_DDE_DATA, pDDEdata, DDEPM_RETRY); } else { ShowMessage("rejecting..."); pDDEdata->cbData = 0; /* Data length */ pDDEdata->fsStatus = DDE_NOTPROCESSED; /* Status */ pDDEdata->usFormat = DDEFMT_TEXT; /* Text format */ WinDdePostMsg(hClientWnd, hwnd, WM_DDE_ACK, pDDEdata, DDEPM_RETRY); } ShowMessage("sent..."); break; /* Menu item processing */ case WM_COMMAND: switch (SHORT1FROMMP(mp1)) { case IDM_EXIT: WinPostMsg(hwnd, WM_CLOSE, 0, 0); break; default: return (*SysWndProc)(hwnd, msg, mp1, mp2); break; } break; /* Send the message to the usual WC_FRAME WndProc */ default: return (*SysWndProc)(hwnd, msg, mp1, mp2); break; } return (MRESULT)FALSE; }
/***********************************************************************/ /* ShowMessage(). */ /***********************************************************************/ void ShowMessage(PSZ szText) { WinPostMsg(hListWnd, LM_INSERTITEM, MPFROMSHORT(LIT_END), szText); }
=============== DDES.RC =============== #include <os2.h> #include "ddes.h" MENU DDES BEGIN SUBMENU "Commands", IDM_MENU BEGIN MENUITEM "Exit", IDM_EXIT END END
=============== DDES.H =============== #define DDES 100 #define IDM_MENU 1000 #define IDM_EXIT 1001
=============== DDES.DEF =============== NAME DDES WINDOWAPI DESCRIPTION 'PM DDE Server Sample' CODE MOVEABLE DATA MOVEABLE MULTIPLE STACKSIZE 24576 HEAPSIZE 10240 PROTMODE
=============== DDES.LNK =============== ddes.obj ddes.exe ddes.map ddes.def
=============== DDES.MAK =============== CC = icc /c /Ge /Gd- /Se /Re /ss /Gm+ LINK = link386 HEADERS = ddes.h #------------------------------------------------------------------- # A list of all of the object files. #------------------------------------------------------------------- ALL_OBJ1 = ddes.obj all: ddes.exe ddes.res: ddes.rc ddes.h ddes.obj: ddes.c $(HEADERS) ddes.exe: $(ALL_OBJ1) ddes.def ddes.lnk ddes.res $(LINK) @ddes.lnk rc -p -x ddes.res ddes.exe