home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / msysjour / vol02 / 05 / maze.all < prev    next >
Text File  |  1987-12-27  |  30KB  |  1,246 lines

  1. Microsoft Systems Journal
  2. Volume 2; Issue 5; November, 1987
  3.  
  4. Code Listings For:
  5.  
  6.     MAZE
  7.     pp. 13-38
  8.  
  9. Author(s): Kevin P. Welch
  10. Title:     Interprogram COmmunications Using Winows' Dynamic Data Exchange
  11.  
  12.  
  13.  
  14. Figure 12
  15. =========
  16.  
  17.  
  18. CFLAGS=-c -u -AS -FPa -Gsw -Os -Zep
  19.  
  20. maze.obj: maze.h maze.c
  21.    cl $(CFLAGS) maze.c
  22.  
  23. maze.exe: maze.obj maze.def maze.lnk
  24.    link4 @maze.lnk
  25.  
  26.  
  27.  
  28.  
  29. Figure 13
  30. =========
  31.  
  32.  
  33. NAME                Maze
  34.  
  35. DESCRIPTION            'Dynamic Data Exchange Maze'
  36.  
  37. STUB                '\BIN\WINSTUB.EXE'
  38.  
  39. CODE                MOVEABLE 
  40. DATA                MOVEABLE MULTIPLE
  41.  
  42. HEAPSIZE            2048
  43. STACKSIZE            2048
  44.  
  45. EXPORTS            
  46.     MazeWndFn        @1
  47.  
  48.  
  49.  
  50.  
  51.  
  52. Figure 14
  53. =========
  54.  
  55.  
  56. maze /align:16
  57. maze
  58. maze
  59. slibw
  60. maze.def
  61.  
  62.  
  63.  
  64. Figure 15
  65. =========
  66.  
  67.  
  68. /*
  69.  * DDE MAZE - HEADER FILE
  70.  *
  71.  * LANGUAGE : Microsoft C
  72.  * MODEL        : Small
  73.  * STATUS    : Operational
  74.  *
  75.  * 09/22/87 - Kevin P. Welch - initial creation.
  76.  *
  77.  */
  78.  
  79. #define MAZE_COLS            3
  80. #define MAZE_ROWS            5
  81.  
  82. #define SC_GRAB_BALL        0x0100
  83. #define SC_GRAB_FOCUS    0x0101
  84.  
  85. #define HI                HIWORD(lPrm)
  86. #define LO                LOWORD(lPrm)
  87.  
  88. #define RGB_BLACK            RGB(0x00,0x00,0x00)
  89. #define RGB_WHITE            RGB(0xFF,0xFF,0xFF)
  90.  
  91. /*
  92.  * BALL DATA STRUCTURE DEFINITIONS
  93.  *
  94.  */
  95.  
  96. #define BALL_WIDTH    6
  97. #define BALL_HEIGHT    6
  98.  
  99. #define RANDOM_MOTION    (((rand()%2)*2)-1)
  100.  
  101. #define H_BOUNCE(a,b)    ((a<=0)?1:((a>=b)?-1:Ball.iHorzMotion))
  102. #define V_BOUNCE(a,b)    ((a<=0)?1:((a>=b)?-1:Ball.iVertMotion))
  103.  
  104. #define ADVISE_OFF    (!Link[i].bAdviseBall)
  105.  
  106. #define OUTSIDE_LEFT    (Ball.rPosn.left<=Link[i].rHole.left)
  107. #define OUTSIDE_RIGHT    (Link[i].rHole.right<=Ball.rPosn.right)
  108. #define OUTSIDE_TOP    (Ball.rPosn.top<=Link[i].rHole.top)
  109. #define OUTSIDE_BOTTOM    (Link[i].rHole.bottom<=Ball.rPosn.bottom)
  110.  
  111. #define OUTSIDE_WIDTH    ((OUTSIDE_LEFT)||(OUTSIDE_RIGHT))
  112. #define OUTSIDE_HEIGHT    ((OUTSIDE_TOP)||(OUTSIDE_BOTTOM))
  113.  
  114. #define OUTSIDE_HOLE    ((ADVISE_OFF)||(OUTSIDE_WIDTH)||(OUTSIDE_HEIGHT))
  115.  
  116. typedef struct {
  117.     RECT        rPosn;        /* current ball position */
  118.     long        lTimeIn;    /* time ball entered maze */
  119.     int        iHorzMotion;    /* vertical ball motion offset */
  120.     int        iVertMotion;    /* horizontal ball motion offset */
  121.     BOOL        bIsBouncing;    /* ball bouncing flag */
  122. } BALL;
  123.  
  124. /*
  125.  * MAZE DATA STRUCTURE DEFINITIONS
  126.  *
  127.  */
  128.  
  129. #define DISPLAY_WIDTH            (Maze.wWidth+BALL_WIDTH)
  130. #define DISPLAY_HEIGHT            (Maze.wHeight+BALL_HEIGHT)
  131.  
  132. typedef struct {
  133.     WORD        wNum;        /* maze window number */
  134.     WORD        wLinks;        /* number of maze links */
  135.     WORD        wWidth;        /* width of maze client area */
  136.     WORD        wHeight;    /* height of maze client area */
  137.     BOOL        bInitiate;    /* in initiate flag */
  138.     BOOL        bGrabBall;    /* grab the ball flag */
  139.     BOOL        bGrabFocus;    /* grab the focus flag */
  140.     WORD        wGoingAway;    /* maze going away counter */
  141. } MAZE;
  142.  
  143.  
  144. /*
  145.  * COMMUNICATION LINK DATA STRUCTURE DEFINITIONS
  146.  *
  147.  */
  148.  
  149. #define MAX_LINK            32
  150.  
  151. #define HOLE_WIDTH            20
  152. #define HOLE_HEIGHT            14
  153.  
  154. typedef struct {
  155.     HWND        hWnd;        /* client window handle */
  156.     WORD        wNum;        /* client window number */
  157.     RECT        rHole;        /* client hole position */
  158.     BOOL        bAdviseBall;    /* advise client of ball flag */
  159.     BOOL        bAdviseStat;    /* advise client of stats flag */
  160. } LINK;
  161.  
  162.  
  163. /*
  164.  * DYNAMIC DATA EXCHANGE DEFINITIONS
  165.  *
  166.  */
  167.  
  168. #define ACCEPTED        0x8000
  169. #define REJECTED        0x0000
  170.  
  171. #define WM_DDE_INITIATE     0x03e0
  172. #define WM_DDE_TERMINATE    0x03e1
  173. #define WM_DDE_ADVISE         0x03e2
  174. #define WM_DDE_UNADVISE       0x03e3
  175. #define WM_DDE_ACK            0x03e4
  176. #define WM_DDE_DATA           0x03e5
  177. #define WM_DDE_REQUEST       0x03e6
  178. #define WM_DDE_POKE           0x03e7
  179. #define WM_DDE_EXECUTE        0x03e8
  180.  
  181. #define SEND(a,b,c)        SendMessage(a,b,hWnd,c)
  182. #define POST(a,b,c)        PostMessage(a,b,hWnd,c)
  183.  
  184. #define DDE_INITIATE(a,b,c)    SEND(a,WM_DDE_INITIATE,MAKELONG(b,c))
  185. #define DDE_TERMINATE(a)    POST(a,WM_DDE_TERMINATE,0L)
  186. #define DDE_ADVISE(a,b)        Advise(a,hWnd,b)
  187. #define DDE_UNADVISE(a,b)    POST(a,WM_DDE_UNADVISE,MAKELONG(0,b))
  188. #define DDE_ACK(a,b,c)        POST(a,WM_DDE_ACK,MAKELONG(b,c))
  189. #define DDE_DATA(a,b,c,d,e)    Transmit(a,hWnd,WM_DDE_DATA,b,c,d,e)
  190. #define DDE_POKE(a,b,c,d,e)    Transmit(a,hWnd,WM_DDE_POKE,b,c,d,e)
  191.  
  192. typedef struct {
  193.     WORD    fEmpty:12;    /* reserved for future use */
  194.     WORD    fResponse:1;    /* in response to request */
  195.     WORD    fRelease:1;    /* release data */
  196.     WORD    fNoData:1;    /* null data handle ok */
  197.     WORD    fAck:1;        /* Ack expected */
  198.     WORD    cfFormat;    /* clipboard data format */
  199.     BYTE    info[30];    /* data buffer */
  200. } DATA;
  201.  
  202. typedef DATA *        PDATA;
  203. typedef DATA FAR *    LPDATA;
  204.  
  205.  
  206.  
  207.  
  208. Figure 16
  209. =========
  210.  
  211.  
  212. /*
  213.  * DDE MAZE - FUNCTION DEFINITIONS
  214.  *
  215.  * LANGUAGE     : Microsoft C
  216.  * MODEL    : Small
  217.  * STATUS    : Operational
  218.  *
  219.  * 09/22/87 - Kevin P. Welch - initial creation.
  220.  *
  221.  */
  222.  
  223. VOID        BounceBall( HWND );
  224. WORD        DestroyMaze( WORD );
  225. ATOM        BumpGlobalAtom( ATOM );
  226. BOOL        FindLink( WORD *, HWND );
  227. BOOL        Advise( HWND, HWND, ATOM );
  228. HWND        CreateMaze( HANDLE, HANDLE, WORD );
  229. BOOL        Transmit( HWND, HWND, WORD, ATOM, BOOL, LONG, LONG );
  230.  
  231. LPSTR FAR PASCAL    lstrcpy( LPSTR, LPSTR );
  232. LONG FAR PASCAL    MazeWndFn( HWND, WORD, WORD, LONG );
  233.  
  234.  
  235.  
  236.  
  237. Figure 17
  238. =========
  239.  
  240.  
  241. /*
  242.  * DDE MAZE - SOURCE CODE
  243.  *
  244.  * LANGUAGE     : Microsoft C
  245.  * MODEL    : Small
  246.  * STATUS    : Operational
  247.  *
  248.  * 09/22/87 - Kevin P. Welch - initial creation.
  249.  *
  250.  * The DDE Maze demonstrates how Dynamic Data Exchange (DDE) can be
  251.  * used in multiple server, multiple client conversations.  The maze
  252.  * supports two distinct data items - one used in passing the animated
  253.  * ball between instances of the Maze, the other for reporting vital
  254.  * Maze statistics to an interested listener.
  255.  *
  256.  * Note that the DDE techniques demonstrated in this program are
  257.  * a simplification of the general specification.  In ALL cases
  258.  * where you observe differences between this application and the
  259.  * specification, please follow the published protocol.
  260.  *
  261.  * Special thanks to Ed Fries, Geoff Nichols, and David West as
  262.  * they helped me see the forest from the trees!
  263.  *
  264.  */
  265.  
  266. #include <windows.h>
  267. #include "maze.h"
  268. #include "maze.d"
  269.  
  270. ATOM        aWnd;        /* window number item */
  271. ATOM        aBall;        /* bouncing ball topic */
  272. ATOM        aGrab;        /* grab the ball item */
  273. ATOM        aStat;        /* statistics item */
  274. ATOM        aAnyMaze;    /* generic maze atom */
  275. ATOM        aThisMaze;    /* this maze atom */
  276.  
  277. MAZE        Maze;        /* maze data structure */        
  278. BALL        Ball;        /* ball data structure */
  279. LINK        Link[MAX_LINK];    /* maze communications link */
  280.  
  281.  
  282. /*
  283.  * MAZE MAINLINE & MESSAGE PROCESSING LOOP
  284.  *
  285.  *    hInst        current instance handle
  286.  *    hPrevInst    previous instance handle
  287.  *    lpsCmd        execution command line string
  288.  *    wCmdShow        initial show-window option
  289.  *
  290.  * This mainline is responsible for calling the initialization and
  291.  * termination routines in addition to processing and distributing
  292.  * all incoming messages.  Note the revised message processing
  293.  * loop used to animate the bouncing ball.
  294.  *
  295.  */
  296.  
  297. WORD PASCAL WinMain( hInst, hPrevInst, lpsCmd, wCmdShow )
  298.     HANDLE        hInst;
  299.     HANDLE        hPrevInst;
  300.     LPSTR        lpsCmd;
  301.     WORD        wCmdShow;
  302. {
  303.     MSG        Msg;        /* current system message */
  304.     HWND        hWnd;        /* maze window handle */
  305.  
  306.     /* create & initialize maze */
  307.     hWnd = CreateMaze( hInst, hPrevInst, wCmdShow );
  308.  
  309.     /* message processing loop */
  310.     do {
  311.     
  312.         /* retrieve next message */
  313.         if ( Ball.bIsBouncing ) {            
  314.             if ( PeekMessage(&Msg,NULL,0,0,TRUE) ) {
  315.                 if ( Msg.message != WM_QUIT ) {
  316.                     TranslateMessage( &Msg );
  317.                     DispatchMessage( &Msg );
  318.                 }
  319.             } else
  320.                 BounceBall( hWnd );            
  321.         } else 
  322.             if ( GetMessage(&Msg,NULL,0,0) ) {
  323.                 TranslateMessage( &Msg );
  324.                 DispatchMessage( &Msg );
  325.             }
  326.     
  327.     } while ( Msg.message != WM_QUIT );
  328.  
  329.     /* destroy maze & exit */
  330.     exit( DestroyMaze(Msg.wParam) );
  331.  
  332. }
  333.  
  334.  
  335. /*
  336.  * MAZE WINDOW MESSAGE PROCESSING FUNCTION
  337.  *
  338.  *    hWnd            window handle
  339.  *    wMsg            window message number
  340.  *    wPrm            additional message info
  341.  *    lPrm            additional message info
  342.  *
  343.  * This function processes all the messages related to the maze
  344.  * window, including all the DDE messages required in order to
  345.  * participate in one or more conversations.  Note that this
  346.  * window function handles DDE messages for both the client and
  347.  * the server sides of the conversation!
  348.  *
  349.  */
  350.  
  351. LONG FAR PASCAL MazeWndFn( hWnd, wMsg, wPrm, lPrm )
  352.     HWND        hWnd;
  353.     WORD        wMsg;
  354.     WORD        wPrm;
  355.     LONG        lPrm;
  356. {
  357.     WORD        i;        /* channel number */
  358.     LONG        lAck;        /* acknowledgement of message */
  359.  
  360.     /* initialization */
  361.     lAck = FALSE;
  362.     
  363.     /* process message */
  364.     switch( wMsg )
  365.         {
  366.     case WM_CREATE : /* create window */
  367.         
  368.         /* adjust window position if tileable */
  369.         if ( Maze.wNum <= MAZE_ROWS*MAZE_COLS )
  370.             MoveWindow(
  371.                 hWnd,
  372.                 (Maze.wWidth*((Maze.wNum-1)%MAZE_COLS)),
  373.                 (Maze.wHeight*((Maze.wNum-1)/MAZE_COLS)),
  374.                 Maze.wWidth,
  375.                 Maze.wHeight,
  376.                 TRUE
  377.             );
  378.             
  379.         break;
  380.     case WM_SYSCOMMAND : /* system command */
  381.         
  382.         /* process sub-message */
  383.         switch( wPrm )
  384.             {
  385.         case SC_GRAB_BALL :            
  386.             
  387.             /* inform all clients */
  388.             if ( Maze.bGrabBall ) {
  389.                 Maze.bGrabBall = FALSE;
  390.                 for (i=0; i<Maze.wLinks; i++)
  391.                     DDE_UNADVISE( Link[i].hWnd, aGrab );
  392.             } else {
  393.                 Maze.bGrabBall = TRUE;
  394.                 for (i=0; i<Maze.wLinks; i++)
  395.                     DDE_ADVISE( Link[i].hWnd, aGrab );
  396.             }
  397.             
  398.             /* update system menu */
  399.             CheckMenuItem( 
  400.                 GetSystemMenu(hWnd,FALSE), 
  401.                 SC_GRAB_BALL,
  402.                 (Maze.bGrabBall)?MF_CHECKED:MF_UNCHECKED
  403.             );
  404.             
  405.             break;
  406.         case SC_GRAB_FOCUS :
  407.             
  408.             /* adjust state & system menu */
  409.             Maze.bGrabFocus = (Maze.bGrabFocus) ? FALSE : TRUE;
  410.             CheckMenuItem( 
  411.                 GetSystemMenu(hWnd,FALSE), 
  412.                 SC_GRAB_FOCUS,
  413.                 (Maze.bGrabFocus)?MF_CHECKED:MF_UNCHECKED
  414.             );
  415.             
  416.             break;
  417.         default :
  418.             lAck = DefWindowProc( hWnd, wMsg, wPrm, lPrm );
  419.             break;
  420.         }
  421.         
  422.         break;
  423.     case WM_GETMINMAXINFO : /* get window size info */
  424.         
  425.         /* set minimum tracking height */
  426.         ((LPPOINT)lPrm)[3].y = 5 * HOLE_HEIGHT;
  427.         
  428.         break;
  429.     case WM_SIZE : /* window being sized */
  430.         
  431.         /* adjust animation statistics */
  432.         Ball.lTimeIn = GetCurrentTime();
  433.  
  434.         /* calculate hole adjusted window dimensions */
  435.         Maze.wWidth = LOWORD(lPrm) - HOLE_WIDTH;
  436.         Maze.wHeight = HIWORD(lPrm) - HOLE_HEIGHT;
  437.             
  438.         /* randomly position all holes */
  439.         for ( i=0; i<Maze.wLinks; i++ ) {
  440.             Link[i].rHole.left = rand() % Maze.wWidth;
  441.             Link[i].rHole.top = rand() % Maze.wHeight;
  442.             Link[i].rHole.right = Link[i].rHole.left + HOLE_WIDTH;
  443.             Link[i].rHole.bottom = Link[i].rHole.top + HOLE_HEIGHT;
  444.         }
  445.             
  446.         /* calculate ball adjusted window dimensions */
  447.         Maze.wWidth = LO - BALL_WIDTH;
  448.         Maze.wHeight = HI - BALL_HEIGHT;
  449.             
  450.         /* randomly position bouncing ball */
  451.         Ball.rPosn.left = rand() % Maze.wWidth;
  452.         Ball.rPosn.top = rand() % Maze.wHeight;
  453.         Ball.rPosn.right = Ball.rPosn.left + BALL_WIDTH;
  454.         Ball.rPosn.bottom = Ball.rPosn.top + BALL_HEIGHT;
  455.         
  456.         /* assign ball movement direction */
  457.         Ball.iHorzMotion = RANDOM_MOTION;
  458.         Ball.iVertMotion = RANDOM_MOTION;
  459.                         
  460.         break;
  461.     case WM_PAINT : /* window needs painting */
  462.         
  463.         {
  464.             PAINTSTRUCT    Region;    /* temporary paint structure */
  465.             BYTE        sInterior[8];  /* temporary text 
  466.                                                           string */
  467.         
  468.             BeginPaint( hWnd, &Region );
  469.             
  470.             /* draw all holes in window */
  471.             for ( i=0; i<Maze.wLinks; i++ ) {
  472.             
  473.                 /* select appropriate text & brush colors */
  474.                 if ( Link[i].bAdviseBall || Link[i].bAdviseStat ) {
  475.                     SetBkColor( Region.hdc, RGB_BLACK );
  476.                     SetTextColor( Region.hdc, RGB_WHITE );
  477.                     SelectObject( Region.hdc, GetStockObject(BLACK_BRUSH) );
  478.                 } else {
  479.                     SetBkColor( Region.hdc, RGB_WHITE );
  480.                     SetTextColor( Region.hdc, RGB_BLACK );
  481.                     SelectObject( Region.hdc, GetStockObject(WHITE_BRUSH) );
  482.                 }
  483.                 
  484.                 /* draw black hole  */
  485.                 Rectangle(
  486.                     Region.hdc, 
  487.                     Link[i].rHole.left, 
  488.                     Link[i].rHole.top, 
  489.                     Link[i].rHole.right, 
  490.                     Link[i].rHole.bottom 
  491.                 );
  492.                 
  493.                 if ( Link[i].wNum )
  494.                     sprintf( sInterior, "%u", Link[i].wNum );
  495.                 else
  496.                     strcpy( sInterior, "?" );
  497.                 
  498.                 DrawText(
  499.                     Region.hdc,
  500.                     sInterior,
  501.                     strlen(sInterior),
  502.                     &Link[i].rHole,
  503.                     DT_CENTER|DT_VCENTER|DT_NOCLIP|DT_SINGLELINE
  504.                 );
  505.             
  506.             }
  507.                 
  508.             EndPaint( hWnd, &Region );
  509.         
  510.         }
  511.         
  512.         break;
  513.     case WM_CLOSE : /* end it all */
  514.  
  515.         if ( Maze.wLinks ) {
  516.         
  517.             /* pass ball to first interested client */
  518.             if ( Ball.bIsBouncing ) {
  519.                 for (i=0; (i<Maze.wLinks)&&(!Link[i].bAdviseBall); i++);
  520.                 DDE_DATA(
  521.                     Link[ (i<Maze.wLinks) ? i : 0 ].hWnd, 
  522.                     aGrab, 
  523.                     FALSE,
  524.                     (LONG)Maze.wNum,
  525.                     (LONG)((GetFocus()==hWnd)?TRUE:FALSE)
  526.                 );
  527.             }
  528.     
  529.             /* notify all clients */
  530.             for (i=0; i<Maze.wLinks; i++) {
  531.                 if (Link[i].bAdviseBall) DDE_UNADVISE(Link[i].hWnd,aBall);
  532.                 if (Link[i].bAdviseStat) DDE_UNADVISE(Link[i].hWnd,aStat);
  533.                 DDE_TERMINATE( Link[i].hWnd );
  534.             }
  535.  
  536.             /* wait till someone pulls the plug */
  537.             Maze.wGoingAway = Maze.wLinks;
  538.  
  539.             Maze.wLinks = 0;
  540.             Ball.bIsBouncing = FALSE;
  541.             SetWindowText( hWnd, "Maze dying..." );
  542.             
  543.         } else
  544.             DestroyWindow( hWnd );
  545.         
  546.         break;
  547.     case WM_DESTROY : /* end it all! */
  548.         PostQuitMessage( 0 );
  549.         break;
  550.     case WM_DDE_INITIATE : /* initiate DDE conversation */
  551.         
  552.         /* process message if interesting request */
  553.         if ( (hWnd!=wPrm)&&(LO==aAnyMaze||LO==aThisMaze)&&(HI==aBall) ) {
  554.         
  555.             /* check if caller already a client */
  556.             if ( !FindLink(&i,wPrm) ) {
  557.  
  558.                 /* add caller to list of links */
  559.                 i = Maze.wLinks++;
  560.  
  561.                 Link[i].wNum = 0;
  562.                 Link[i].hWnd = (HWND)wPrm;
  563.                 Link[i].bAdviseBall = FALSE;
  564.                 Link[i].bAdviseStat = FALSE;
  565.  
  566.                 /* re-add global atoms */
  567.                 BumpGlobalAtom( HI );
  568.                 BumpGlobalAtom( aThisMaze );
  569.             
  570.                 SEND( wPrm, WM_DDE_ACK, MAKELONG(aThisMaze,HI) );
  571.  
  572.                 /* ask caller to become a server also */
  573.                 if ( (!Maze.bInitiate)&&(LO==aAnyMaze) ) {
  574.                     Maze.bInitiate = TRUE;
  575.                     DDE_INITIATE( wPrm, aAnyMaze, aBall );
  576.                     Maze.bInitiate = FALSE;
  577.                 }
  578.  
  579.                 /* rearrange window & update display */
  580.                 SEND(hWnd,WM_SIZE,MAKELONG(DISPLAY_WIDTH,DISPLAY_HEIGHT));                
  581.                 InvalidateRect( hWnd, NULL, TRUE );
  582.  
  583.             }
  584.  
  585.         }
  586.         
  587.         break;
  588.     case WM_DDE_ADVISE : /* advise client on DDE item */
  589.         
  590.         {
  591.             WORD    wAnswer;   /* answer to advise message */
  592.             LPDATA    lpData;       /* temporary advise structure */
  593.     
  594.             /* initialization */
  595.             wAnswer = REJECTED;
  596.             
  597.             /* search for link */
  598.             if ( FindLink(&i,wPrm)&&((HI==aGrab)||(HI==aStat)) ) {
  599.                 
  600.                 lpData = (LPDATA)GlobalLock( LO );
  601.                 if ( lpData ) {
  602.                             
  603.                     if ( lpData->cfFormat == CF_TEXT ) {
  604.                     
  605.                         wAnswer = ACCEPTED;
  606.                         Link[i].bAdviseBall |= (HI==aGrab);
  607.                         Link[i].bAdviseStat |= (HI==aStat);
  608.                     
  609.                         GlobalUnlock( (HANDLE)LO );
  610.                         GlobalFree( (HANDLE)LO );
  611.                         
  612.                         InvalidateRect( hWnd, NULL, TRUE );
  613.                         
  614.                     } else
  615.                         GlobalUnlock( LO );
  616.                             
  617.                 }
  618.                 
  619.             } 
  620.             
  621.             /* respond to message */
  622.             DDE_ACK( wPrm, wAnswer, HI );
  623.         
  624.         }
  625.         
  626.         break;
  627.     case WM_DDE_UNADVISE : /* stop advising client on DDE item */
  628.         
  629.         {
  630.             WORD    wAnswer;  /* answer to advise message */
  631.             LPDATA    lpData;      /* temporary advise structure */
  632.     
  633.             /* initialization */
  634.             wAnswer = REJECTED;
  635.             
  636.             /* search for link */
  637.             if ( FindLink(&i,wPrm)&&((HI==aGrab)||(HI==aStat)) ) {
  638.                 
  639.                 wAnswer = ACCEPTED;
  640.                 if (HI==aGrab) Link[i].bAdviseBall=FALSE;
  641.                 if (HI==aStat) Link[i].bAdviseStat=FALSE;
  642.                     
  643.                 InvalidateRect( hWnd, NULL, TRUE );
  644.                         
  645.             } 
  646.             
  647.             /* respond to message */
  648.             DDE_ACK( wPrm, wAnswer, HI );
  649.         
  650.         }
  651.         
  652.         break;
  653.     case WM_DDE_REQUEST : /* client requestion data item */
  654.         
  655.         {
  656.             WORD    wAnswer;   /* answer to advise message */
  657.             LPDATA    lpData;       /* temporary advise structure */
  658.     
  659.             if ( FindLink(&i,wPrm)&&(HI==aStat)&&(LO==CF_TEXT) ) 
  660.                 DDE_DATA(
  661.                     Link[i].hWnd, 
  662.                     aStat,
  663.                     TRUE, 
  664.                     (Ball.bIsBouncing)?GetCurrentTime()-Ball.lTimeIn:0L,
  665.                     DISPLAY_WIDTH*(LONG)DISPLAY_HEIGHT
  666.                 );
  667.             else
  668.                 DDE_ACK( wPrm, REJECTED, HI );
  669.         
  670.         }
  671.             
  672.         break;
  673.     case WM_DDE_POKE : /* client providing unsolicited data item */
  674.     case WM_DDE_DATA : /* server providing data item */
  675.         
  676.         {
  677.             WORD    wData1;        /* window number */
  678.             WORD    wData2;        /* focus state */
  679.             LPDATA    lpData;        /* temporary advise structure */
  680.             BOOL    bRespond;    /* boolean respond flag */
  681.             WORD    wResponse;    /* response to message */
  682.             char    sString[30];    /* temporary data string */
  683.     
  684.             /* initialization */
  685.             bRespond = TRUE;
  686.             wResponse = REJECTED;
  687.  
  688.             /* search for link & check data item */
  689.             if ( FindLink(&i,wPrm)&&((HI==aGrab)||(HI==aWnd)) ) {
  690.                         
  691.                 /* retrieve data - lock may not succeed */
  692.                 lpData = (LPDATA)GlobalLock( LO );
  693.                 if ( lpData ) {
  694.                                     
  695.                     /* check format */
  696.                     if ( lpData->cfFormat == CF_TEXT ) {
  697.                         
  698.                         /* extract data */
  699.                         lstrcpy( (LPSTR)sString, lpData->info );                
  700.                         sscanf( sString, "%u\t%u", &wData1, &wData2 );
  701.                         
  702.                         /* process data */
  703.                         if ( HI == aGrab ) {
  704.  
  705.                             /* grab ball */
  706.                             Ball.bIsBouncing = TRUE;
  707.                             Ball.lTimeIn = GetCurrentTime();
  708.                             if (Maze.bGrabFocus && wData2) SetFocus(hWnd);
  709.                             
  710.                             /* randomize display */
  711.                             SEND(
  712.                                 hWnd,
  713.                                 WM_SIZE,
  714.                                 MAKELONG(DISPLAY_WIDTH,DISPLAY_HEIGHT)
  715.                             );                
  716.  
  717.                         } else 
  718.                             Link[i].wNum = wData1;                
  719.             
  720.                         /* Determine if acknowledgement required */
  721.                         if ( !lpData->fAck ) {
  722.                             bRespond = FALSE;
  723.                             GlobalDeleteAtom( HI );
  724.                         } else
  725.                             wResponse = ACCEPTED;
  726.                                     
  727.                         /* unlock memory & free if required */
  728.                         if ( lpData->fRelease ) {
  729.                             GlobalUnlock( LO );
  730.                             GlobalFree( LO );
  731.                         } else
  732.                             GlobalUnlock( LO );
  733.                         
  734.                         /* force system to repaint display */
  735.                         InvalidateRect( hWnd, NULL, TRUE );        
  736.                         
  737.                     } else
  738.                         GlobalUnlock( LO );
  739.                                             
  740.                 } 
  741.                                                 
  742.             } 
  743.                     
  744.             /* respond to caller */
  745.             if (bRespond) DDE_ACK(wPrm,wResponse,HI);
  746.         
  747.         }
  748.  
  749.         break;
  750.     case WM_DDE_ACK : /* DDE acknowledgement */
  751.  
  752.         if ( Maze.bInitiate ) {
  753.             
  754.             /* delete atoms - since bumped */
  755.             GlobalDeleteAtom( LO );
  756.             GlobalDeleteAtom( HI );
  757.             
  758.             /* inform server of window number */
  759.             DDE_POKE(
  760.                 wPrm, 
  761.                 aWnd,
  762.                 FALSE, 
  763.                 (LONG)Maze.wNum,
  764.                 (LONG)((GetFocus()==hWnd)?TRUE:FALSE)
  765.             );
  766.  
  767.             /* inform server of current advise status */
  768.             if ( Maze.bGrabBall ) 
  769.                 DDE_ADVISE( wPrm, aGrab );
  770.             
  771.         }
  772.  
  773.         break;
  774.     case WM_DDE_TERMINATE : /* end a DDE conversation */
  775.  
  776.         if ( FindLink(&i,wPrm) ) {
  777.         
  778.             /* respond with an unadvise on all items */
  779.             if (Link[i].bAdviseBall) DDE_UNADVISE(wPrm,aBall);
  780.             if (Link[i].bAdviseStat) DDE_UNADVISE(wPrm,aStat);
  781.             
  782.             /* remove caller from list */
  783.             memcpy(&Link[i],&Link[i+1],(Maze.wLinks-i-1)*sizeof(LINK));
  784.             Maze.wLinks--;
  785.             
  786.             /* respond with a matching terminate & update display */
  787.             DDE_TERMINATE( wPrm );
  788.             InvalidateRect( hWnd, NULL, TRUE );
  789.         
  790.         } else
  791.             if ( (Maze.wGoingAway)&&(Maze.wGoingAway-- == 1) )
  792.                 DestroyWindow( hWnd );
  793.         
  794.         break;
  795.     default : /* pass on to default */
  796.         lAck = DefWindowProc( hWnd, wMsg, wPrm, lPrm );
  797.         break;
  798.     }
  799.     
  800.     /* return result */
  801.     return( lAck );
  802.  
  803. }
  804.  
  805.  
  806. /*
  807.  * CREATE DDE MAZE
  808.  *
  809.  *    hInst        current instance handle
  810.  *    hPrevInst    previous instance handle
  811.  *    wCmdShow    initial show window command
  812.  *
  813.  * This function creates and initializes the Maze, including the
  814.  * definition of all global atoms.  A handle to the maze window is
  815.  * returned if the entire process is successful.
  816.  *
  817.  */
  818.  
  819. static HWND CreateMaze( hInst, hPrevInst, wCmdShow )
  820.     HANDLE    hInst;
  821.     HANDLE    hPrevInst;
  822.     WORD    wCmdShow;
  823. {
  824.     WORD        i;        /* temporary loop variable */
  825.     HWND        hWnd;        /* new maze window handle */
  826.     HMENU        hMenu;        /* system menu handle */
  827.     WORD        wQueue;        /* queue length counter */
  828.     BOOL        bSearch;    /* boolean search flag */
  829.     BOOL        bPresent;    /* number present flag */
  830.     WNDCLASS    WndClass;    /* window class structure */
  831.     BYTE        sCaption[64];    /* current window caption */
  832.     
  833.     /* perform instance specific initialization */
  834.     if ( !hPrevInst ) {
  835.     
  836.         Maze.wNum = 1;
  837.  
  838.         Ball.bIsBouncing = TRUE;
  839.         Ball.lTimeIn = GetCurrentTime();
  840.  
  841.         memset( &WndClass, 0, sizeof(WNDCLASS) );
  842.         
  843.         WndClass.lpszClassName = (LPSTR)"MazeWindow";
  844.         WndClass.hCursor = LoadCursor(NULL,IDC_ARROW);
  845.         WndClass.lpszMenuName = (LPSTR)NULL;
  846.         WndClass.style = CS_HREDRAW | CS_VREDRAW;
  847.         WndClass.lpfnWndProc =     MazeWndFn;
  848.         WndClass.hInstance = hInst;
  849.         WndClass.hIcon = NULL;
  850.         WndClass.hbrBackground = (HBRUSH)(COLOR_MENU + 1);
  851.  
  852.     } else {
  853.  
  854.         GetInstanceData( hPrevInst, (NPSTR)&Maze, sizeof(MAZE) );
  855.         GetInstanceData( hPrevInst, (NPSTR)Link, MAX_LINK*sizeof(LINK) );
  856.                 
  857.         Link[Maze.wLinks++].wNum = Maze.wNum;
  858.  
  859.         bSearch = TRUE;
  860.         Maze.wNum = 1;
  861.  
  862.         while ( bSearch ) {
  863.         
  864.             bPresent = FALSE;
  865.             for (i=0; i<Maze.wLinks; i++)
  866.                 if ( Link[i].wNum == Maze.wNum )
  867.                     bPresent = TRUE;
  868.                     
  869.             if ( bPresent )
  870.                 Maze.wNum++;
  871.             else
  872.                 bSearch = FALSE;
  873.         
  874.         }        
  875.         
  876.         Maze.wLinks = 0;
  877.         Ball.bIsBouncing = FALSE;
  878.     
  879.     }
  880.  
  881.     /* continue Maze initialization */
  882.     if ( hPrevInst || RegisterClass(&WndClass) ) {
  883.     
  884.         /* adjust maze queue length */
  885.         wQueue = 42;
  886.         while ( !SetMessageQueue(wQueue--) );
  887.         
  888.         /* define caption & initial position */
  889.         sprintf( sCaption, "DDE Maze - #%u", Maze.wNum );
  890.             
  891.         Maze.wGoingAway = 0;
  892.         Maze.bGrabBall = FALSE;
  893.         Maze.bGrabFocus = FALSE;
  894.         Maze.wWidth = GetSystemMetrics(SM_CXSCREEN) / MAZE_COLS;    
  895.         Maze.wHeight = GetSystemMetrics(SM_CYSCREEN) / MAZE_ROWS;
  896.                 
  897.         hWnd = CreateWindow(
  898.                 "MazeWindow",        /* class name */
  899.                 sCaption,        /* caption */
  900.                 WS_OVERLAPPEDWINDOW,    /* style */
  901.                 0,            /* x position */
  902.                 0,            /* y position */
  903.                 0,            /* width */
  904.                 0,            /* height */
  905.                 (HWND)NULL,        /* parent window */
  906.                 (HMENU)NULL,        /* menu */
  907.                 hInst,            /* application */
  908.                 (LPSTR)NULL        /* other data */
  909.             );
  910.  
  911.         if ( hWnd ) {
  912.         
  913.             /* revise system menu */        
  914.             hMenu = GetSystemMenu( hWnd, FALSE );
  915.             ChangeMenu(hMenu,0,NULL,0,MF_APPEND|MF_SEPARATOR );
  916.             ChangeMenu(hMenu,0,"Grab the ball",SC_GRAB_BALL,MF_APPEND);
  917.             ChangeMenu(hMenu,0,"Grab the focus",SC_GRAB_FOCUS,MF_APPEND);
  918.         
  919.             /* define global atoms */
  920.             sprintf( sCaption, "Maze%u", Maze.wNum );
  921.  
  922.             aAnyMaze = GlobalAddAtom( "Maze" );
  923.             aThisMaze = GlobalAddAtom( sCaption );
  924.             aBall = GlobalAddAtom( "Ball" );
  925.             aWnd = GlobalAddAtom( "Window" );
  926.             aGrab = GlobalAddAtom( "Grab" );
  927.             aStat = GlobalAddAtom( "Statistics" );
  928.                 
  929.             /* initiate DDE conversation on ball */
  930.             Maze.bInitiate = TRUE;
  931.             DDE_INITIATE( -1, aAnyMaze, aBall );
  932.             Maze.bInitiate = FALSE;
  933.  
  934.             /* seed random number & display maze */
  935.             srand( hInst );
  936.             ShowWindow( hWnd, wCmdShow );
  937.         
  938.         } else
  939.             exit( 2 );
  940.     
  941.     } else
  942.         exit( 1 );
  943.         
  944.     /* return final result */
  945.     return( hWnd );
  946.     
  947. }
  948.  
  949.  
  950. /*
  951.  * BOUNCE BALL IN DDE MAZE
  952.  *
  953.  *    hWnd    maze window handle
  954.  *
  955.  * This function animates the DDE Maze by moving the ball around the
  956.  * window.  If the ball "falls" into a "hole" a DDE message is
  957.  * posted to the appropriate client and the ball transferred.  In
  958.  * addition, various summary statistics are also provided for those
  959.  * interested listeners.  
  960.  *
  961.  */
  962.  
  963. static VOID BounceBall( hWnd )
  964.     HWND    hWnd;
  965. {
  966.     WORD    i;        /* client channel number */
  967.     HDC    hDC;        /* temporary display context */
  968.     LONG    lElapsed;    /* elapsed time ball was bouncing */
  969.  
  970.     /* check if ball inside hole */
  971.     for ( i=0; (i<Maze.wLinks)&&(OUTSIDE_HOLE); i++ );
  972.  
  973.     /* animate ball if not in hole */
  974.     if ( i == Maze.wLinks ) {
  975.  
  976.         hDC = GetDC( hWnd );
  977.         
  978.         /* Note - you could erase the bouncing ball here using an
  979.          * InvertRect call with the current ball position.  Leaving it
  980.          * out results in an interesting visual effect!
  981.          */
  982.         
  983.         Ball.iHorzMotion = H_BOUNCE( Ball.rPosn.left, Maze.wWidth );
  984.         Ball.iVertMotion = V_BOUNCE( Ball.rPosn.top, Maze.wHeight );
  985.                     
  986.         /* compute new ball position & draw */
  987.         Ball.rPosn.top += Ball.iVertMotion;
  988.         Ball.rPosn.left += Ball.iHorzMotion;
  989.         Ball.rPosn.right += Ball.iHorzMotion;
  990.         Ball.rPosn.bottom += Ball.iVertMotion;    
  991.         
  992.         InvertRect( hDC, &Ball.rPosn );        
  993.         ReleaseDC( hWnd, hDC );
  994.  
  995.     } else {
  996.         
  997.         /* pass ball to client */
  998.         Ball.bIsBouncing = FALSE;
  999.         DDE_DATA(
  1000.             Link[i].hWnd, 
  1001.             aGrab,
  1002.             FALSE, 
  1003.             (LONG)Maze.wNum,
  1004.             (LONG)((GetFocus()==hWnd)?TRUE:FALSE)
  1005.         );
  1006.  
  1007.         /* calculate animation statistics */
  1008.         lElapsed = GetCurrentTime() - Ball.lTimeIn;
  1009.             
  1010.         /* inform all statistics clients */
  1011.         for ( i=0; i<Maze.wLinks; i++ )
  1012.             if ( Link[i].bAdviseStat ) 
  1013.                 DDE_DATA(
  1014.                     Link[i].hWnd, 
  1015.                     aStat,
  1016.                     FALSE,
  1017.                     lElapsed,
  1018.                     DISPLAY_WIDTH*(LONG)DISPLAY_HEIGHT
  1019.                 );
  1020.     
  1021.         InvalidateRect( hWnd, NULL, TRUE );    
  1022.  
  1023.     }
  1024.  
  1025. }
  1026.  
  1027.  
  1028. /*
  1029.  * ADVISE SERVER TO SEND DATA
  1030.  *
  1031.  *    hToWnd        client window handle
  1032.  *    hFromWnd    server window handle
  1033.  *    aItem        atom representing item
  1034.  *
  1035.  * This function enables the calling routine to advise a client window
  1036.  * regarding a particular item of data.  This function assumes that
  1037.  * the data is to be sent in CF_TEXT format without requiring any
  1038.  * acknowledgement.  A value of TRUE is returned if the function is
  1039.  * successful.
  1040.  *
  1041.  */
  1042.  
  1043. static BOOL Advise( hToWnd, hFromWnd, aItem )
  1044.     HWND    hToWnd;
  1045.     HWND    hFromWnd;
  1046.     ATOM    aItem;
  1047. {
  1048.     /* local variables */
  1049.     HANDLE    hMem;        /* temporary memory handle */
  1050.     LPDATA    lpData;        /* pointer to data structure */
  1051.     BOOL    bResult;    /* result of function */
  1052.     
  1053.     bResult = FALSE;
  1054.     
  1055.     /* allocate memory & check if succeeded */
  1056.     hMem = GlobalAlloc( GHND|GMEM_DDESHARE, (DWORD)sizeof(DATA) );
  1057.     if ( hMem ) {
  1058.  
  1059.         /* lock the data - may not suceed with expanded memory */
  1060.         lpData = (LPDATA)GlobalLock( hMem );
  1061.         if ( lpData ) {
  1062.  
  1063.             /* define data structure constants */
  1064.             lpData->fAck = FALSE;
  1065.             lpData->fNoData = FALSE;
  1066.             lpData->cfFormat = CF_TEXT;
  1067.             
  1068.             /* unlock prior to sending */
  1069.             GlobalUnlock( hMem );
  1070.             
  1071.             /* notify server to send data */
  1072.             bResult = PostMessage(
  1073.                 hToWnd,
  1074.                 WM_DDE_ADVISE,
  1075.                 hFromWnd,
  1076.                 MAKELONG(hMem,aItem)
  1077.             );
  1078.  
  1079.         } else
  1080.             GlobalFree( hMem );
  1081.  
  1082.     }
  1083.     
  1084.     /* return result */
  1085.     return( bResult );
  1086.  
  1087.  
  1088.  
  1089. /*
  1090.  * TRANSMIT DDE DATA TO CLIENT
  1091.  *
  1092.  *    hToWnd        destination window handle
  1093.  *    hFromWnd    server window handle
  1094.  *    wMsg        message number to use
  1095.  *    aItem        atom representing data item
  1096.  *    bResp        data in response to a request
  1097.  *    l1        first portion data item to send
  1098.  *    l2        second portion data item to send
  1099.  *
  1100.  * This function enables the calling routine to transmit data to a
  1101.  * client window using either a DDE_DATA or DDE_POKE messgae.  It is
  1102.  * assumed that the information is sent in CF_TEXT format and does not
  1103.  * require the client to respond.  A value of TRUE is returned if the
  1104.  * entire process is successful.
  1105.  *
  1106.  */
  1107.  
  1108. static BOOL Transmit( hToWnd, hFromWnd, wMsg, aItem, bResp, l1, l2 )
  1109.     HWND    hToWnd;
  1110.     HWND    hFromWnd;
  1111.     WORD    wMsg;
  1112.     ATOM    aItem;
  1113.     BOOL    bResp;
  1114.     LONG    l1;
  1115.     LONG    l2;
  1116. {
  1117.     /* local variables */
  1118.     HANDLE    hMem;        /* temporary memory handle */
  1119.     LPDATA    lpData;        /* pointer to data structure */
  1120.     BOOL    bResult;    /* boolean result value */
  1121.     char    sString[32];    /* local string variable */
  1122.     
  1123.     bResult = FALSE;
  1124.  
  1125.     /* allocate memory & check if succeeded */
  1126.     hMem = GlobalAlloc( GHND|GMEM_DDESHARE, (DWORD)sizeof(DATA) );
  1127.     if ( hMem ) {
  1128.  
  1129.         /* lock the data - may not suceed with expanded memory */
  1130.         lpData = (LPDATA)GlobalLock( hMem );
  1131.         if ( lpData ) {
  1132.  
  1133.             /* define data structure constants */
  1134.             lpData->fAck = FALSE;
  1135.             lpData->fRelease = TRUE;
  1136.             lpData->fResponse = bResp;
  1137.             lpData->cfFormat = CF_TEXT;
  1138.             
  1139.             BumpGlobalAtom( aItem );
  1140.  
  1141.             /* format the data */
  1142.             sprintf( sString, "%ld\t%ld", l1, l2 );
  1143.             lstrcpy( lpData->info, (LPSTR)sString );
  1144.             
  1145.             /* unlock prior to sending */
  1146.             GlobalUnlock( hMem );
  1147.             
  1148.             bResult = PostMessage(
  1149.                     hToWnd,
  1150.                     wMsg,
  1151.                     hFromWnd,
  1152.                     MAKELONG(hMem,aItem)
  1153.                 );
  1154.             
  1155.         } else
  1156.             GlobalFree( hMem );
  1157.  
  1158.     }
  1159.     
  1160.     /* return result */
  1161.     return( bResult );
  1162.  
  1163.  
  1164.  
  1165.  
  1166. /*
  1167.  * DESTROY DDE MAZE
  1168.  *
  1169.  *    wQuit    application exit code
  1170.  *
  1171.  * This function destroys all resources consumed by the Maze during
  1172.  * execution.  Included is the removal of all global atoms.  A final
  1173.  * exit code is returned by the function.
  1174.  *
  1175.  */
  1176.  
  1177. static WORD DestroyMaze( wQuit )
  1178.     WORD    wQuit;
  1179. {
  1180.  
  1181.     /* remove global atoms */
  1182.     GlobalDeleteAtom( aAnyMaze );
  1183.     GlobalDeleteAtom( aThisMaze );
  1184.     GlobalDeleteAtom( aBall );
  1185.     GlobalDeleteAtom( aWnd );
  1186.     GlobalDeleteAtom( aGrab );
  1187.     GlobalDeleteAtom( aStat );
  1188.  
  1189.     /* return final exit code */
  1190.     return( wQuit );
  1191.  
  1192. }
  1193.  
  1194.  
  1195.  
  1196. /*
  1197.  * FIND DDE LINK
  1198.  *
  1199.  *    pwNdx    index to link
  1200.  *    hWnd    window handle of caller
  1201.  *
  1202.  * This function finds the link references by the window handle
  1203.  * provided.  The resulting link is returned to the caller.  The
  1204.  * function returns TRUE if the window handle was found.
  1205.  *
  1206.  */
  1207.  
  1208. static BOOL FindLink( pwNdx, hWnd )
  1209.     WORD *    pwNdx;
  1210.     HWND    hWnd;
  1211. {
  1212.     WORD    i;
  1213.     
  1214.     for (i=0; i<Maze.wLinks; i++)
  1215.         if ( Link[i].hWnd == hWnd ) {
  1216.             *pwNdx = i;
  1217.             return( TRUE );
  1218.         }
  1219.             
  1220.     return( FALSE );
  1221.         
  1222. }
  1223.  
  1224.  
  1225. /*
  1226.  * BUMP GLOBAL ATOM
  1227.  *
  1228.  *    aAtom    atom to bump
  1229.  *
  1230.  * This function increments the reference count to the specified
  1231.  * global atom.  It is assumed that the atom supplied is defined and
  1232.  * valid.  The value supplied is returned.
  1233.  *
  1234.  */
  1235.  
  1236. static ATOM BumpGlobalAtom( aAtom )
  1237.     ATOM        aAtom;
  1238. {
  1239.     char        sName[64];                
  1240.     
  1241.     GlobalGetAtomName( aAtom, sName, sizeof(sName) );
  1242.     return( GlobalAddAtom(sName) );
  1243. }    
  1244.