www.pudn.com > DalsaNetlink.rar > CyDiagnosticPage.cpp
// CyDiagnosticPage.cpp : implementation file // #include "stdafx.h" #include "CyApp.h" #include "CyDiagnosticPage.h" #include "CyDiagnosticInfoDlg.h" #include #include #include #include #include #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // DiagnosticThread ///////////////////////////////////////////////////////////////////////////// #define GRAB_QUEUE_SIZE 4 class DiagnosticThread : public CyThread { public: DiagnosticThread( CyGrabber& aGrabber, unsigned long aBufferSize, unsigned long aBufferCount ) : mGrabber( aGrabber ) , mStopEvent( false, false ) , mGrabSemaphore( GRAB_QUEUE_SIZE, GRAB_QUEUE_SIZE ) , mJobQueueSemaphore( 0, aBufferCount ) { aBufferCount += GRAB_QUEUE_SIZE; // create the buffers mProcThread = new ProcessingThread( *this ); for ( unsigned long i = 0; i < ( aBufferCount ); ++i ) mFreeBuffers.push_back( new CySimpleBuffer( new unsigned char[ aBufferSize ], aBufferSize, i ) ); StartThread(); } virtual ~DiagnosticThread() { StopThread(); mStopEvent.Signal(); WaitUntilSignaled(); while ( mFreeBuffers.size() > 0 ) { delete mFreeBuffers.front(); mFreeBuffers.pop_front(); } delete mProcThread; mProcThread = NULL; } CyResult Grab( unsigned long aFlags ) { CySimpleBuffer* lBuffer; { CyLockScope lLS( mFreeBuffersGate ); if ( mFreeBuffers.empty() ) { Sleep( 1 ); return CY_RESULT_NOT_FOUND; } lBuffer = mFreeBuffers.front(); } CyResult lResult = mGrabber.Grab( CyChannel( 0 ), *lBuffer, CyGrabber::FLAG_NO_WAIT ); if ( lResult == CY_RESULT_OK ) { mFreeBuffers.pop_front(); mQueuedBuffers.push_back( lBuffer ); } return lResult; } // // Add a job to the job queue // void AddJob( CySimpleBuffer* aBuffer ) { CyAssert( aBuffer != NULL ); CyLockScope lLS( mJobQueueGate ); mJobQueue.push_back( aBuffer ); mJobQueueSemaphore.Release(); } // // Get a job from the job queue // bool GetJob( CySimpleBuffer** aBuffer, unsigned long aTimeout ) { CyAssert( aBuffer != NULL ); CyAssert( *aBuffer == NULL ); CyAssert( aTimeout > 0 ); *aBuffer = NULL; bool lResult = false; if ( mJobQueueSemaphore.WaitUntilSignaled( aTimeout ) == CY_RESULT_OK ) { CyLockScope lLS( mJobQueueGate ); if ( !mJobQueue.empty() ) { *aBuffer = mJobQueue.front(); mJobQueue.pop_front(); } lResult = true; } return lResult; } // // Returns a job to the acquisition thread // The buffer will simply be placed in the free buffer list // void JobFinished( CySimpleBuffer* aBuffer ) { CyAssert( aBuffer ); CyLockScope lLS( mFreeBuffersGate ); mFreeBuffers.push_back( aBuffer ); } virtual CyResult Run() { SetPriority( TIME_CRITICAL ); CySimpleBuffer* lBuffer; CyResult lResult; unsigned int lIndex; bool lRun = true; unsigned long lFlags = CyGrabber::FLAG_NO_WAIT; // Start the continuous mode mGrabber.StartContinuous( CyChannel( 0 ), true, 0 ); while ( lRun ) { // Prepare the events CySynchro::Vector lEvents; lEvents.AddSynchro( mStopEvent ); lEvents.AddSynchro( mGrabSemaphore ); // Just queue the first buffer, it is the next one to be ready anyway if ( mQueuedBuffers.size() > 0 ) lEvents.AddSynchro( mQueuedBuffers[ 0 ]->GetCompletionEvent() ); lResult = CySynchro::WaitUntilSignaled( lEvents ); lIndex = lResult - CySynchro::OBJECT_0; switch ( lIndex ) { default: CyAssert( false ); case 0: lRun = false; break; case 1: if ( Grab( lFlags ) != CY_RESULT_OK ) { lFlags = CyGrabber::FLAG_NO_WAIT; mGrabSemaphore.Release( 1 ); } else lFlags = CyGrabber::FLAG_NO_WAIT | CyGrabber::FLAG_NO_FLUSH; break; case 2: lBuffer = mQueuedBuffers.front(); mQueuedBuffers.pop_front(); // Queue the next buffer right away if ( Grab( lFlags ) != CY_RESULT_OK ) { lFlags = CyGrabber::FLAG_NO_WAIT; mGrabSemaphore.Release( 1 ); } else lFlags = CyGrabber::FLAG_NO_WAIT | CyGrabber::FLAG_NO_FLUSH; // Give the queue to the processing thread // We want the grabbing thread to do as little as possible // So we give the job to another thread. AddJob( lBuffer ); break; } } // Stop the continuous mode mGrabber.GetDevice().AbortReceiveData( CyChannel( 0 ) ); CyThread::Sleep( 1000 ); mGrabber.StopContinuous( CyChannel( 0 ) ); return CY_RESULT_OK; } // stats public: CyGate mStatsGate; std::map< CyResult, unsigned long > mErrorCounts; struct Stats { unsigned long mCount; unsigned long mFrameOverrun; unsigned long mGrabberFIFOOverrun; unsigned long mImageDropped; unsigned long mPartialLineMissing; unsigned long mFullLineMissing; unsigned long mExpectedResendCount; unsigned long mIgnoredPacketCount; unsigned long mLostPacketCount; unsigned long mResendRequestCount; unsigned long mStartPacketCount; unsigned long mUnexpectedResendCount; Stats() : mCount( 0 ) , mFrameOverrun( 0 ) , mGrabberFIFOOverrun( 0 ) , mImageDropped( 0 ) , mPartialLineMissing( 0 ) , mFullLineMissing( 0 ) , mExpectedResendCount( 0 ) , mIgnoredPacketCount( 0 ) , mLostPacketCount( 0 ) , mResendRequestCount( 0 ) , mStartPacketCount( 0 ) , mUnexpectedResendCount( 0 ) { } }; Stats mGoodStats; Stats mBadStats; std::deque mMissedID; private: class ProcessingThread : public CyThread { public: ProcessingThread( DiagnosticThread& aThread ) : mThread( aThread ) { StartThread(); } virtual ~ProcessingThread() { StopThread(); WaitUntilSignaled(); } virtual CyResult Run() { Stats * lStats; bool lFirst = true; unsigned long lCurrentID = 0; unsigned long lPreviousID = 0; CySimpleBuffer* lBuffer = 0; unsigned long i; // // Run loop // CyResult lResult; while ( !IsStopping() ) { // Get a job from the acquisition thread lBuffer = NULL; if ( !mThread.GetJob( &lBuffer, 1000 ) ) continue; CyLockScope lLS( mThread.mStatsGate ); lResult = lBuffer->GetCompletionEvent().GetResult(); if ( lResult == CY_RESULT_OK ) lStats = &mThread.mGoodStats; else { mThread.mErrorCounts[ lResult ] = mThread.mErrorCounts[ lResult ] + 1; lStats = &mThread.mBadStats; } lStats->mCount += 1; lStats->mFrameOverrun += lBuffer->GetSharedMemory().GetImageStatus().mFrameOverrun; lStats->mGrabberFIFOOverrun += lBuffer->GetSharedMemory().GetImageStatus().mGrabberFIFOOverrun; lStats->mImageDropped += lBuffer->GetSharedMemory().GetImageStatus().mImageDropped; lStats->mPartialLineMissing += lBuffer->GetSharedMemory().GetImageStatus().mPartialLineMissing; lStats->mFullLineMissing += lBuffer->GetSharedMemory().GetImageStatus().mFullLineMissing; lStats->mExpectedResendCount += lBuffer->GetSharedMemory().GetExpectedResendCount(); lStats->mIgnoredPacketCount += lBuffer->GetSharedMemory().GetIgnoredPacketCount(); lStats->mLostPacketCount += lBuffer->GetSharedMemory().GetLostPacketCount(); lStats->mResendRequestCount += lBuffer->GetSharedMemory().GetResendRequestCount(); lStats->mStartPacketCount += lBuffer->GetSharedMemory().GetStartPacketCount(); lStats->mUnexpectedResendCount += lBuffer->GetSharedMemory().GetUnexpectedResendCount(); lCurrentID = lBuffer->GetSharedMemory().GetImageID(); if ( !lFirst ) { if ( ( lCurrentID != 0 ) && ( lPreviousID != 0xffff ) ) { for ( i = lPreviousID + 1; i < lCurrentID; ++i ) mThread.mMissedID.push_back( i ); } } lFirst = false; lPreviousID = lCurrentID; // Release the job back to the acquisition thread if ( lBuffer != NULL ) mThread.JobFinished( lBuffer ); lBuffer = NULL; } return CY_RESULT_OK; } private: DiagnosticThread& mThread; }; private: CyGrabber& mGrabber; CyEvent mStopEvent; CySemaphore mGrabSemaphore; CyGate mFreeBuffersGate; std::deque mFreeBuffers; std::deque mQueuedBuffers; // Processing queue. Data processors will use the GetJob and JobFinished // methods to get jobs std::deque mJobQueue; CyGate mJobQueueGate; CySemaphore mJobQueueSemaphore; // Job processing thread ProcessingThread* mProcThread; }; ///////////////////////////////////////////////////////////////////////////// // CyDiagnosticPage property page IMPLEMENT_DYNCREATE(CyDiagnosticPage, CPropertyPage) CyDiagnosticPage::CyDiagnosticPage() : CPropertyPage(CyDiagnosticPage::IDD) , mThread( NULL ) { //{{AFX_DATA_INIT(CyDiagnosticPage) mBadItem1 = _T(""); mBadItem10 = _T(""); mBadItem11 = _T(""); mBadItem12 = _T(""); mBadItem2 = _T(""); mBadItem3 = _T(""); mBadItem4 = _T(""); mBadItem5 = _T(""); mBadItem6 = _T(""); mBadItem7 = _T(""); mBadItem8 = _T(""); mBadItem9 = _T(""); mErrorTypes = _T(""); mMissedID = _T(""); //}}AFX_DATA_INIT } CyDiagnosticPage::~CyDiagnosticPage() { } void CyDiagnosticPage::DoDataExchange(CDataExchange* pDX) { CPropertyPage::DoDataExchange(pDX); //{{AFX_DATA_MAP(CyDiagnosticPage) DDX_Control(pDX, IDC_SHOW_MISSED_ID, mShowMissedIDButton); DDX_Control(pDX, IDC_SHOW_ERROR_COUNTS, mShowErrorCountsButton); DDX_Control(pDX, IDC_DIAG_START_STOP, mStartStopButton); DDX_Control(pDX, IDC_DIAG_SAVE, mSaveButton); DDX_Text(pDX, IDC_BAD_ITEM1, mBadItem1); DDX_Text(pDX, IDC_BAD_ITEM10, mBadItem10); DDX_Text(pDX, IDC_BAD_ITEM11, mBadItem11); DDX_Text(pDX, IDC_BAD_ITEM12, mBadItem12); DDX_Text(pDX, IDC_BAD_ITEM2, mBadItem2); DDX_Text(pDX, IDC_BAD_ITEM3, mBadItem3); DDX_Text(pDX, IDC_BAD_ITEM4, mBadItem4); DDX_Text(pDX, IDC_BAD_ITEM5, mBadItem5); DDX_Text(pDX, IDC_BAD_ITEM6, mBadItem6); DDX_Text(pDX, IDC_BAD_ITEM7, mBadItem7); DDX_Text(pDX, IDC_BAD_ITEM8, mBadItem8); DDX_Text(pDX, IDC_BAD_ITEM9, mBadItem9); DDX_Text(pDX, IDC_GOOD_ITEM1, mGoodItem1); DDX_Text(pDX, IDC_GOOD_ITEM10, mGoodItem10); DDX_Text(pDX, IDC_GOOD_ITEM11, mGoodItem11); DDX_Text(pDX, IDC_GOOD_ITEM12, mGoodItem12); DDX_Text(pDX, IDC_GOOD_ITEM2, mGoodItem2); DDX_Text(pDX, IDC_GOOD_ITEM3, mGoodItem3); DDX_Text(pDX, IDC_GOOD_ITEM4, mGoodItem4); DDX_Text(pDX, IDC_GOOD_ITEM5, mGoodItem5); DDX_Text(pDX, IDC_GOOD_ITEM6, mGoodItem6); DDX_Text(pDX, IDC_GOOD_ITEM7, mGoodItem7); DDX_Text(pDX, IDC_GOOD_ITEM8, mGoodItem8); DDX_Text(pDX, IDC_GOOD_ITEM9, mGoodItem9); DDX_Text(pDX, IDC_ERROR_TYPES, mErrorTypes); DDX_Text(pDX, IDC_MISSED_ID, mMissedID); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CyDiagnosticPage, CPropertyPage) //{{AFX_MSG_MAP(CyDiagnosticPage) ON_WM_TIMER() ON_BN_CLICKED(IDC_DIAG_SAVE, OnDiagSave) ON_BN_CLICKED(IDC_DIAG_START_STOP, OnDiagStartStop) ON_BN_CLICKED(IDC_SHOW_ERROR_COUNTS, OnShowErrorCounts) ON_BN_CLICKED(IDC_SHOW_MISSED_ID, OnShowMissedId) ON_WM_DESTROY() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CyDiagnosticPage message handlers BOOL CyDiagnosticPage::OnSetActive() { CyCameraInterface& lCamera = reinterpret_cast( AfxGetApp() )->GetCamera(); CyDisplayEx& lDisplay = reinterpret_cast( AfxGetApp() )->GetDisplay(); CyImageBuffer& lBuffer = reinterpret_cast( AfxGetApp() )->GetBuffer( 0 ); bool lRunning = false; unsigned int i; if ( lBuffer.GetCapacity() <= 1 ) { MessageBox( "Please set-up the buffer by testing the acquisition first!" ); reinterpret_cast( GetParent() )->SetActivePage( 0 ); return FALSE; } // Check if the display is running for ( i = 0; ( !lRunning ) && ( i < lDisplay.GetSubDisplayCount() ); ++i ) { if ( lDisplay.IsDisplaying( i ) ) lRunning = true; } // Check if the grabber is running for ( i = 0; ( !lRunning ) && ( i < lCamera.GetGrabber().GetDevice().GetChannelCount() ); ++i ) { if ( lCamera.GetGrabber().IsStarted( CyChannel( i ) ) ) lRunning = true; } if ( lRunning ) { MessageBox( "You must stop any display or acquisition before using the diagnotic tool!" ); reinterpret_cast( GetParent() )->SetActivePage( 0 ); return FALSE; } else return CPropertyPage::OnSetActive(); } BOOL CyDiagnosticPage::OnKillActive() { if ( mThread != 0 ) OnDiagStartStop(); return CPropertyPage::OnKillActive(); } BOOL CyDiagnosticPage::OnInitDialog() { CPropertyPage::OnInitDialog(); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } void CyDiagnosticPage::OnTimer(UINT nIDEvent) { CString lTemp; if ( nIDEvent == 1 ) { DiagnosticThread * lThread = reinterpret_cast< DiagnosticThread* >( mThread ); CyLockScope lLS( lThread->mStatsGate ); mBadItem1.Format( "%lu", lThread->mBadStats.mCount ); mBadItem2.Format( "%lu", lThread->mBadStats.mFrameOverrun ); mBadItem3.Format( "%lu", lThread->mBadStats.mGrabberFIFOOverrun ); mBadItem4.Format( "%lu", lThread->mBadStats.mImageDropped ); mBadItem5.Format( "%lu", lThread->mBadStats.mPartialLineMissing ); mBadItem6.Format( "%lu", lThread->mBadStats.mFullLineMissing ); mBadItem7.Format( "%lu", lThread->mBadStats.mExpectedResendCount ); mBadItem8.Format( "%lu", lThread->mBadStats.mIgnoredPacketCount ); mBadItem9.Format( "%lu", lThread->mBadStats.mLostPacketCount ); mBadItem10.Format( "%lu",lThread->mBadStats.mResendRequestCount ); mBadItem11.Format( "%lu",lThread->mBadStats.mStartPacketCount ); mBadItem12.Format( "%lu",lThread->mBadStats.mUnexpectedResendCount ); mGoodItem1.Format( "%lu", lThread->mGoodStats.mCount ); mGoodItem2.Format( "%lu", lThread->mGoodStats.mFrameOverrun ); mGoodItem3.Format( "%lu", lThread->mGoodStats.mGrabberFIFOOverrun ); mGoodItem4.Format( "%lu", lThread->mGoodStats.mImageDropped ); mGoodItem5.Format( "%lu", lThread->mGoodStats.mPartialLineMissing ); mGoodItem6.Format( "%lu", lThread->mGoodStats.mFullLineMissing ); mGoodItem7.Format( "%lu", lThread->mGoodStats.mExpectedResendCount ); mGoodItem8.Format( "%lu", lThread->mGoodStats.mIgnoredPacketCount ); mGoodItem9.Format( "%lu", lThread->mGoodStats.mLostPacketCount ); mGoodItem10.Format( "%lu",lThread->mGoodStats.mResendRequestCount ); mGoodItem11.Format( "%lu",lThread->mGoodStats.mStartPacketCount ); mGoodItem12.Format( "%lu",lThread->mGoodStats.mUnexpectedResendCount ); mErrorTypes.Format( "%lu", lThread->mErrorCounts.size() ); mMissedID.Format( "%lu", lThread->mMissedID.size() ); UpdateData( FALSE ); } CPropertyPage::OnTimer(nIDEvent); } void CyDiagnosticPage::OnDiagSave() { CFileDialog lDialog( false, "txt", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Text Files (*.txt)|*.txt|All files (*.*)|*.*||" ); if ( lDialog.DoModal() == IDOK ) { FILE* lFile = fopen( lDialog.GetPathName(), "wt" ); if ( lFile == NULL ) { MessageBox( "Could not create file!" ); return; } DiagnosticThread * lThread = reinterpret_cast< DiagnosticThread* >( mThread ); CyLockScope lLS( lThread->mStatsGate ); fprintf( lFile, "Good images: %lu\n" "\t FrameOverrun : %lu\n" "\t GrabberFIFOOverrun : %lu\n" "\t ImageDropped : %lu\n" "\t PartialLineMissing : %lu\n" "\t FullLineMissing : %lu\n" "\t ExpectedResendCount : %lu\n" "\t IgnoredPacketCount : %lu\n" "\t LostPacketCount : %lu\n" "\t ResendRequestCount : %lu\n" "\t StartPacketCount : %lu\n" "\t UnexpectedResendCount: %lu\n", lThread->mGoodStats.mCount, lThread->mGoodStats.mFrameOverrun, lThread->mGoodStats.mGrabberFIFOOverrun, lThread->mGoodStats.mImageDropped, lThread->mGoodStats.mPartialLineMissing, lThread->mGoodStats.mFullLineMissing, lThread->mGoodStats.mExpectedResendCount, lThread->mGoodStats.mIgnoredPacketCount, lThread->mGoodStats.mLostPacketCount, lThread->mGoodStats.mResendRequestCount, lThread->mGoodStats.mStartPacketCount, lThread->mGoodStats.mUnexpectedResendCount ); fprintf( lFile, "\n" ); fprintf( lFile, "Bad images: %lu\n" "\t FrameOverrun : %lu\n" "\t GrabberFIFOOverrun : %lu\n" "\t ImageDropped : %lu\n" "\t PartialLineMissing : %lu\n" "\t FullLineMissing : %lu\n" "\t ExpectedResendCount : %lu\n" "\t IgnoredPacketCount : %lu\n" "\t LostPacketCount : %lu\n" "\t ResendRequestCount : %lu\n" "\t StartPacketCount : %lu\n" "\t UnexpectedResendCount: %lu\n", lThread->mBadStats.mCount, lThread->mBadStats.mFrameOverrun, lThread->mBadStats.mGrabberFIFOOverrun, lThread->mBadStats.mImageDropped, lThread->mBadStats.mPartialLineMissing, lThread->mBadStats.mFullLineMissing, lThread->mBadStats.mExpectedResendCount, lThread->mBadStats.mIgnoredPacketCount, lThread->mBadStats.mLostPacketCount, lThread->mBadStats.mResendRequestCount, lThread->mBadStats.mStartPacketCount, lThread->mBadStats.mUnexpectedResendCount ); fprintf( lFile, "\n" ); std::map< CyResult, unsigned long >::const_iterator lItr1; for ( lItr1 = lThread->mErrorCounts.begin(); lItr1 != lThread->mErrorCounts.end(); ++lItr1 ) { if ( lItr1 == lThread->mErrorCounts.begin() ) fprintf( lFile, "Encountered errors:\n" ); fprintf( lFile, "\t%s: %lu\n", CoyoteStatusMessage( CyResult( lItr1->first ) ), lItr1->second ); } fprintf( lFile, "\n" ); std::deque::const_iterator lItr2; for ( lItr2 = lThread->mMissedID.begin(); lItr2 != lThread->mMissedID.end(); ++lItr2 ) { if ( lItr2 == lThread->mMissedID.begin() ) fprintf( lFile, "Missed Image IDs:\n" ); fprintf( lFile, "\t%lu\n", *lItr2 ); } fprintf( lFile, "\n" ); fclose( lFile ); } } void CyDiagnosticPage::OnDiagStartStop() { if ( mThread == 0 ) { CyGrabber& lGrabber = reinterpret_cast( AfxGetApp() )->GetGrabber(); CyImageBuffer& lBuffer = reinterpret_cast( AfxGetApp() )->GetBuffer( 0 ); // check the size of the image. unsigned long lBufferCount = lBuffer.GetQueueSize(); // check the image size, if it is smaller unsigned long lMaxFrameRate = static_cast( ceil( 100000000.0 / static_cast( lBuffer.GetCapacity() ) ) ); lMaxFrameRate /= 192; if ( lBufferCount < lMaxFrameRate ) lBufferCount = lMaxFrameRate; // Start the thread mThread = new DiagnosticThread( lGrabber, lBuffer.GetCapacity(), lBufferCount ); mStartStopButton.SetWindowText( "Stop" ); SetTimer( 1, 500, NULL ); mShowErrorCountsButton.EnableWindow( TRUE ); mShowMissedIDButton.EnableWindow( TRUE ); mSaveButton.EnableWindow( TRUE ); } else { mShowErrorCountsButton.EnableWindow( FALSE ); mShowMissedIDButton.EnableWindow( FALSE ); mSaveButton.EnableWindow( FALSE ); KillTimer( 1 ); delete mThread; mThread = NULL; mStartStopButton.SetWindowText( "Start" ); } } void CyDiagnosticPage::OnShowErrorCounts() { DiagnosticThread * lThread = reinterpret_cast< DiagnosticThread* >( mThread ); CyDiagnosticInfoDlg lDlg; CyDiagnosticInfoDlg::Column lCol1, lCol2; lCol1.mHeader = "Error type"; lCol1.mWidth = 250; lCol2.mHeader = "Count"; lCol2.mWidth = 75; { CyLockScope lLS( lThread->mStatsGate ); CString lTemp; std::map< CyResult, unsigned long >::const_iterator lItr; for ( lItr = lThread->mErrorCounts.begin(); lItr != lThread->mErrorCounts.end(); ++lItr ) { lCol1.mItems.push_back( CoyoteStatusMessage( CyResult( lItr->first ) ) ); lTemp.Format( "%lu", lItr->second ); lCol2.mItems.push_back( (LPCTSTR) lTemp ); } } lDlg.mData.push_back( lCol1 ); lDlg.mData.push_back( lCol2 ); lDlg.DoModal(); } void CyDiagnosticPage::OnShowMissedId() { DiagnosticThread * lThread = reinterpret_cast< DiagnosticThread* >( mThread ); CyDiagnosticInfoDlg lDlg; CyDiagnosticInfoDlg::Column lCol1; lCol1.mHeader = "Missed Image ID"; lCol1.mWidth = 100; { CyLockScope lLS( lThread->mStatsGate ); CString lTemp; std::deque::const_iterator lItr; for ( lItr = lThread->mMissedID.begin(); lItr != lThread->mMissedID.end(); ++lItr ) { lTemp.Format( "%lu", *lItr ); lCol1.mItems.push_back( (LPCTSTR) lTemp ); } } lDlg.mData.push_back( lCol1 ); lDlg.DoModal(); } void CyDiagnosticPage::OnDestroy() { if ( mThread != NULL ) OnDiagStartStop(); CPropertyPage::OnDestroy(); }