Click here to Skip to main content
15,896,557 members
Articles / Programming Languages / C++

Using CodeProject - A Day In the Life of an Application - Part 3 of 5

Rate me:
Please Sign up or sign in to vote.
4.63/5 (20 votes)
27 Jan 2007CPOL14 min read 43.3K   436   17  
The right way to code using CodeProject for occasional support
// MainFrm.cpp : implementation of the CMainFrame class
//

#include "stdafx.h"
#include "SDIMultiApp1.h"

#include "MainFrm.h"

#include "DlgOne.h"
#include "DlgTwo.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame message map
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
	ON_WM_CREATE()
	ON_WM_CLOSE()

	ON_COMMAND				(ID_VIEW_PRIMARYVIEW,	&CMainFrame::OnViewPrimaryview			)
	ON_UPDATE_COMMAND_UI	(ID_VIEW_PRIMARYVIEW,	&CMainFrame::OnUpdateViewPrimaryview	)
	ON_COMMAND				(ID_VIEW_SECONDARYVIEW,	&CMainFrame::OnViewSecondaryview		)
	ON_UPDATE_COMMAND_UI	(ID_VIEW_SECONDARYVIEW,	&CMainFrame::OnUpdateViewSecondaryview	)

	ON_MESSAGE(UDM_TIMER_LONG,		OnTimerLong		)
	ON_MESSAGE(UDM_TIMER_SHORT,		OnTimerShort	)
	ON_MESSAGE(UDM_TIMER_ELAPSED,	OnTimerElapsed	)
	ON_MESSAGE(UDM_TIMER_DATETIME,	OnTimerDateTime	)
	ON_MESSAGE(UDM_LONG_TASK_DONE,	OnLongTaskDone	)
	ON_MESSAGE(UDM_SHORT_TASK_DONE,	OnShortTaskDone	)

	ON_COMMAND(ID_SAMPLESTUFF_DIALOGONE, &CMainFrame::OnSamplestuffDialogone)
	ON_COMMAND(ID_SAMPLESTUFF_DIALOGTWO, &CMainFrame::OnSamplestuffDialogtwo)
END_MESSAGE_MAP()


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame status bar panes
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
static UINT indicators[] =
{
	ID_SEPARATOR,           // status line indicator
/*
ID_INDICATOR_CAPS,
	ID_INDICATOR_NUM,
	ID_INDICATOR_SCRL,
*/
};


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
CMainFrame::CMainFrame()
{
	m_pLongStatus        = NULL;
	m_pShortStatus       = NULL;
	m_pSysTimeStatus     = NULL;
	m_pTimersThread      = NULL;
	m_pThreadActionLong  = NULL;
	m_pThreadActionShort = NULL;
	m_pLongRunning       = NULL;
	m_pShortRunning      = NULL;
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
CMainFrame::~CMainFrame()
{
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame overrides
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if (!CFrameWnd::PreCreateWindow(cs))
	{
		return FALSE;
	}
	return TRUE;
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
		| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
		!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
	{
		TRACE0("Failed to create toolbar\n");
		return -1;      // fail to create
	}

	if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators,
	    sizeof(indicators)/sizeof(UINT)))
	{
		TRACE0("Failed to create status bar\n");
		return -1;      // fail to create
	}
	// The default height of the statusbar chops off descenders in lowercase 
	// letters like "y", "g", and "p" in our sunken statusbar panes. Because 
	// of this, we need to set our statusbar height to be just a little taller 
	// than default.  To set the statusbar height, we need to send the 
	// SB_SETMINHEIGHT message with the desired height being passed as a 
	// WPARAM.  Finally, we need to call UpdateWindow to make sure the 
	// statusbar is resized.
	m_wndStatusBar.SendMessage(SB_SETMINHEIGHT, 20, 0);
	m_wndStatusBar.UpdateWindow();

	// now, let's add our new statusbar panes
	AddStatusPanes();

	// TODO: Delete these three lines if you don't want the toolbar to be dockable
	m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
	EnableDocking(CBRS_ALIGN_ANY);
	DockControlBar(&m_wndToolBar);

	return 0;
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
	CRect cr;
	GetWindowRect(&cr);
	// we need to subtract a reasonable value from the returned screen height 
	// so that when we actually create the view, it won't cover up part of the 
	// screen with it's soon-to-be-resized rectangle.
	int nHeight = ::GetSystemMetrics(SM_CYSCREEN) - 100;

	// our splitter window will be static
	if (!m_mainSplitter.CreateStatic(this, 2, 1))
	{
		AfxMessageBox("Error setting up splitter window", MB_ICONERROR);
		return FALSE;
	}

	// add our replacable views - the view IDs are defined in constants.h
	m_mainSplitter.AddSwitchableView(RUNTIME_CLASS(CPrimaryView  ), pContext, CRect(0, 1, cr.Width(), nHeight), IDC_PANE0_PRIMARY_VIEW  );
	m_mainSplitter.AddSwitchableView(RUNTIME_CLASS(CSecondaryView), pContext, CRect(0, 1, cr.Width(), nHeight), IDC_PANE0_SECONDARY_VIEW);

	// create views in the splitter
	// we need a blank view to start off in the upper pane as the "old view 
	// because part of the initialization of the app is changing to the proper 
	// view. We'll use the view created by the app wizard.
	m_mainSplitter.CreateView(0, 0, RUNTIME_CLASS(CSDIMultiApp1View), CSize(cr.Width(), nHeight), pContext);
	// and for the lower pane, we'll use our CInfoView class.
	m_mainSplitter.CreateView(1, 0, RUNTIME_CLASS(CInfoView),  CSize(cr.Width(), 0      ), pContext);

	return TRUE;
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
	CFrameWnd::AssertValid();
}
void CMainFrame::Dump(CDumpContext& dc) const
{
	CFrameWnd::Dump(dc);
}
#endif //_DEBUG



////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame programmer-added functions
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------------
// Initialize the splitter window with the desired initial views.
//------------------------------------------------------------------------------
bool CMainFrame::PrepareViews()
{
	bool bResult = m_mainSplitter.SwitchView(IDC_PANE0_PRIMARY_VIEW, 0, 0);
	//m_mainSplitter.RecalcLayout();

	if (bResult)
	{
		CPrimaryView* pView1 = GetPrimaryView();
		if (pView1)
		{
			// do view intizialtion here
		}
		else
		{
			bResult = false;
		}
		if (bResult)
		{
			CInfoView* pView2 = GetInfoView();
			if (pView2)
			{
				// initizliae view here
			}
			else
			{
				bResult = false;
			}
		}
	}
	return bResult;
}

////////////////////////////////////////////////////////////////////////////////
//                           Code for changing views                          //
////////////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------------
// Gets the pointer to the primary view. Since we're using dynamic_cast, this 
// function will return NULL if the view in the upper pain isn't the one we're 
// expecting.
//------------------------------------------------------------------------------
CPrimaryView* CMainFrame::GetPrimaryView()
{
	return (dynamic_cast<CPrimaryView*>(m_mainSplitter.GetPane(0,0)));
}

//------------------------------------------------------------------------------
// Gets the pointer to the scondary view. Since we're using dynamic_cast, 
// this function will return NULL if the view in the upper pain isn't the one 
// we're expecting.
//------------------------------------------------------------------------------
CSecondaryView* CMainFrame::GetSecondaryView()
{
	return (dynamic_cast<CSecondaryView*>(m_mainSplitter.GetPane(0,0)));
}

//------------------------------------------------------------------------------
// Gets the pointer to the info view. Since we're using dynamic_cast, this 
// function will return NULL if the view in the upper pain isn't the one we're 
// expecting.  In the case of the info view, we should never get a null 
// pointer since that's the only view currently residing in the bottom pane.
//------------------------------------------------------------------------------
CInfoView* CMainFrame::GetInfoView()
{
	return (dynamic_cast<CInfoView*>(m_mainSplitter.GetPane(1,0)));
}

////////////////////////////////////////////////////////////////////////////////
//                         Code for status bar panes                          //
////////////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------------
// Add the specified pane.
//------------------------------------------------------------------------------
BOOL CMainFrame::AddOnePane(CStatusStatic* pPane, UINT nPaneID, CString sDefaultText, int nPaneWidth)
{
	ASSERT(pPane);
	ASSERT(nPaneID > 0);

	DWORD dwFlags  = WS_CHILD|WS_VISIBLE|SS_CENTER|SS_CENTERIMAGE|SS_NOPREFIX;
	CRect ctrlRect(0,0,0,0);
	BOOL bReturn   = FALSE;
	int nIndex     = -1;
	//int nPaneWidth = 150;

	if (!pPane->Create(sDefaultText, dwFlags, ctrlRect,  &m_wndStatusBar, 0))
	{
		return FALSE;
	}

	// We MUST set the font for each pane. If we don't, the pane will be set to 
	// 12-point MS Shell Dlg.  This should actually be set within the 
	// ExtStatusBarControl class, but I wanted to illustrate the need to do 
	// this.
	pPane->SetFont(m_wndStatusBar.GetFont());

	bReturn = m_wndStatusBar.AddPane(nPaneID, 1);
	if (!bReturn)
	{
		AfxMessageBox(_T("Pane index out of range\nor pane with same ID already exists in the status bar"), MB_ICONERROR);
		return FALSE;
	}
	nIndex = m_wndStatusBar.CommandToIndex(nPaneID);
	if (nIndex == -1)
	{
		return FALSE;
	}
	m_wndStatusBar.SetPaneWidth(nIndex, nPaneWidth);
	m_wndStatusBar.AddPaneControl(pPane, nPaneID, true);

	return TRUE;
}

//------------------------------------------------------------------------------
// Adds the status panes to our custom status bar.
//------------------------------------------------------------------------------
BOOL CMainFrame::AddStatusPanes()
{
	m_pSysTimeStatus = new CStatusStatic;
	if (m_pSysTimeStatus)
	{
		if (!AddOnePane(m_pSysTimeStatus, ID_STATUSPANE_TIME, " 00/00/0000    00:00 ", 110))
		{
			if (m_pSysTimeStatus)
			{
				delete m_pSysTimeStatus;
				m_pSysTimeStatus = NULL;
			}
			return FALSE;
		}
	}
	else
	{
		return FALSE;
	}

	m_pLongStatus = new CStatusStatic;
	if (m_pLongStatus)
	{
		if (!AddOnePane(m_pLongStatus, ID_STATUSPANE_LONGUPDATE, " Nextg Long Update - 00:00 ", 150))
		{
			delete m_pLongStatus;
			m_pLongStatus = NULL;
			return FALSE;
		}
	}
	else
	{
		return FALSE;
	}

	m_pShortStatus = new CStatusStatic;
	if (m_pShortStatus)
	{
		if (!AddOnePane(m_pShortStatus, ID_STATUSPANE_SHORTUPDATE, " Next Short Update - 00:00 ", 150))
		{
			delete m_pShortStatus;
			m_pShortStatus = NULL;
			return FALSE;
		}
	}
	else
	{
		return FALSE;
	}

	m_pLongRunning = new CStatusStatic;
	if (m_pLongRunning)
	{
		if (!AddOnePane(m_pLongRunning, ID_STATUSPANE_LONGRUNNING, "", 90))
		{
			delete m_pLongRunning;
			m_pLongRunning = NULL;
			return FALSE;
		}
	}
	else
	{
		return FALSE;
	}
	m_pShortRunning = new CStatusStatic;
	if (m_pShortRunning)
	{
		if (!AddOnePane(m_pShortRunning, ID_STATUSPANE_SHORTRUNNING, "", 90))
		{
			delete m_pShortRunning;
			m_pShortRunning = NULL;
			return FALSE;
		}
	}
	else
	{
		return FALSE;
	}

	return TRUE;
}

////////////////////////////////////////////////////////////////////////////////
//                              Code for threads                              //
////////////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------------
// Create the timer thread. The timer thread can support multiple timers. At 
// it's most simple, it can count down an interval of milliseconds and fire a 
// message back to the specified window.  In our case, the timers are attached 
// to our custom statusbar panes. The reason is because the timer updates a 
// couple of the status bar panes every second (countdown timers). One of the 
// timers doesn't fir a message at all (but it could), and the other sends a 
// message to update an elapsed time in the grid.
//------------------------------------------------------------------------------
BOOL CMainFrame::CreateTimerThread()
{
	// In my app, the settings are held in a XML file.  For purposes of example, 
	// we'll just hard code the intervals to expedite the article.
	int nLongInterval      = 300000; // 5 minutes
	int nShortInterval     = 15000;
	bool bAllowLongUpdate  = true;
	bool bAllowShortUpdate = true;

	if (!bAllowLongUpdate)
	{
		nLongInterval = 0;
	}
	if (!bAllowShortUpdate)
	{
		nShortInterval = 0;
	}

	// create the new thread - all timer messages will be sent to this object, 
	// and if necessary, reflected to the current view(s).
	m_pTimersThread = new CTimersThread((void*)this, 0);
	if (m_pTimersThread)
	{
		m_pTimersThread->SetInterval(1000);
		// having these two timers seems redundant since they both fire at 
		// the same time - see about combining them
		m_pTimersThread->SetTimer(TIMER_DATETIME, 0,                 1000,           "Current Date/Time",      " %m/%d/%Y  %H:%M ",               m_pSysTimeStatus );
		m_pTimersThread->SetTimer(TIMER_ELAPSED,  UDM_TIMER_ELAPSED, 1000,           "Patient's Elapsed Time", "",                                NULL             );
		m_pTimersThread->SetTimer(TIMER_LONG,     UDM_TIMER_LONG,    nLongInterval,  "Long Update",            " Next Long Update - %02d:%02d ",  m_pLongStatus    );
		m_pTimersThread->SetTimer(TIMER_SHORT,    UDM_TIMER_SHORT,   nShortInterval, "Short Update",           " Next Short Update - %02d:%02d ", m_pShortStatus   );
		// sanity checks - if we don't want automatic updates, or if something went 
		// wrong when ewe retrieved the intervals, the timers need to be turned off 
		// to avoid unexpected/undesireable events.
		if (!bAllowLongUpdate || nLongInterval == 0)
		{
			m_pTimersThread->EnableTimer(TIMER_LONG, false);
		}
		if (!bAllowShortUpdate || nShortInterval == 0)
		{
			m_pTimersThread->EnableTimer(TIMER_SHORT, false);
		}
	}

	return (m_pTimersThread != NULL);
}

//------------------------------------------------------------------------------
// Starts the timer thread. All timers in the thread start at the same time.
//------------------------------------------------------------------------------
void CMainFrame::StartTimerThread()
{
	if (m_pTimersThread)
	{
		m_pTimersThread->Start();
	}
}

//------------------------------------------------------------------------------
// Stop the timer thread.
//------------------------------------------------------------------------------
void CMainFrame::StopTimerThread()
{
	if (m_pTimersThread)
	{
		m_pTimersThread->Kill();
	}
}


//------------------------------------------------------------------------------
// Creates our action threads that do something important when signaled to do 
// so. It would be a simple matter to add more threads if necessary. I guess 
// you could think of this as a rudimentary thread pool. The threads are started 
// in response to messages received by this window from the timer thread.
//------------------------------------------------------------------------------
BOOL CMainFrame::CreateActionThreads()
{
	// Create them, but don't start them. This lets us start them over and over 
	// again without having to deal with the overhead of creating/deleting 
	// threads that are used repeatedly.
	m_pThreadActionLong  = new CThreadActionLong ((void*)this, UDM_LONG_TASK_DONE);
	m_pThreadActionShort = new CThreadActionShort((void*)this, UDM_SHORT_TASK_DONE);

	return (m_pThreadActionLong && m_pThreadActionShort);
}


//------------------------------------------------------------------------------
// Pauses the timers thread 
//------------------------------------------------------------------------------
void CMainFrame::PauseTimers()
{
	// make sure we have a valid pointer
	if (m_pTimersThread)
	{
		m_pTimersThread->Pause();
	}
}

