home *** CD-ROM | disk | FTP | other *** search
- /*+==========================================================================
- File: GUIBALL.CPP
-
- Summary: Implementation file for the CGuiBall C++ class. A GuiBall is
- a C++ object that uses three independent worker threads to
- display a moving and bouncing ball in the client area of a
- designated window. It is anchored to the Windows GUI
- (Graphical User Interface) environment. This GuiBall object
- continuously paints a ball image based on data it obtains
- from a virtual ball object. This virtual ball object is
- instantiated as a COM object (a COBall) in a separate
- thread-safe In-process server.
-
- GuiBall launches three threads which all continuously and
- asynchronously command the ball to move. GuiBall itself
- provides methods to initialize the GuiBall, paint the ball
- image, and restart the motion. The cool thing about this
- arrangement between client and server is that the ball
- changes color as it moves. The ball color indicates the
- thread that last moved the ball. This gives a visual
- impact to multi-threading.
-
- For a comprehensive tutorial code tour of GUIBALL's contents
- and offerings see the accompanying FRECLIEN.TXT file. For more
- specific technical details on the internal workings see the
- comments dispersed throughout the GUIBALL source code.
-
- Classes: CThreadInitData, CGuiBall
-
- Origin: 4-5-96: atrent - Created for OLE Tutorial Code Samples. Also
- benefits from the GDIDEMO sample in the Win32 samples of the
- Win32 SDK.
-
- ----------------------------------------------------------------------------
- This file is part of the Microsoft OLE Tutorial Code Samples.
-
- Copyright (C) Microsoft Corporation, 1996. All rights reserved.
-
- This source code is intended only as a supplement to Microsoft
- Development Tools and/or on-line documentation. See these other
- materials for detailed information regarding Microsoft code samples.
-
- THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
- KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
- PARTICULAR PURPOSE.
- ==========================================================================+*/
-
- /*--------------------------------------------------------------------------
- We include WINDOWS.H for all Win32 applications.
- We include OLE2.H because we will be making calls to the OLE Libraries.
- We include APPUTIL.H because we will be building this application using
- the convenient Virtual Window and Dialog classes and other
- utility functions in the APPUTIL Library (ie, APPUTIL.LIB).
- We include IBALL.H and BALLGUID.H for the common Ball-related Interface
- class, GUID, and CLSID specifications.
- We include GUIBALL.H because it has the C++ class used for GUI display
- of the moving ball.
- ---------------------------------------------------------------------------*/
- #include "preclien.h"
-
- /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
- Method: CGuiBall::CGuiBall
-
- Summary: Constructor.
-
- Args: void
-
- Modifies: ...
-
- Returns: void
- M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
- CGuiBall::CGuiBall(void)
- {
- m_hWnd = 0;
- m_crColor = RGB(0,0,0);
- m_dwBallThread1 = 0;
- m_dwBallThread2 = 0;
- m_dwBallThread3 = 0;
- m_hBallThreads[0] = 0;
- m_hBallThreads[1] = 0;
- m_hBallThreads[2] = 0;
- }
-
-
- /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
- Method: CGuiBall::~CGuiBall
-
- Summary: Destructor.
-
- Args: void
-
- Modifies: ...
-
- Returns: void
- M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
- CGuiBall::~CGuiBall(void)
- {
- BOOL bOk = TRUE;
-
- if ((bool) m_pIBall)
- {
- // Kill the client's app timer for its repaints.
- KillTimer(m_hWnd, 1);
-
- // Call down to the server's COBall and tell it to shutdown.
- m_pIBall->Move(FALSE);
-
- // Wait for the threads to terminate before closing their thread handles.
- WaitForMultipleObjects(3, m_hBallThreads, TRUE, INFINITE);
- for (size_t i = 0; i<3; i++)
- CloseHandle(m_hBallThreads[i]);
-
- }
- }
-
-
- /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
- Function: BallThreadProc
-
- Summary: The common thread procedure for all Ball Threads.
-
- Args: LPARAM lparam
- Standard Window Proc parameter.
-
- Modifies: .
-
- Returns: DWORD
- Thread procedure return (usually msg.wParam).
- F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
- DWORD WINAPI BallThreadProc(
- LPARAM lparam)
- {
- CThreadInitData* pInitData = (CThreadInitData*) lparam;
- HRESULT hr;
- DWORD nEndCount = 0;
- BOOL bAlive = TRUE;
- DWORD nDelay;
-
- // Keep a copy here on the local stack of the ball move delay.
- nDelay = pInitData->m_nDelay;
-
- // Initialize COM for use by this thread. Tell COM we are
- // multi-threaded.
- hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
-
- // Continuously move the ball while it is still alive.
- while (bAlive)
- {
- // Use system timer to slow down the ball motion to the range
- // of the humanly perceptible.
- if (GetTickCount() > nEndCount)
- {
- // After the delay, call from this thread thru IBall interface to
- // move the single ball that lives in the COBall COM object.
- bAlive = pInitData->m_pIBall->Move(TRUE);
-
- // Set new timer end count.
- nEndCount = GetTickCount() + nDelay;
- }
- }
-
- // UnInitialize COM for use by this thread.
- CoUninitialize();
-
- return 0;
- }
-
-
- /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
- Method: CGuiBall::Init
-
- Summary: Get everything related to CGuiBall started. Make any
- subordinate objects, like COBall, and get it started.
- Starts the worker threads that breathe life into the
- COBall COM object.
-
- Args: HWND hWnd
- Handle of the main window. Part of what makes CGuiBall
- a GUI kind of thing.
-
- Modifies: ...
-
- Returns: BOOL
- TRUE for success; FALSE for fail.
- M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
- BOOL CGuiBall::Init(
- HWND hWnd)
- {
- BOOL bOk = FALSE;
-
- if (hWnd)
- {
- m_hWnd = hWnd;
-
- // Call OLE service to create the single COBall instance.
- // We are not aggregating it so we ask for its IBall interface
- // directly.
-
- try {
- HRESULT hr = m_pIBall.CreateInstance(__uuidof(Ball), NULL, CLSCTX_INPROC_SERVER);
- if (FAILED(hr))
- _com_issue_error(hr);
-
-
- // Set up the client process to periodically paint the ball
- // thru WM_TIMER messages to the main Window proc.
- SetTimer(hWnd, 1, BALL_PAINT_DELAY, NULL);
-
- // Now start up 3 client BallThreads that will all try to move the
- // Ball concurrently. They will bring independent asynchronous life
- // to the ball. The main client process only displays the ball.
-
- // Create Structures for thread initialization.
- m_BallThreadData1.m_hWnd = hWnd;
- m_BallThreadData1.m_pIBall = m_pIBall;
- m_BallThreadData1.m_nDelay = BALL_MOVE_DELAY;
- m_BallThreadData2.m_hWnd = hWnd;
- m_BallThreadData2.m_pIBall = m_pIBall;
- m_BallThreadData2.m_nDelay = BALL_MOVE_DELAY;
- m_BallThreadData3.m_hWnd = hWnd;
- m_BallThreadData3.m_pIBall = m_pIBall;
- m_BallThreadData3.m_nDelay = BALL_MOVE_DELAY;
-
- // Create the Ball Moving Thread1.
- m_hBallThreads[0] = CreateThread(
- 0,
- 0,
- (LPTHREAD_START_ROUTINE) BallThreadProc,
- (LPVOID) &m_BallThreadData1,
- 0,
- &m_dwBallThread1);
-
- bOk = (NULL != m_hBallThreads[0]);
- if (!bOk)
- {
- hr = GetLastError();
- }
- else
- {
- // Create the Ball Moving Thread2.
- m_hBallThreads[1] = CreateThread(
- 0,
- 0,
- (LPTHREAD_START_ROUTINE) BallThreadProc,
- (LPVOID) &m_BallThreadData2,
- 0,
- &m_dwBallThread2);
-
- bOk = (NULL != m_hBallThreads[1]);
- if (!bOk)
- hr = GetLastError();
- else
- {
- // Create the Ball Moving Thread3.
- m_hBallThreads[2] = CreateThread(
- 0,
- 0,
- (LPTHREAD_START_ROUTINE) BallThreadProc,
- (LPVOID) &m_BallThreadData3,
- 0,
- &m_dwBallThread3);
-
- bOk = (NULL != m_hBallThreads[2]);
- if (!bOk)
- hr = GetLastError();
- }
- }
- } catch(_com_error& e) {
- _bstr_t bstrSource(e.Source());
- _bstr_t bstrDescription(e.Description());
- TCHAR szTemp[256];
- TCHAR szMsg[1024];
- wsprintf(szTemp, _T("Code = %08lx\n"), e.Error());
- _ftcscpy(szMsg, szTemp);
- wsprintf(szTemp, _T("Code meaning = %s\n"), e.ErrorMessage());
- _ftcscat(szMsg, szTemp);
- wsprintf(szTemp, _T("Source = %s\n"), bstrSource.length() ? (LPCTSTR)bstrSource : _T("null"));
- _ftcscat(szMsg, szTemp);
- wsprintf(szTemp, _T("Description = %s\n"), bstrDescription.length() ? (LPCTSTR)bstrDescription : _T("null"));
- _ftcscat(szMsg, szTemp);
- MessageBox(m_hWnd, szMsg, "Oops - hit an error!", MB_APPLMODAL | MB_ICONHAND);
- }
- }
-
- return (bOk);
- }
-
-
- /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
- Method: CGuiBall::PaintBall
-
- Summary: Tell CGuiBall to paint one image of the GuiBall.
-
- Args: void
-
- Modifies: ...
-
- Returns: void
- M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
- void CGuiBall::PaintBall(void)
- {
- HDC hDC;
- HBRUSH hBrush;
- POINT Org, Ext;
- HRGN hNew;
-
- if ((bool) m_pIBall)
- {
- // Ask the COBall for its current ball (location, region, and color).
- m_crColor = m_pIBall->GetBall(&Org, &Ext);
-
- // Create the new ball image/region.
- hNew = CreateEllipticRgn(Org.x, Org.y, Ext.x, Ext.y);
-
- if(hDC = GetDC(m_hWnd))
- {
- // Make a paint brush, dip it in pixel paint, and paint the
- // ball image.
- hBrush = CreateSolidBrush(m_crColor);
- FillRgn(hDC, hNew, hBrush);
- DeleteObject(hBrush);
- ReleaseDC(m_hWnd, hDC);
- }
-
- // Delete the region object.
- DeleteObject(hNew);
- }
-
- return;
- }
-
-
- /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
- Method: CGuiBall::Restart
-
- Summary: Restart the display process. Places ball in start position
- in a clean window.
-
- Args: void.
-
- Modifies: ...
-
- Returns: void.
- M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
- void CGuiBall::Restart(void)
- {
- RECT WinRect;
- HDC hDC = GetDC(m_hWnd);
-
- if(hDC && (bool) m_pIBall)
- {
- GetClientRect(m_hWnd, &WinRect);
- FillRect(hDC, &WinRect, GETCLASSBRUSH(m_hWnd));
-
- // Tell the COBall to reset itself.
- m_pIBall->Reset(&WinRect, 0);
-
- // Call our own CGuiBall method to paint an initial image of the ball.
- PaintBall();
-
- // Release the Device Context.
- ReleaseDC(m_hWnd, hDC);
- }
-
- return;
- }
-
-
- /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
- Method: CGuiBall::PaintWin
-
- Summary: Clears window background and paints the GuiBall at its
- current location.
-
- Args: void
-
- Modifies: ...
-
- Returns: void
- M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
- void CGuiBall::PaintWin(void)
- {
- HDC hDC;
- PAINTSTRUCT ps;
- RECT WinRect;
-
- if(hDC = BeginPaint(m_hWnd, &ps))
- EndPaint(m_hWnd, &ps);
-
- if(hDC = GetDC(m_hWnd))
- {
- // Get our window's client area rectangle.
- GetClientRect(m_hWnd, &WinRect);
- // Fill that rectangle with pixels of white paint.
- FillRect(hDC, &WinRect, GETCLASSBRUSH(m_hWnd));
-
- // Paint a current image of the ball wherever it is.
- PaintBall();
-
- ReleaseDC(m_hWnd, hDC);
- }
-
- return;
- }
-