📄 guiball.cpp
字号:
/*+==========================================================================
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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -