⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 multtimer2.cpp

📁 window下的多线程编程参考书。值得一读
💻 CPP
字号:
//
// FILE: MultTimer2.cpp
//
// Copyright (c) 1997 by Aaron Michael Cohen and Mike Woodring
//
/////////////////////////////////////////////////////////////////////////
#include "MultTimer2.h"

// define the single application instance...
CMultTimerApp theApp;

// static class variables for popping up the dialogs just to 
// the right and below the last one...
int CMultTimerDlg::s_LastDialogX = 0;
int CMultTimerDlg::s_LastDialogY = 0;

// static class variable for tracking child dialog threads...
CMclQueue<CMcl4MfcGUIThread *> CMultTimerDlg::m_cqChildThreads;

// CMultTimerApp application class member functions...
BOOL CMultTimerApp::InitInstance(void) {
    CMultTimerDlg *pDlg = new CMultTimerDlg();
  
    // this is also the application main window...
    m_pMainWnd = pDlg;

    // inform the user that this is the primary thread...
    CString csTitle;
    pDlg->GetWindowText(csTitle);
    csTitle += " (primary thread)";
    pDlg->SetWindowText(csTitle);

    return TRUE;
}

// CMultTimerThread thread class member functions...
CMultTimerThread::InitInstance(void) {
    // create a dialog that will run on a secondary thread...
    CMultTimerDlg *pDlg = new CMultTimerDlg();

    // set the dialog as the thread's main window,
    // this will allow the framework to terminate the
    // thread when the dialog box window is destroyed...
    SetMainWnd(pDlg);

    // inform the user that this is a child thread...
    CString csTitle;
    pDlg->GetWindowText(csTitle);
    csTitle += " (child thread)";
    pDlg->SetWindowText(csTitle);

    return TRUE;
}

BOOL CMultTimerThread::PreTranslateMessage( MSG *pMsg) {
    if (pMsg->message == WM_SHUTDOWN_THREAD) {
        // handle this message...
        OnShutdownThread( pMsg->wParam, pMsg->lParam);
        return 1;
    }
    else {
        // continue normal message processing...
        return 0;
    }
}

afx_msg void CMultTimerThread::OnShutdownThread( UINT wParam, LONG lParam) {
    // DestroyWindow is supposed to be called by the thread that
    // created the window...
    // The Primary thread tells the child threads to destroy their
    // windows and themselves by sending the WM_SHUTDOWN_THREAD message
    // which the child thread uses to call DestroyWindow in the correct
    // thread context...
    GetMainWnd()->DestroyWindow();
}    

// CMultTimerDlg dialog class message map...
BEGIN_MESSAGE_MAP( CMultTimerDlg, CDialog)
    ON_WM_PAINT()
    ON_WM_SYSCOMMAND()
END_MESSAGE_MAP()

// CMultTimerDlg dialog class member functions...
CMultTimerDlg::CMultTimerDlg() {
    m_ctStartTime = 0;
    m_bTimerRunning = FALSE;
    m_nTicks = 0;

    // The parent of the dialog window will be sent
    // WM_ACTIVATE and WM_ACTIVATEAPP messages when the new dialog 
    // is made active. these are sent from the thread of the new
    // dilaog and this will cause the dialog to be frozen 
    // if the parent is blocked, even if the parent window is
    // processed on another thread.
    //
    // In addition the system may attach the raw input queue of the
    // new thread to the thread of the parent if the parent is a
    // dialog box.
    //
    // It is best to use the desktop window as the
    // parent (or the new dialog window itself, 
    // which amounts to the same thing) so that the new dialog
    // has no dependancies on windows from other threads. Using
    // null makes the main window m_pMainWnd 
    // the dialog's parent.
    //
    // If the default parent is used, or the parent window
    // is NULL, the application main window will be used as
    // the parent, and this causes the blocking problem.
    Create(IDD_MULTTIMER_DLG, GetDesktopWindow());
}

void CMultTimerDlg::PostNcDestroy() {
    delete this;
}

// CMultTimerDlg dialog class message map handlers...
BOOL CMultTimerDlg::OnInitDialog() {
    CDialog::OnInitDialog();

    // attach our member object to the edit control...
    m_ceSeconds.SubclassDlgItem( IDC_SECONDS, this);
    m_ceSeconds.SetWindowText("5");

    // we must set the new dialog box as the foreground
    // window because dialogs created on secondary threads
    // will pop up BEHIND the active application window...
    SetForegroundWindow();

    // set the dialog icon...
    HICON hIcon = AfxGetApp()->LoadIcon(IDI_MULTTIMER);
    SetIcon( hIcon, TRUE);

    // place the window offset from the last one we created...
    if (s_LastDialogX || s_LastDialogY) {
        // not the first dialog...
        s_LastDialogX += 10;
        s_LastDialogY += 10;
        SetWindowPos( NULL, s_LastDialogX, s_LastDialogY, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
    }
    else {
        // this is the first dialog, center it and remember
        // where we put it...
        CenterWindow();
        CRect cr;
        GetWindowRect(cr);
        s_LastDialogX = cr.left;
        s_LastDialogY = cr.top;
    }

    return TRUE;
}

void CMultTimerDlg::OnSysCommand( UINT nID, LPARAM lParam) {
    if ((nID & 0xFFF0) == SC_CLOSE) {
        // treat the main window specially...
        if (this == theApp.m_pMainWnd) {
            // loop through the CMcl4MfcGUIThread pointers in our list
            // and shutdown each of the child dialogs...
            CMcl4MfcGUIThread *pThread;
            while (m_cqChildThreads.Get( pThread, 0)) {
                // Use the CWinThread object to instruct the thread to terminate...
                // The thread may have previously terminated, in which case this
                // will do nothing since the child thread's message pump
                // has already terminated...
                pThread->PostThreadMessage( WM_SHUTDOWN_THREAD, 0, 0);

                ///////////////////////////////////////////////////////////
                // AN ASIDE:
                // Sending a WM_CLOSE message to a MFC dialog box causes
                // the OnCancel member function to be called, which is not
                // what we want here.
                // The proper way to close a modeless dialog such as this 
                // is to have the dialog thread call DestroyWindow, which
                // we can do by sending a WM_SYSCOMMAND message with
                // the SC_CLOSE parameter...                
                ///////////////////////////////////////////////////////////

                // wait for the thread to exit...
                pThread->Wait(INFINITE);

                // delete the CMcl4MfcGUIThread object...
                delete pThread;
            }

            // destroy the modeless dialog window,
            // since this is the main window, the application
            // will exit...
            DestroyWindow();
        }
        else {
            // destroy the modeless dialog window...
            // this also terminates the thread since
            // the dialog was set to be the m_pMainWnd of the thread...
            DestroyWindow();
        }
    }
    else {
        CDialog::OnSysCommand( nID, lParam);
    }
}

void CMultTimerDlg::OnCancel() {
    // create a new dialog box running on it's own thread...
    CMcl4MfcGUIThread *pThread = new CMultTimerThread();

    // add the CMcl4MfcGUIThread pointer to our list of threads...
    m_cqChildThreads.Put(pThread);
}

void CMultTimerDlg::OnOK() {
    // tell the OnPaint handler that we are running...
    m_bTimerRunning = TRUE;

    // read the user input...
    CString csTime;
    m_ceSeconds.GetWindowText(csTime);

    // convert to clock ticks...
    m_nTicks = atoi(csTime) * CLOCKS_PER_SEC;

    // read the current time...
    m_ctStartTime = clock();

    // run in a tight loop...
    // we need the RedrawWindow to use SendMessage calls to
    // invoke the OnPaint handler when we are not processing
    // the application message loop...
    while ((clock() - m_ctStartTime) < m_nTicks) {
        // invalidate the time portion of the window and
        // update the display...
        RedrawWindow( m_crLastPaint, NULL, RDW_INVALIDATE | RDW_UPDATENOW);

        // update the display ten times a second...
        Sleep(100);    
    }

    // all done, tell the OnPaint handler we've stopped...
    m_bTimerRunning = FALSE;

    // update the time display one last time, this time we
    // don't need the RDW_UPDATENOW because we are going to
    // process messages again...
    RedrawWindow( m_crLastPaint, NULL, RDW_INVALIDATE);
}

afx_msg void CMultTimerDlg::OnPaint() {
    CPaintDC dc(this);

    // read the current time...
    clock_t ctNow = clock();
    clock_t ctRemaining;

    // compute the time remaining, show all zero's
    // if the timer is not running...
    if (m_bTimerRunning) {
        ctRemaining = m_nTicks - ctNow + m_ctStartTime;
    }
    else {
        ctRemaining = 0;
    }

    // compute the parts of time...
    int hours = ctRemaining / (CLOCKS_PER_SEC * 60 * 60);
    ctRemaining -= hours * (CLOCKS_PER_SEC * 60 * 60);
    int minutes = ctRemaining / (CLOCKS_PER_SEC * 60);
    ctRemaining -= minutes * (CLOCKS_PER_SEC * 60);
    int seconds = ctRemaining / (CLOCKS_PER_SEC);
    ctRemaining -= seconds * CLOCKS_PER_SEC;
    int hundreths = (ctRemaining * 100) / CLOCKS_PER_SEC;

    // format the time string...
    CString csTime;
    csTime.Format( "%02d:%02d:%02d:%02d", hours, minutes, seconds, hundreths);

    // compute the size of the time string...
    CSize sz = dc.GetTextExtent(csTime);

    // place time centered, one-fourth down in the window...
    CRect cr;
    GetClientRect(cr);
    int x = (cr.Width() - sz.cx) >> 1;
    int y = (cr.Height() - sz.cy) >> 2;

    m_crLastPaint = CRect( x, y, x + sz.cx, y + sz.cy);

    // set up the DC to draw the countdown time...
    UINT oldAlign = dc.SetTextAlign( TA_TOP | TA_LEFT);
    COLORREF oldBkColor = dc.SetBkColor( ::GetSysColor(COLOR_3DFACE));

    // use GREEN for a running timer and RED for a stopped timer...
    COLORREF oldTextColor = dc.SetTextColor( (m_bTimerRunning) ? RGB(0, 255, 0) : RGB(255, 0, 0));

    dc.ExtTextOut( x, y, ETO_OPAQUE, NULL, csTime, NULL);

    // restore the DC...
    dc.SetTextAlign(oldAlign);
    dc.SetBkColor(oldBkColor);
    dc.SetTextColor(oldTextColor);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -