home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / msj / msjv2_5 / maze / maze.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-02  |  24.4 KB  |  1,024 lines

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