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

📄 multtimer.cpp

📁 window下的多线程编程参考书。值得一读
💻 CPP
字号:
//
// FILE: MultTimer.cpp
//
// Copyright (c) 1997 by Aaron Michael Cohen and Mike Woodring
//
/////////////////////////////////////////////////////////////////////////
#include "MultTimer.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;

// CMultTimerApp application class member functions...
BOOL CMultTimerApp::InitInstance(void) {
    // no child threads at first...
    m_lChildThreadCount = 0;

    CMultTimerDlg *pDlg = new CMultTimerDlg();
  
    // this is also the application main window...
    m_pMainWnd = pDlg;

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

    return TRUE;
}

void CMultTimerApp::IncrementChildThreadCount(void) {
    ::InterlockedIncrement(&m_lChildThreadCount);
}

void CMultTimerApp::DecrementChildThreadCount(void) {
    ::InterlockedDecrement(&m_lChildThreadCount);
}

BOOL CMultTimerApp::CanExitNow(void) {
    return (m_lChildThreadCount == 0);
}

CMultTimerThread::InitInstance(void) {
    // increase the application child thread count...
    theApp.IncrementChildThreadCount();

    // create a dialog that will run on a secondary thread...
    CMultTimerDlg *pDlg = new CMultTimerDlg();
    
    // set the main window for this thread
    // the thread will exit when this window closes...
    SetMainWnd(pDlg);

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

    return TRUE;
}

int CMultTimerThread::ExitInstance(void) {
    // decrease the application child thread count...
    theApp.DecrementChildThreadCount();

    // call the parent class implementation...
    int nStatus = CMcl4MfcGUIThread::ExitInstance();

    // delete ourselves...
    delete this;

    // return the saved status...
    return nStatus;
}

// 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) {
            // this is the dialog on the primary thread,
            // check if the other dialog threads have exited and
            // only exit the program if this is TRUE...
            if (theApp.CanExitNow()) {
                // destroy the modeless dialog window,
                // since this is the main window, the application
                // will exit...
                DestroyWindow();
            }
            else {
                AfxMessageBox( "Cannot exit, child dialogs still open.", MB_ICONSTOP);
            }
        }
        else {
            // destroy the modeless dialog window...
            DestroyWindow();

            // Destroying the main window for a thread will cause the
            // thread to exit. If we had not set the main window for the
            // thread to this window, we could exit the thread with a
            // call to ::PostQuitMessage(0);
        }
    }
    else {
        CDialog::OnSysCommand( nID, lParam);
    }
}

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

    // dialog box thread C++ object will delete itself in
    // it's ExitInstance handler...
}

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, 
    // if the timer is not running show all zero's...
    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 + -