//------------------------------------------------------------------------------
// Un-pauses the timers thread 
//------------------------------------------------------------------------------
void CMainFrame::ContinueTimers()
{
	// make sure we have a valid pointer
	if (m_pTimersThread)
	{
		m_pTimersThread->Continue();
	}
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame programmer-defined message handlers
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------------
// This function handles the message sent from the timer thread for the long 
// timer.
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnTimerLong(WPARAM wParam, LPARAM lParam)
{
	wParam;
	lParam;
	if (m_pTimersThread)
	{
		EDTIMER* pTimer = m_pTimersThread->GetTimerByID(TIMER_LONG);
		if (pTimer)
		{
			CStatusStatic* pPane = pTimer->pStatusPane;
			if (pPane)
			{
				pPane->SetWindowText(" Next Long Update - updating ");
			}
		}
	}

	// Make sure we have a thread to run, and make sure its not already running.
	// If it is running, we just skip over so we don't fall behind in 
	// handling further activation messages from the timer thread.
	if (m_pThreadActionLong && m_pThreadActionLong->GetActivityStatus() != CThread::THREAD_RUNNING)
	{
		m_pLongRunning->SetWindowText(" Long Running  ");
		m_pThreadActionLong->Start();
	}
	return 1L;
}

//------------------------------------------------------------------------------
// This function handles the message sent from the timer thread for the short 
// timer.
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnTimerShort(WPARAM wParam, LPARAM lParam)
{
	wParam;
	lParam;
	if (m_pTimersThread)
	{
		EDTIMER* pTimer = m_pTimersThread->GetTimerByID(TIMER_SHORT);
		if (pTimer)
		{
			CStatusStatic* pPane = pTimer->pStatusPane;
			if (pPane)
			{
				pPane->SetWindowText(" Next Short Update - updating ");
			}
		}
	}

	// Make sure we have a thread to run, and make sure its not already running.
	// If it is running, we just skip over so we don't fall behind in 
	// handling further activation messages from the timer thread.
	if (m_pThreadActionShort && m_pThreadActionShort->GetActivityStatus() != CThread::THREAD_RUNNING)
	{
		m_pShortRunning->SetWindowText(" Short Running ");
		m_pThreadActionShort->Start();
	}
	return 1L;
}

//------------------------------------------------------------------------------
// This function handles the message sent from the timer thread for the elapsed 
// timer.
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnTimerElapsed(WPARAM wParam, LPARAM lParam)
{
	wParam;
	lParam;
	return 1L;
}

//------------------------------------------------------------------------------
// This function handles the message sent from the timer thread for the 
// date/time timer.  For now, no message is sent because the thread itself 
// updates the date/time statusbar pane, and nothing else needs to be done.
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnTimerDateTime(WPARAM wParam, LPARAM lParam)
{
	wParam;
	lParam;
	return 1L;
}

//------------------------------------------------------------------------------
// This function handles the message sent by the long action thread when that 
// thread has completed it's task.
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnLongTaskDone(WPARAM wParam, LPARAM lParam)
{
	wParam;
	lParam;
	m_pLongRunning->SetWindowText("");
	return 1L;
}

//------------------------------------------------------------------------------
// This function handles the message sent by the short action thread when that 
// thread has completed it's task.  
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnShortTaskDone(WPARAM wParam, LPARAM lParam)
{
	wParam;
	lParam;
	m_pShortRunning->SetWindowText("");
	return 1L;
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------------
// Switch to the grid view in the upper splitter pane
//------------------------------------------------------------------------------
void CMainFrame::OnViewPrimaryview()
{
	m_mainSplitter.SwitchView(IDC_PANE0_PRIMARY_VIEW, 0, 0);
}

//------------------------------------------------------------------------------
// If we're not already looking at the grid view, enable the menu item that 
// allows us to select it.
//------------------------------------------------------------------------------
void CMainFrame::OnUpdateViewPrimaryview(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(!m_mainSplitter.GetIsActiveView(IDC_PANE0_PRIMARY_VIEW, 0, 0));
}

//------------------------------------------------------------------------------
// Switch to the gdi view in the upper splitter pane
//------------------------------------------------------------------------------
void CMainFrame::OnViewSecondaryview()
{
	m_mainSplitter.SwitchView(IDC_PANE0_SECONDARY_VIEW, 0, 0);
}

//------------------------------------------------------------------------------
// If we're not already looking at the GDI view, enable the menu item that 
// allows us to select it.
//------------------------------------------------------------------------------
void CMainFrame::OnUpdateViewSecondaryview(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(!m_mainSplitter.GetIsActiveView(IDC_PANE0_SECONDARY_VIEW, 0, 0));
}

//------------------------------------------------------------------------------
// We need to kill and delete the timer thread BEFORE the destructor is called 
// in CMainFrame because the thread has pointers to the custom statusbar panes.
// Since we're killing the timer thread here, we may as well wait for the action
// threads if they're running.
//------------------------------------------------------------------------------
void CMainFrame::OnClose()
{
	if (m_pThreadActionLong)
	{
		m_pThreadActionLong->Kill();
		// we may have to wait for the thread to stop, so just sit and spin here
		while (m_pThreadActionLong->IsAlive()) {}
		delete m_pThreadActionLong;
	}
	if (m_pThreadActionShort)
	{
		m_pThreadActionShort->Kill();
		// we may have to wait for the thread to stop, so just sit and spin here
		while (m_pThreadActionShort->IsAlive()) {}
		delete m_pThreadActionShort;
	}
	if (m_pTimersThread)
	{
		m_pTimersThread->Kill();
		// we may have to wait for the thread to stop, so just sit and spin here
		while (m_pTimersThread->IsAlive()) {}
		m_pTimersThread->Cleanup();
		delete m_pTimersThread;
	}
	m_pTimersThread = NULL;

	CFrameWnd::OnClose();
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CMainFrame::OnSamplestuffDialogone()
{
	PauseTimers();
	CDlgOne dlg;
	dlg.DoModal();
	ContinueTimers();
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CMainFrame::OnSamplestuffDialogtwo()
{
	CDlgTwo dlg;
	dlg.DoModal();
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Paddedwall Software
United States United States
I've been paid as a programmer since 1982 with experience in Pascal, and C++ (both self-taught), and began writing Windows programs in 1991 using Visual C++ and MFC. In the 2nd half of 2007, I started writing C# Windows Forms and ASP.Net applications, and have since done WPF, Silverlight, WCF, web services, and Windows services.

My weakest point is that my moments of clarity are too brief to hold a meaningful conversation that requires more than 30 seconds to complete. Thankfully, grunts of agreement are all that is required to conduct most discussions without committing to any particular belief system.

Comments and Discussions