home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / com / freethrd / freclien / guiball.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-03  |  12.6 KB  |  406 lines

  1. /*+==========================================================================
  2.   File:      GUIBALL.CPP
  3.  
  4.   Summary:   Implementation file for the CGuiBall C++ class. A GuiBall is
  5.              a C++ object that uses three independent worker threads to
  6.              display a moving and bouncing ball in the client area of a
  7.              designated window.  It is anchored to the Windows GUI
  8.              (Graphical User Interface) environment. This GuiBall object
  9.              continuously paints a ball image based on data it obtains
  10.              from a virtual ball object. This virtual ball object is
  11.              instantiated as a COM object (a COBall) in a separate
  12.              thread-safe In-process server.
  13.  
  14.              GuiBall launches three threads which all continuously and
  15.              asynchronously command the ball to move. GuiBall itself
  16.              provides methods to initialize the GuiBall, paint the ball
  17.              image, and restart the motion. The cool thing about this
  18.              arrangement between client and server is that the ball
  19.              changes color as it moves. The ball color indicates the
  20.              thread that last moved the ball. This gives a visual
  21.              impact to multi-threading.
  22.  
  23.              For a comprehensive tutorial code tour of GUIBALL's contents
  24.              and offerings see the accompanying FRECLIEN.TXT file. For more
  25.              specific technical details on the internal workings see the
  26.              comments dispersed throughout the GUIBALL source code.
  27.  
  28.   Classes:   CThreadInitData, CGuiBall
  29.  
  30.   Origin:    4-5-96: atrent - Created for OLE Tutorial Code Samples. Also
  31.              benefits from the GDIDEMO sample in the Win32 samples of the
  32.              Win32 SDK.
  33.  
  34. ----------------------------------------------------------------------------
  35.   This file is part of the Microsoft OLE Tutorial Code Samples.
  36.  
  37.   Copyright (C) Microsoft Corporation, 1996.  All rights reserved.
  38.  
  39.   This source code is intended only as a supplement to Microsoft
  40.   Development Tools and/or on-line documentation.  See these other
  41.   materials for detailed information regarding Microsoft code samples.
  42.  
  43.   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  44.   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  45.   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  46.   PARTICULAR PURPOSE.
  47. ==========================================================================+*/
  48.  
  49. /*--------------------------------------------------------------------------
  50.   We include WINDOWS.H for all Win32 applications.
  51.   We include OLE2.H because we will be making calls to the OLE Libraries.
  52.   We include APPUTIL.H because we will be building this application using
  53.     the convenient Virtual Window and Dialog classes and other
  54.     utility functions in the APPUTIL Library (ie, APPUTIL.LIB).
  55.   We include IBALL.H and BALLGUID.H for the common Ball-related Interface
  56.     class, GUID, and CLSID specifications.
  57.   We include GUIBALL.H because it has the C++ class used for GUI display
  58.     of the moving ball.
  59. ---------------------------------------------------------------------------*/
  60. #include "preclien.h"
  61.  
  62. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  63.   Method:   CGuiBall::CGuiBall
  64.  
  65.   Summary:  Constructor.
  66.  
  67.   Args:     void
  68.  
  69.   Modifies: ...
  70.  
  71.   Returns:  void
  72. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  73. CGuiBall::CGuiBall(void)
  74. {
  75.   m_hWnd    = 0;
  76.   m_crColor = RGB(0,0,0);
  77.   m_dwBallThread1 = 0;
  78.   m_dwBallThread2 = 0;
  79.   m_dwBallThread3 = 0;
  80.   m_hBallThreads[0] = 0;
  81.   m_hBallThreads[1] = 0;
  82.   m_hBallThreads[2] = 0;
  83. }
  84.  
  85.  
  86. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  87.   Method:   CGuiBall::~CGuiBall
  88.  
  89.   Summary:  Destructor.
  90.  
  91.   Args:     void
  92.  
  93.   Modifies: ...
  94.  
  95.   Returns:  void
  96. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  97. CGuiBall::~CGuiBall(void)
  98. {
  99.   BOOL bOk = TRUE;
  100.  
  101.   if ((bool) m_pIBall)
  102.   {
  103.     // Kill the client's app timer for its repaints.
  104.     KillTimer(m_hWnd, 1);
  105.  
  106.     // Call down to the server's COBall and tell it to shutdown.
  107.     m_pIBall->Move(FALSE);
  108.  
  109.     // Wait for the threads to terminate before closing their thread handles.
  110.     WaitForMultipleObjects(3, m_hBallThreads, TRUE, INFINITE);
  111.     for (size_t i = 0; i<3; i++)
  112.       CloseHandle(m_hBallThreads[i]);
  113.  
  114.   }
  115. }
  116.  
  117.  
  118. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  119.   Function: BallThreadProc
  120.  
  121.   Summary:  The common thread procedure for all Ball Threads.
  122.  
  123.   Args:     LPARAM lparam
  124.               Standard Window Proc parameter.
  125.  
  126.   Modifies: .
  127.  
  128.   Returns:  DWORD
  129.               Thread procedure return (usually msg.wParam).
  130. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  131. DWORD WINAPI BallThreadProc(
  132.                LPARAM lparam)
  133. {
  134.   CThreadInitData* pInitData = (CThreadInitData*) lparam;
  135.   HRESULT hr;
  136.   DWORD nEndCount = 0;
  137.   BOOL bAlive = TRUE;
  138.   DWORD nDelay;
  139.  
  140.   // Keep a copy here on the local stack of the ball move delay.
  141.   nDelay = pInitData->m_nDelay;
  142.  
  143.   // Initialize COM for use by this thread. Tell COM we are
  144.   // multi-threaded.
  145.   hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  146.  
  147.   // Continuously move the ball while it is still alive.
  148.   while (bAlive)
  149.   {
  150.     // Use system timer to slow down the ball motion to the range
  151.     // of the humanly perceptible.
  152.     if (GetTickCount() > nEndCount)
  153.     {
  154.       // After the delay, call from this thread thru IBall interface to
  155.       // move the single ball that lives in the COBall COM object.
  156.       bAlive = pInitData->m_pIBall->Move(TRUE);
  157.  
  158.       // Set new timer end count.
  159.       nEndCount = GetTickCount() + nDelay;
  160.     }
  161.   }
  162.  
  163.   // UnInitialize COM for use by this thread.
  164.   CoUninitialize();
  165.  
  166.   return 0;
  167. }
  168.  
  169.  
  170. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  171.   Method:   CGuiBall::Init
  172.  
  173.   Summary:  Get everything related to CGuiBall started. Make any
  174.             subordinate objects, like COBall, and get it started.
  175.             Starts the worker threads that breathe life into the
  176.             COBall COM object.
  177.  
  178.   Args:     HWND hWnd
  179.               Handle of the main window. Part of what makes CGuiBall
  180.               a GUI kind of thing.
  181.  
  182.   Modifies: ...
  183.  
  184.   Returns:  BOOL
  185.               TRUE for success; FALSE for fail.
  186. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  187. BOOL CGuiBall::Init(
  188.        HWND hWnd)
  189. {
  190.   BOOL bOk = FALSE;
  191.  
  192.   if (hWnd)
  193.   {
  194.     m_hWnd = hWnd;
  195.  
  196.     // Call OLE service to create the single COBall instance.
  197.     // We are not aggregating it so we ask for its IBall interface
  198.     // directly.
  199.  
  200.     try {
  201.     HRESULT hr = m_pIBall.CreateInstance(__uuidof(Ball), NULL, CLSCTX_INPROC_SERVER);
  202.     if (FAILED(hr)) 
  203.         _com_issue_error(hr);
  204.                                 
  205.  
  206.     // Set up the client process to periodically paint the ball
  207.     // thru WM_TIMER messages to the main Window proc.
  208.     SetTimer(hWnd, 1, BALL_PAINT_DELAY, NULL);
  209.  
  210.     // Now start up 3 client BallThreads that will all try to move the
  211.     // Ball concurrently. They will bring independent asynchronous life
  212.     // to the ball. The main client process only displays the ball.
  213.  
  214.     // Create Structures for thread initialization.
  215.     m_BallThreadData1.m_hWnd = hWnd;
  216.     m_BallThreadData1.m_pIBall = m_pIBall;
  217.     m_BallThreadData1.m_nDelay = BALL_MOVE_DELAY;
  218.     m_BallThreadData2.m_hWnd = hWnd;
  219.     m_BallThreadData2.m_pIBall = m_pIBall;
  220.     m_BallThreadData2.m_nDelay = BALL_MOVE_DELAY;
  221.     m_BallThreadData3.m_hWnd = hWnd;
  222.     m_BallThreadData3.m_pIBall = m_pIBall;
  223.     m_BallThreadData3.m_nDelay = BALL_MOVE_DELAY;
  224.  
  225.     // Create the Ball Moving Thread1.
  226.     m_hBallThreads[0] = CreateThread(
  227.                             0,
  228.                             0,
  229.                             (LPTHREAD_START_ROUTINE) BallThreadProc,
  230.                             (LPVOID) &m_BallThreadData1,
  231.                             0,
  232.                             &m_dwBallThread1);
  233.  
  234.     bOk = (NULL != m_hBallThreads[0]);
  235.     if (!bOk)
  236.     {
  237.         hr = GetLastError();
  238.     }
  239.     else
  240.     {
  241.         // Create the Ball Moving Thread2.
  242.         m_hBallThreads[1] = CreateThread(
  243.                               0,
  244.                               0,
  245.                               (LPTHREAD_START_ROUTINE) BallThreadProc,
  246.                               (LPVOID) &m_BallThreadData2,
  247.                               0,
  248.                               &m_dwBallThread2);
  249.  
  250.         bOk = (NULL != m_hBallThreads[1]);
  251.         if (!bOk)
  252.           hr = GetLastError();
  253.         else
  254.         {
  255.           // Create the Ball Moving Thread3.
  256.           m_hBallThreads[2] = CreateThread(
  257.                                 0,
  258.                                 0,
  259.                                 (LPTHREAD_START_ROUTINE) BallThreadProc,
  260.                                 (LPVOID) &m_BallThreadData3,
  261.                                 0,
  262.                                 &m_dwBallThread3);
  263.  
  264.           bOk = (NULL != m_hBallThreads[2]);
  265.           if (!bOk)
  266.               hr = GetLastError();
  267.         }
  268.       }
  269.     } catch(_com_error& e) {
  270.     _bstr_t bstrSource(e.Source());
  271.     _bstr_t bstrDescription(e.Description());
  272.     TCHAR szTemp[256];
  273.     TCHAR szMsg[1024]; 
  274.     wsprintf(szTemp, _T("Code = %08lx\n"), e.Error());
  275.     _ftcscpy(szMsg, szTemp);
  276.     wsprintf(szTemp, _T("Code meaning = %s\n"), e.ErrorMessage());
  277.     _ftcscat(szMsg, szTemp);    
  278.     wsprintf(szTemp, _T("Source = %s\n"), bstrSource.length() ? (LPCTSTR)bstrSource : _T("null"));
  279.     _ftcscat(szMsg, szTemp);
  280.     wsprintf(szTemp, _T("Description = %s\n"), bstrDescription.length() ? (LPCTSTR)bstrDescription : _T("null"));
  281.     _ftcscat(szMsg, szTemp);
  282.     MessageBox(m_hWnd, szMsg, "Oops - hit an error!", MB_APPLMODAL | MB_ICONHAND);
  283.     }
  284.   }
  285.  
  286.   return (bOk);
  287. }
  288.  
  289.  
  290. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  291.   Method:   CGuiBall::PaintBall
  292.  
  293.   Summary:  Tell CGuiBall to paint one image of the GuiBall.
  294.  
  295.   Args:     void
  296.  
  297.   Modifies: ...
  298.  
  299.   Returns:  void
  300. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  301. void CGuiBall::PaintBall(void)
  302. {
  303.   HDC       hDC;
  304.   HBRUSH    hBrush;
  305.   POINT     Org, Ext;
  306.   HRGN      hNew;
  307.  
  308.   if ((bool) m_pIBall)
  309.   {
  310.     // Ask the COBall for its current ball (location, region, and color).
  311.     m_crColor = m_pIBall->GetBall(&Org, &Ext);
  312.  
  313.     // Create the new ball image/region.
  314.     hNew = CreateEllipticRgn(Org.x, Org.y, Ext.x, Ext.y);
  315.  
  316.     if(hDC = GetDC(m_hWnd))
  317.     {
  318.       // Make a paint brush, dip it in pixel paint, and paint the
  319.       // ball image.
  320.       hBrush = CreateSolidBrush(m_crColor);
  321.       FillRgn(hDC, hNew, hBrush);
  322.       DeleteObject(hBrush);
  323.       ReleaseDC(m_hWnd, hDC);
  324.     }
  325.  
  326.     // Delete the region object.
  327.     DeleteObject(hNew);
  328.   }
  329.  
  330.   return;
  331. }
  332.  
  333.  
  334. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  335.   Method:   CGuiBall::Restart
  336.  
  337.   Summary:  Restart the display process. Places ball in start position
  338.             in a clean window.
  339.  
  340.   Args:     void.
  341.  
  342.   Modifies: ...
  343.  
  344.   Returns:  void.
  345. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  346. void CGuiBall::Restart(void)
  347. {
  348.   RECT  WinRect;
  349.   HDC   hDC = GetDC(m_hWnd);
  350.  
  351.   if(hDC && (bool) m_pIBall)
  352.   {
  353.     GetClientRect(m_hWnd, &WinRect);
  354.     FillRect(hDC, &WinRect, GETCLASSBRUSH(m_hWnd));
  355.  
  356.     // Tell the COBall to reset itself.
  357.     m_pIBall->Reset(&WinRect, 0);
  358.  
  359.     // Call our own CGuiBall method to paint an initial image of the ball.
  360.     PaintBall();
  361.  
  362.     // Release the Device Context.
  363.     ReleaseDC(m_hWnd, hDC);
  364.   }
  365.  
  366.   return;
  367. }
  368.  
  369.  
  370. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  371.   Method:   CGuiBall::PaintWin
  372.  
  373.   Summary:  Clears window background and paints the GuiBall at its
  374.             current location.
  375.  
  376.   Args:     void
  377.  
  378.   Modifies: ...
  379.  
  380.   Returns:  void
  381. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  382. void CGuiBall::PaintWin(void)
  383. {
  384.   HDC         hDC;
  385.   PAINTSTRUCT ps;
  386.   RECT        WinRect;
  387.  
  388.   if(hDC = BeginPaint(m_hWnd, &ps))
  389.     EndPaint(m_hWnd, &ps);
  390.  
  391.   if(hDC = GetDC(m_hWnd))
  392.   {
  393.     // Get our window's client area rectangle.
  394.     GetClientRect(m_hWnd, &WinRect);
  395.     // Fill that rectangle with pixels of white paint.
  396.     FillRect(hDC, &WinRect, GETCLASSBRUSH(m_hWnd));
  397.  
  398.     // Paint a current  image of the ball wherever it is.
  399.     PaintBall();
  400.  
  401.     ReleaseDC(m_hWnd, hDC);
  402.   }
  403.  
  404.   return;
  405. }
  406.