home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 11 / 11.iso / m / m248 / 4.ddi / DDE.C_ / DDE.C
Encoding:
C/C++ Source or Header  |  1993-02-01  |  14.4 KB  |  570 lines

  1. /* 
  2.  
  3.     DDE.C -- user code for DDE communication
  4.     Copyright 1987-1991 Authorware Inc.
  5.  
  6.     Revsion History
  7.  
  8.         7/25/91    -    Initial version
  9.  
  10.     General Notes:
  11.  
  12.     1) In this application it is assumed that this dll is the client and the
  13.     application it is communicating with is the server.
  14.  
  15.     2) This implementation is for cold link sessions only.
  16.  
  17.     3) A DDE session consists of three parts
  18.         a) Application - this is the name of the application which you are
  19.             communicating with.  This DOES NOT mean the name of the executable.
  20.             It is an internal name for the application. 
  21.  
  22.         b) Topic - this is the general name of the information you are requesting
  23.             data on.  This is internal to the application you are communicating
  24.             with. 
  25.  
  26.         c) Item - this is the specific information based on the topic which the
  27.             DDE session is based on.
  28.  
  29.         See the notes which come with the application you are communicating 
  30.         with for the proper application name, topic and item.  Other sources
  31.         of information on DDE include:
  32.             
  33.             Microsoft (R) Windows SDK 3.0
  34.             Reference Manual Volume 2
  35.  
  36.             Programming for Windows by Charles Petzold 
  37.             Microsoft Press (R)
  38.             Second Edition.
  39.  
  40.  
  41.     Lifecycle of a cold link DDE session:
  42.  
  43.     1) Client sends a WM_DDE_INITIATE message (with the application and topic)
  44.  
  45.     2) Server responds with a WM_DDE_ACK (this lets the client know the server
  46.         window handle).
  47.  
  48.     3) Client sends a WM_DDE_REQUEST (with the item requested and the format 
  49.         for the data to be returned).
  50.  
  51.     4) Server responds with one of the following:
  52.             a) Server sends a WM_DDE_DATA message with the data.
  53.                     - May request the client to respond with a WM_DDE_ACK.
  54.  
  55.             b) Server sends a WM_DDE_ACK (negative response) message to inform
  56.                 the client that the request cannot be filled.
  57.  
  58.     5) Client sends a WM_DDE_TERMINATE message.
  59.  
  60.     6) Server responds with a WM_DDE_TERMINATE message.
  61.  
  62.     Please note: 
  63.     
  64.         a) steps 3 and 4 can be repeated many times.
  65.         b) steps 5 and 6 can be reversed.
  66. */
  67.  
  68. #include "windows.h"
  69. #include "dde.h"
  70.  
  71. // Defines for cbWndExtra bytes on the window class.
  72. #define    SERVERWND        0            // Handle to the server window
  73. #define    SERVERDATA        2            // Returned server data
  74. #define    CLIENTSTATUS    4            // Current status of communication
  75. #define  REQUESTHAND        6            // Copy of the request
  76.  
  77. // CLIENTSTATUS defines - defines current state of the DDE link
  78. #define    IDLESTATUS        0            // Communication established - idle status
  79. #define    INITSTATUS        1            // Initializing the link
  80. #define    REQUESTING        2            // Requesting data
  81. #define    NEWDATA            3            // New data has arrived from the server
  82. #define    EXECUTING        4            // Executing request to the server
  83.  
  84. // Size of a item request field
  85. #define  ITEMLENGTH        256
  86.  
  87. // Request data structure. All item requests use this structure.
  88. typedef struct
  89.     {
  90.     short        type;                        //    Type of data requested - ie CF_TEXT
  91.     char        item[ITEMLENGTH];        // Item name.
  92.     } REQUEST, FAR *REQUEST_PTR;
  93.  
  94. #define VALID_WINDOW(w) ((w) && IsWindow(w))    // Make NULL an invalid window
  95.  
  96. // Class name - for Window registering
  97. static char szClassName[] = "APWDLLDDE";
  98. static WORD        hInst;
  99. static DDEACK    ack;
  100.  
  101. /* Function Prototypes */
  102. short FAR  PASCAL WEP( short bSystemExit );
  103. short    FAR  PASCAL LibMain( HANDLE hModule, WORD wDataSeg, WORD cbHeapSize, 
  104.                                     LPSTR lpszCmdLine );
  105. long     FAR  PASCAL DDEProc( HWND wnd, WORD message, WORD wParam, LONG lParam );
  106.  
  107. static BOOL     NEAR PASCAL DDERequest( HWND DDElink, LPSTR item, short type );
  108.  
  109.  
  110. short    FAR PASCAL LibMain( HANDLE hModule, WORD wDataSeg, WORD cbHeapSize, 
  111.                                         LPSTR lpszCmdLine )
  112. /*
  113.     Is called by LibEntry.  LibEntry is called by Windows when the DLL is
  114.     loaded.  The LibEntry routine is provided in the LIBENTRY.OBJ in the SDK
  115.     Link Libraries disk.  (The source LIBENTRY.ASM is also provided.)  
  116.  
  117.     LibEntry initializes the DLL's heap, if a HEAPSIZE value is specified
  118.     in the DLL's DEF file.  Then LibEntry calls LibMain.  The LibMain function
  119.     below satisfies that call.
  120.              
  121.     The LibMain function should perform additional initialization tasks
  122.     required by the DLL.  In this example, no initialization tasks are
  123.     required.  LibMain should return a value of 1 if the initialization is
  124.     successful.
  125.  
  126.     Returns:
  127.  
  128.         1    if ok
  129.         0    if error
  130. */
  131. {
  132.     WNDCLASS    wc;
  133.  
  134.     hInst = hModule;
  135.  
  136.     /* 
  137.         Register our basic window class. Extra bytes are used to keep track
  138.         of the server window, current status of the DDE link, received data 
  139.         and a copy of the request being sent to the server.
  140.     */
  141.     wc.style                 = 0;
  142.     wc.lpfnWndProc         = DDEProc;
  143.     wc.cbClsExtra             = 0;
  144.     wc.cbWndExtra             = 4*sizeof(short);
  145.     wc.hInstance             = hInst;
  146.     wc.hIcon                 = NULL;
  147.     wc.hCursor                 = NULL;
  148.     wc.hbrBackground         = NULL;
  149.     wc.lpszMenuName         = NULL;
  150.     wc.lpszClassName         = szClassName;
  151.  
  152.     if (!RegisterClass(&wc))
  153.         return 0;
  154.  
  155.     return 1;
  156. }
  157.  
  158.  
  159. short FAR PASCAL WEP( short bSystemExit )
  160. /*
  161.     Performs cleanup tasks when the DLL is unloaded.  WEP() is called
  162.     automatically by Windows when the DLL is unloaded (no remaining tasks
  163.     still have the DLL loaded).  It is strongly recommended that a DLL have a
  164.     WEP() function, even if it does nothing but returns success (1), as in
  165.     this example.
  166. */
  167. {
  168.     return 1;
  169. }
  170.  
  171.  
  172. HWND FAR PASCAL DDEInitialize( LPSTR app, LPSTR topic )
  173. /*
  174.     This function initializes a DDE session with the application "app" on the
  175.     topic "topic". 
  176.     
  177.     Returns:
  178.  
  179.         a handle to the DDE Session, or 0 if an error occured
  180.  
  181.         This handle is to be passed to all subsequent calls to DDE 
  182.         user code functions.
  183. */
  184. {
  185.     ATOM        a_app;
  186.     ATOM        a_top;
  187.     HWND        hServer = 0;
  188.     HWND        DDElink;
  189.  
  190.     // Create an offscreen window to handle all DDE communication
  191.     DDElink = CreateWindowEx(WS_EX_NOPARENTNOTIFY, szClassName, "", WS_POPUP,
  192.                             -1000, -1000, 10, 10, NULL, NULL, hInst, NULL);
  193.  
  194.     if (VALID_WINDOW(DDElink))
  195.     {
  196.         // Set the CLIENTSTATUS to an initialization state
  197.         SetWindowWord(DDElink, CLIENTSTATUS, INITSTATUS);
  198.  
  199.         // Fill in the application and topic supplied by the caller
  200.         if ((a_app = GlobalAddAtom((LPSTR)app)) != 0)
  201.         {
  202.             if ((a_top = GlobalAddAtom((LPSTR)topic)) == 0)
  203.             {
  204.                 GlobalDeleteAtom(a_app);
  205.                 DestroyWindow(DDElink);
  206.                 DDElink = 0;
  207.             }
  208.             else
  209.             {
  210.                 // Send a message out to other applications looking for communication
  211.                 // with "app" on "topic"
  212.                 SendMessage(0xffff, WM_DDE_INITIATE, DDElink, MAKELONG(a_app, a_top));
  213.  
  214.                 // If an application responded its WindowHandle will be stored in 
  215.                 // SERVERWND by DDEProc
  216.                 if ((hServer = GetWindowWord(DDElink, SERVERWND)) == 0)
  217.                 {
  218.                     // Nobody responded
  219.                     DestroyWindow(DDElink);
  220.                     DDElink = 0;
  221.                 }
  222.                 else
  223.                     SetWindowWord(DDElink, CLIENTSTATUS, IDLESTATUS);
  224.             }
  225.         }
  226.         else
  227.         {
  228.             DestroyWindow(DDElink);
  229.             DDElink = 0;
  230.         }
  231.     }
  232.  
  233.     // Returns a handle to the DDElink. Since all information on the DDE session
  234.     // is stored in the cbWndExtra of DDElink's window all information is 
  235.     // accessable from DDElink.
  236.     return DDElink;
  237. }
  238.  
  239.  
  240. void FAR PASCAL DDETerminate( HWND DDElink )
  241. /*
  242.     This function terminates the DDE session associated with the window
  243.     "DDElink".
  244.  
  245.     Returns:
  246.  
  247.         Void
  248. */
  249. {
  250.     HWND        server;
  251.  
  252.     if (VALID_WINDOW(DDElink))
  253.     {
  254.         // Get the server window handle
  255.         server = GetWindowWord(DDElink, SERVERWND);
  256.  
  257.         // Terminate any session which exists
  258.         if (VALID_WINDOW(server))
  259.             PostMessage(server, WM_DDE_TERMINATE, DDElink, 0L);
  260.         else
  261.             DestroyWindow(DDElink);
  262.     }
  263.  
  264.     return;
  265.  
  266.  
  267. BOOL FAR PASCAL DDEExecuteString( HWND DDElink, LPSTR item )
  268. /*
  269.     This function requests the server to execute the contents of string.
  270.  
  271.     The Application and topic MUST have already be set by a call to DDEInitialize.
  272.  
  273.     Returns:
  274.  
  275.         0 -    Failure.
  276.         1 -     Request sent.
  277. */
  278. {
  279.    HANDLE        a_item;
  280.     HWND            server;
  281.     LPSTR            str;
  282.     BOOL            rv = FALSE;
  283.  
  284.     if (VALID_WINDOW(DDElink))
  285.     {
  286.         if (GetWindowWord(DDElink, CLIENTSTATUS) == IDLESTATUS)
  287.         {
  288.             server = GetWindowWord(DDElink, SERVERWND);
  289.             if (VALID_WINDOW(server))
  290.             {
  291.                 // Allocate the necessary memory first
  292.                 if ((a_item = GlobalAlloc(GMEM_DDESHARE, lstrlen(item)+1)) != 0)
  293.                 {
  294.                     // Dupe the string.
  295.                     str = GlobalLock(a_item);
  296.                     lstrcpy(str, item);
  297.                     GlobalUnlock(a_item);
  298.  
  299.                     // Post the request to the server. a_item is freed by the server
  300.                     // if the PostMessage succeeded
  301.                     if (!PostMessage(server, WM_DDE_EXECUTE, DDElink, MAKELONG(0,a_item)))
  302.                             GlobalFree(a_item);
  303.                     else
  304.                     {
  305.                         SetWindowWord(DDElink, CLIENTSTATUS, EXECUTING);
  306.                         rv = TRUE;
  307.                     }
  308.                 }
  309.             }
  310.         }
  311.     }
  312.  
  313.    return rv;
  314. }
  315.  
  316.  
  317. BOOL FAR PASCAL DDERequestString( HWND DDElink, LPSTR item )
  318. /*
  319.     This function requests data from the server in string format.
  320.  
  321.     The Application and topic MUST have already be set by a call to DDEInitialize.
  322.  
  323.     Returns:
  324.  
  325.         0 -    Failure.
  326.         1 -     Request sent.
  327. */
  328. {
  329.     return DDERequest(DDElink, item, CF_TEXT);
  330. }
  331.  
  332.  
  333. static BOOL NEAR PASCAL DDERequest( HWND DDElink, LPSTR item, short type )
  334. /*
  335.     This function requests data from the server. The server window is found in
  336.     the cbWndExtra bytes of DDElink.
  337.  
  338.     The Application and topic MUST have already be set by a call to DDEInitialize.
  339.  
  340.     Returns:
  341.  
  342.         0 -    Failure.
  343.         1 -     Request sent.
  344. */
  345. {
  346.    ATOM          a_item;
  347.     HWND            server;
  348.     HANDLE        req;
  349.     BOOL            rv = FALSE;
  350.     REQUEST_PTR    req_ptr;
  351.  
  352.     if (VALID_WINDOW(DDElink))
  353.     {
  354.         if (GetWindowWord(DDElink, CLIENTSTATUS) == IDLESTATUS)
  355.         {
  356.             server = GetWindowWord(DDElink, SERVERWND);
  357.             if (VALID_WINDOW(server))
  358.             {
  359.                 // Allocate the necessary memory first
  360.                 if ((a_item = GlobalAddAtom((LPSTR)item)) != 0)
  361.                 {
  362.                     if ((req = GlobalAlloc(GHND, sizeof(REQUEST))) != 0)
  363.                     {
  364.                         // Post the request to the server.
  365.                        if (!PostMessage(server, WM_DDE_REQUEST, DDElink, MAKELONG(type,a_item)))
  366.                         {
  367.                             GlobalDeleteAtom(a_item);
  368.                             GlobalFree(req);
  369.                         }
  370.                         else
  371.                         {
  372.                             // Fill in the request data structure
  373.                             req_ptr = (REQUEST_PTR)GlobalLock(req);
  374.                             req_ptr->type = type;
  375.                             lstrcpy(req_ptr->item, (LPSTR)item);
  376.                             GlobalUnlock(req);
  377.                 
  378.                             // Set the CLIENTSTATUS to request made - waiting for response and
  379.                             // store a copy of the request in REQUESTHAND
  380.                             SetWindowWord(DDElink, CLIENTSTATUS, REQUESTING);
  381.                             SetWindowWord(DDElink, REQUESTHAND, req);
  382.                             rv = TRUE;
  383.                         }
  384.                     }
  385.                     else
  386.                         GlobalDeleteAtom(a_item);
  387.                 }
  388.             }
  389.         }
  390.     }
  391.  
  392.    return rv;
  393. }
  394.  
  395.  
  396. HANDLE FAR PASCAL DDEDataString( HWND DDElink )
  397. /*
  398.     This function returns the handle to a string of data which was received
  399.     from the server window in response to a call to DDERequestString.
  400.  
  401.     There must be a call to DDERequestString in order for data to arrive.
  402.  
  403.     The data is returned string format.
  404.  
  405.     Returns:
  406.  
  407.         0            -    No data 
  408.         HANDLE    -  Handle to the data string.
  409. */
  410. {
  411.     HANDLE        hand=0;
  412.  
  413.     if (VALID_WINDOW(DDElink))
  414.     {
  415.         if (GetWindowWord(DDElink, CLIENTSTATUS) == NEWDATA)
  416.         {
  417.             hand = GetWindowWord(DDElink, SERVERDATA);
  418.             SetWindowWord(DDElink, CLIENTSTATUS, IDLESTATUS);
  419.         }
  420.     }
  421.     return hand;
  422. }
  423.  
  424.  
  425. long FAR PASCAL DDEProc( HWND wnd, WORD message, WORD wParam, LONG lParam )
  426. /*
  427.     This function is the window procedure for all DDE communication.  The 
  428.     extra bytes at the end of the Window handle contain all information
  429.     about the DDE session.
  430. */
  431. {
  432.     GLOBALHANDLE        DDE;
  433.     ATOM                    a_app, a_top, a_item;
  434.     DDEDATA FAR            *DDE_ptr;
  435.     char                    szItem[ITEMLENGTH];
  436.     HANDLE                req;
  437.     REQUEST_PTR            req_ptr;
  438.     WORD                    wStatus;
  439.  
  440.     switch (message)
  441.     {
  442.     case WM_DDE_ACK:
  443.  
  444.         wStatus = GetWindowWord(wnd, CLIENTSTATUS);
  445.  
  446.         // Accept an Ack only if we are in a INITSTATUS or EXECUTING
  447.         if (wStatus == INITSTATUS)
  448.         {
  449.             // Store the SERVER window in SERVERWND
  450.             SetWindowWord(wnd, SERVERWND, wParam);
  451.  
  452.             // Clean up the atoms allocated on initialization
  453.             GlobalDeleteAtom(LOWORD(lParam));
  454.             GlobalDeleteAtom(HIWORD(lParam));
  455.         }
  456.         else if (wStatus == EXECUTING)
  457.         {
  458.             // Reset the flag
  459.             SetWindowWord(wnd, CLIENTSTATUS, IDLESTATUS);
  460.  
  461.             // Clean up the global memory
  462.             if (HIWORD(lParam))
  463.                 GlobalFree(HIWORD(lParam));
  464.         }
  465.  
  466.         break;
  467.  
  468.     case WM_DDE_TERMINATE:
  469.  
  470.         // Destroy this window
  471.         DestroyWindow(wnd);
  472.         break;
  473.  
  474.  
  475.     case WM_DDE_DATA:
  476.     
  477.  
  478.             // wParam            -    sending window handle
  479.             // LOWORD lParam    -    DDEDATA memory handle
  480.             // HIWORD lParam    -    item atom.
  481.  
  482.         // Must have complete information
  483.         if ((DDE = LOWORD(lParam)) == 0 || (a_item = HIWORD(lParam)) == 0)
  484.             break;
  485.  
  486.         DDE_ptr = (DDEDATA FAR *)GlobalLock(DDE);
  487.  
  488.         ack.bAppReturnCode = 0;
  489.         ack.reserved = 0;
  490.         ack.fBusy = FALSE;
  491.         ack.fAck = FALSE;
  492.  
  493.         // Data has been sent in response to a request - did we request it?
  494.         if (GetWindowWord(wnd, CLIENTSTATUS) == REQUESTING)
  495.         {
  496.             // We only support CF_TEXT format at this time.
  497.             if (DDE_ptr->cfFormat == CF_TEXT)
  498.             {
  499.                 // Get the atom which tells us if this data is for our request.
  500.                 GlobalGetAtomName(a_item, szItem, sizeof(szItem));
  501.  
  502.                 // Get our request
  503.                 req = GetWindowWord(wnd, REQUESTHAND);
  504.                 if (req)
  505.                 {
  506.                     req_ptr = (REQUEST FAR *)GlobalLock(req);
  507.  
  508.                     // Compare our request with what was sent - are they the same?
  509.                     if (lstrcmp(req_ptr->item, szItem) == NULL)
  510.                     {
  511.                         HANDLE    data;
  512.                         LPSTR        data_ptr;
  513.             
  514.                         // Make a copy of the data sent
  515.                         if ((data = GlobalAlloc(GHND, lstrlen(DDE_ptr->Value)+1)) != 0)
  516.                         {
  517.                             data_ptr = GlobalLock(data);
  518.                             lstrcpy(data_ptr, DDE_ptr->Value);
  519.                             GlobalUnlock(data);
  520.                         }
  521.                 
  522.                         // Store the new data in SERVERDATA and set the CLIENTSTATUS
  523.                         // to NEWDATA informing of new data.
  524.                         SetWindowWord(wnd, SERVERDATA, data);
  525.                         SetWindowWord(wnd, CLIENTSTATUS, NEWDATA);
  526.             
  527.                         ack.fAck = TRUE;
  528.                     }
  529.  
  530.                     // Free our copy of the request - no longer needed.
  531.                     GlobalUnlock(req);
  532.                     GlobalFree(req);
  533.                     SetWindowWord(wnd, REQUESTHAND, 0);
  534.                 }
  535.             }
  536.         }
  537.  
  538.         // If the server wants an acknowledgment send it to him.
  539.         if (DDE_ptr->fAckReq == TRUE)
  540.         {
  541.             wStatus = *(WORD *)&ack;
  542.             if (!PostMessage(wParam, WM_DDE_ACK, wnd, MAKELONG(wStatus, a_item)))
  543.             {
  544.                 GlobalDeleteAtom(a_item);
  545.                 GlobalUnlock(DDE);
  546.                 GlobalFree(DDE);
  547.             }
  548.  
  549.         }
  550.         else
  551.             GlobalDeleteAtom(a_item);
  552.  
  553.         GlobalUnlock(DDE);
  554.  
  555.         // Free up memory if we are supposed to
  556.         if (DDE_ptr->fRelease == TRUE || ack.fAck == FALSE)
  557.             GlobalFree(DDE);
  558.  
  559.         break;
  560.  
  561.     default:
  562.         return DefWindowProc(wnd, message, wParam, lParam);
  563.     }
  564.  
  565.     return 0L;
  566. }
  567.  
  568.  
  569.