Hello, I am Hoon-sup, Kim as a beginner for multi-thread.
I would like to use multi-thread MFC code which are consist of three threads(DAQThread, DSPThread, DRAWThread). As you can see in the bottom of this letter, there are three files which are MainFrm1.h, MainFrm1.cpp and ChildView1.cpp. This code can play a role
in acquiring CCD camera data and then signal processing of it and last drawing the resultant image of it. When I push the play button, OnDaqOctplaying() function is called and the play button is toggled into stop button and the display shows me real time image
at 10 frames per sec correctly. However, as I want to finish the thread operation, and when I push the stop button which goes into botton part in OnDaqOctplaying(), 126byte size memory leak error occurs because the DRAWThread is terminated forcefully
as you can see in the
Debug mode Output display. I used global variable such as DAQThreadFlag, DSPThreadFlag and DRAWThreadFlag
to finish each thread by itself. However I don't know why the DRAWThread is not finished by itself. I want to know whether it is correct that I am calling RedrawWindow() function in DRAWThread which belongs to CMainFrm class.
CChildView *pView = (CChildView *)pParam;
pView->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
Is it correct thatI use CChildView *pView when I want to call OnPaint() function of CChildView class in a thread of
CMainFrame class?
When I try to finish DRAWThread by itself via global variables, it seems like deadlock and then finally WAIT_TIMEOUT
happened.
According to a book about MFC multi-thread, it is said that when we need to access to Main frame window or View window in a specific worker thread under multi-thread, User defined message is recommended
to use. For examples,GetActiveView()->PostMessage(....) But there is no any reason why I use User defined message in this situation and I have no any idea about
how to implement it in detail.
Please let me know which part or structure of the code that I attached is not correct and do I need toexplicitly write NULL to Handle of DRAWThread right before finishing it?
Thank you very much any way.
----Debug mode Output display:----------------------------------------
DAQ Thread End
The thread 'Win64 Thread' (0xd50) has exited with code 0 (0x0).
pDAQThread is terminated normally
pDAQThread is terminated(code=0)
DSP Thread End
The thread 'Win64 Thread' (0x17b4) has exited with code 0 (0x0).
pDSPThread is terminated normally
pDSPThread is terminated(code=0)
pDRAWThread is terminated FORCELY!!!
The thread 'Win64 Thread' (0x17c8) has exited with code 0 (0x0).
pDRAWThread is terminated(code=259)
The thread 'Win64 Thread' (0x17f4) has exited with code 0 (0x0).
all thread finish?...
Warning: no message line prompt for ID 0x800C.
Warning: no message line prompt for ID 0x800D.
Warning: no message line prompt for ID 0x801F.
Warning: no message line prompt for ID 0x800E.
Warning: no message line prompt for ID 0x800F.
Warning: no message line prompt for ID 0x8010.
Warning: no message line prompt for ID 0x8011.
Warning: no message line prompt for ID 0x801B.
Warning: no message line prompt for ID 0x800C.
Warning: no message line prompt for ID 0x800D.
Warning: no message line prompt for ID 0x801F.
Warning: no message line prompt for ID 0x800E.
Warning: no message line prompt for ID 0x800F.
Warning: no message line prompt for ID 0x8010.
Warning: no message line prompt for ID 0x8011.
Warning: no message line prompt for ID 0x801B.
The thread 'Win64 Thread' (0x1588) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x9ac) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0xc48) has exited with code 0 (0x0).
I am OnDestroy!!!
The thread 'Win64 Thread' (0xc20) has exited with code 0 (0x0).
Detected memory leaks!
Dumping objects ->
f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\thrdcore.cpp(306) : {1318} client block at 0x00000000074DFC00, subtype c0, 136 bytes long.
a CWinThread object at $00000000074DFC00, 136 bytes long
Object dump complete.
The thread 'Win64 Thread' (0xea0) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x63c) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x13d4) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x15c8) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x1664) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x120c) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0xc78) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0xf6c) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x1408) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x11fc) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x1560) has exited with code 0 (0x0).
The thread 'MxsProxyGC' (0x1514) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x1074) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x10fc) has exited with code 0 (0x0).
The thread 'MxsProxyPump' (0x128c) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x166c) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x1558) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0xb34) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x131c) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0xb28) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x101c) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x1098) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x16ec) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x1744) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x16a0) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0xf0c) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x15dc) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x484) has exited with code 0 (0x0).
The thread 'Win64 Thread' (0x150c) has exited with code 0 (0x0).
The program '[3132] Real OCT and ODT.exe: Native' has exited with code 0 (0x0).
//MainFrm1.h start //////////////////////////////////////
class CMainFrame : public CFrameWnd
{
public:
CMainFrame();
protected:
DECLARE_DYNAMIC(CMainFrame)
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMainFrame)
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo);
//}}AFX_VIRTUAL
// Implementation
public:
void ReOpen();
virtual ~CMainFrame();
int MotionControl;
// CxImage *m_pMergingCanvas;
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected: // control bar embedded members
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
CReBar m_wndReBar;
CDialogBar m_wndDlgBar;
CChildView m_wndView;
// Generated message map functions
// protected:
public:
afx_msg void OnDaqOctplaying();
afx_msg BOOL MultiThreadMaking();
static UINT DRAWThread(LPVOID pParam);
static UINT DAQThread(LPVOID pParam);
static UINT DSPThread(LPVOID pParam);
// static UINT StopThread(LPVOID lpdwParam);
static void Wait(DWORD);
afx_msg void Messagebox_test(void);
afx_msg void OnFilepath();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
int PIStageID;
void OnBgBLACK(void);
bool m_MergingCheck;
afx_msg void OnEnChangeBrightness();
afx_msg void OnEnChangeContrast();
afx_msg void OnBnClickedFringebg();
};
//MainFrm1.h end////////////////////////////////////////////
//MainFrm1.cpp start //////////////////////////////////////
CWinThread *pDAQThread = NULL;
CWinThread *pDSPThread = NULL;
CWinThread *pDRAWThread = NULL;
//MultiThread variable
static bool volatile startFlag = false;
static bool volatile DAQThreadFlag, DSPThreadFlag, DRAWThreadFlag;
CEvent Event_DAQ, Event_DSP, Event_DRAW, Event_STOP;
static uInt16 *user_buff;
static Int8 *ImaqBuffers[NUM_RING_BUFFERS]; // acquisiton buffer
UINT CMainFrame::DAQThread(LPVOID pParam)
{
HANDLE hProcess; // Current process handle
hProcess = GetCurrentProcess();
//SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS);
SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS);
int i, j;
CMainFrame *pIMAQError = (CMainFrame *)pIMAQError;
CMainFrame *pDAQError = (CMainFrame *)pDAQError;
int32 DAQerror=0;
int IMAQerror=0;
char errBuff[2048]={'\0'};
int bufferIndex = 0;
uInt32 currBufNum;
void* bufAddr = NULL;
while(startFlag == true)
{
while(DAQThreadFlag == true)
{
Event_DAQ.Lock();
errChk(imgSessionExamineBuffer2 (Sid, bufferIndex, &currBufNum, &bufAddr));
user_buff = (uInt16 *) bufAddr;
// reinsert the buffer back in the ring
errChk(imgSessionReleaseBuffer (Sid));
// Now get next buffer
bufferIndex ++;
Event_DSP.PulseEvent();
}
DSPThreadFlag = false;
DRAWThreadFlag = false;
}
TRACE("DAQ Thread End\n");
return 0;
}
UINT CMainFrame::DSPThread(LPVOID pParam)
{
BOOL err;
float temp1, temp2;
unsigned short bmpCount=0;
while(startFlag==true)
{
while(DSPThreadFlag==true)
{
Event_DSP.Lock();
if(iProgStatus == 8) //OCT displaying
{
int i, j, k;
Ipp32f *data1, *data3Re, *data3Im, *spectrumRe, *spectrumIm, *magnitude;
#pragma omp parallel private(i, j, k, data1, temp1, temp2, spectrumRe, spectrumIm, data3Re, data3Im, magnitude)//, pOCTAscanSpec)
{
data1 = (float*)calloc(iCount, sizeof(float));
spectrumRe = (float*)calloc(iCount, sizeof(float));
spectrumIm = (float*)calloc(iCount, sizeof(float));
data3Re = (float*)calloc(iCount, sizeof(float));
data3Im = (float*)calloc(iCount, sizeof(float));
magnitude = (float*)calloc(HeightPixel, sizeof(float));
#pragma omp for
for(i=0;i<XRange;i++)
{
for (j=0; j<iCount-1; j++) //interpolation
{
temp1= *(user_buff + index[j]+i*iCount)-m_ibackground[index[j]];
temp2= *(user_buff + index[j]+1+i*iCount)-m_ibackground[index[j]+1];
data3Re[j]=temp1+(temp2-temp1)*k2LambdaRate[j];//(klambda[k]-lambda[index[k]])/(lambda[index[k]+1]-lambda[index[k]]);//*cosfDispersion[j];
data3Im[j]=0;
}
data3Re[iCount-1] = *(user_buff + iCount-1+i*iCount)-m_ibackground[iCount-1];
data3Im[iCount-1] = 0.0;
ippsMul_32f_I(win, data3Re, iCount);
ippsFFTFwd_CToC_32f(data3Re, data3Im, spectrumRe, spectrumIm, FFTspecification, NULL);
ippsMagnitude_32f(spectrumRe, spectrumIm, magnitude, HeightPixel);//500*700 pixel
ippsSub_32f(pOCTBackGround[i], magnitude, magnitude, HeightPixel);
ippsFlip_32f_I(magnitude, HeightPixel);
ippsThreshold_LTVal_32f_I(magnitude, HeightPixel, 1.0e-10, 0.0);
ippsLog10_32f_A11(magnitude, magnitude, HeightPixel);
ippsMulC_32f_I(10.0, magnitude, HeightPixel);
ippsAddC_32f_I(dBrightness, magnitude, HeightPixel);//dBrightness
ippsMulC_32f_I(dContrast, magnitude, HeightPixel);//dContrast
ippsThreshold_LTValGTVal_32f_I(magnitude, HeightPixel, 0.0, 0.0, 255.0, 255.0);
ippsCopy_32f(magnitude, pOCTAscanSpec[i], HeightPixel);
}
free(data1);
free(spectrumRe);
free(spectrumIm);
free(data3Re);
free(data3Im);
free(magnitude);
}
ippsJoin_32f16s_D2L((const Ipp32f**) pOCTAscanSpec, XRange, HeightPixel, ipp_1d_16s);
ippsConvert_16s32f_Sfs(ipp_1d_16s, ipp_1d_32f, XRange*HeightPixel, 0);
ippsConvert_32f8u_Sfs(ipp_1d_32f, lpBmpOCT, XRange*HeightPixel, ippRndZero, 0);
}
Event_DRAW.PulseEvent();
/ }
DRAWThreadFlag = false;
DAQThreadFlag = false;
}
TRACE("DSP Thread End\n");
return 0;
}
UINT CMainFrame::DRAWThread(LPVOID pParam) // ADC performance for OCT displaying
{
// CChildView *pView = (CChildView *)pParam;
int DrawTime = 1;
int IMAQerror=0;
while(startFlag == true)
{
while(DRAWThreadFlag == true)
{
Event_DRAW.Lock();
QueryPerformanceFrequency( &DRAWFrequency );
QueryPerformanceCounter( &DRAWBeginTime );
// pView->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
QueryPerformanceCounter( &DRAWEndtime );
DRAWelapsed = DRAWEndtime.QuadPart- DRAWBeginTime.QuadPart;
timeDRAWResult = (double)DRAWelapsed / (double)DRAWFrequency.QuadPart*1000.0;
Event_DAQ.PulseEvent();
}
DSPThreadFlag = false;
DAQThreadFlag = false;
}
TRACE("DRAW Thread End\n");
return 0;
}
BOOL CMainFrame::MultiThreadMaking()
{
pDAQThread = AfxBeginThread(DAQThread, (LPVOID)this);
pDSPThread = AfxBeginThread(DSPThread,(LPVOID)this);
pDRAWThread = AfxBeginThread(DRAWThread,(LPVOID)this);
Event_DAQ.PulseEvent();
return 0;
}
void CMainFrame::OnDaqOctplaying() //iProgStatus==8
{
if(iProgStatus == 1)
{
m_wndDlgBar.SetDlgItemText(IDC_PLAY, "STOP"); // Set caption of button to "STOP"
iProgStatus = 8;
iBufferStatus = 0;
// Allocate memory for bitmap
if(hDIB1 != NULL) hDIB1 = GlobalFree(hDIB1);
hDIB1 = GlobalAlloc(GMEM_MOVEABLE, sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD) + XRange*HeightPixel);//(int)((iCount)/2.0));
lpBmpOCT = (BYTE *)GlobalLock(hDIB1);
////////////////////////////////////////////////////////////////////////////////////////
// Write BITMAPINFOHEADER
lpDIBInfoOCT = (LPBITMAPINFOHEADER)lpBmpOCT;
lpDIBInfoOCT->biSize = sizeof(BITMAPINFOHEADER);
lpDIBInfoOCT->biWidth = XRange;
lpDIBInfoOCT->biHeight = HeightPixel;//(int)((iCount)/2.0);//(int)((iCount - 1)/3.0); // Why divide it by 5
lpDIBInfoOCT->biPlanes = 1;
lpDIBInfoOCT->biBitCount = 8;
lpDIBInfoOCT->biCompression = BI_RGB;
lpDIBInfoOCT->biSizeImage = XRange*lpDIBInfoOCT->biHeight;
lpDIBInfoOCT->biXPelsPerMeter = 0;
lpDIBInfoOCT->biYPelsPerMeter = 0;
lpDIBInfoOCT->biClrUsed = 256;
lpDIBInfoOCT->biClrImportant = 0;
// Write color table
// Write Color table
RGBQUAD *bmiColors;
bmiColors = (RGBQUAD *) (lpBmpOCT + sizeof(BITMAPINFOHEADER));
unsigned int i;
for(i=0;i<256;i++,bmiColors++)
{
bmiColors->rgbBlue = i;
bmiColors->rgbGreen = i;
bmiColors->rgbRed = i;
bmiColors->rgbReserved = 0;
}
lpBmpOCT = lpBmpOCT + sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD);
//***** Create Task for Frame grabber & DAQ board (3/7/2008)
errChk(imgInterfaceOpen("img0", &Iid));
errChk(imgSessionOpen(Iid, &Sid));
//**** Set the ROI to obtain spectrum
AcqWinWidth = iCount;
AcqWinHeight = XRange;
// Set the ROI to the size of the Canvas so that it will fit nicely
errChk(imgSetAttribute2 (Sid, IMG_ATTR_ROI_WIDTH, AcqWinWidth));
errChk(imgSetAttribute2 (Sid, IMG_ATTR_ROI_HEIGHT, AcqWinHeight));
errChk(imgSetAttribute2 (Sid, IMG_ATTR_ROWPIXELS, AcqWinWidth));
// create a buffer list with one element
errChk(imgCreateBufList(NUM_RING_BUFFERS, &Bid));
// compute the size of the required buffer
errChk(imgGetAttribute (Sid, IMG_ATTR_BYTESPERPIXEL, &bytesPerPixel));
bufSize = AcqWinWidth * AcqWinHeight * bytesPerPixel;
for (int i = 0; i < NUM_RING_BUFFERS; i++)
{
errChk(imgCreateBuffer(Sid, FALSE, bufSize, (void**)&ImaqBuffers[i]));
errChk(imgSetBufferElement2(Bid, i, IMG_BUFF_ADDRESS, ImaqBuffers[i]));
errChk(imgSetBufferElement2(Bid, i, IMG_BUFF_SIZE, bufSize));
bufCmd = (i == (NUM_RING_BUFFERS - 1)) ? IMG_CMD_LOOP : IMG_CMD_NEXT;
errChk(imgSetBufferElement2(Bid, i, IMG_BUFF_COMMAND, bufCmd));
}
// lock down the buffers contained in the buffer list
errChk(imgMemLock(Bid));
// Tell the acquisition to use a line trigger, which should be derived from the IMG_SIGNAL_EXTERNAL.
errChk(imgSessionLineTrigSource2(Sid, IMG_SIGNAL_EXTERNAL, 0, IMG_TRIG_POLAR_ACTIVEH, 0));
// configure the session to use this buffer list
errChk(imgSessionConfigure(Sid, Bid));
// start the acquisition, asynchronous
errChk(imgSessionAcquire(Sid, TRUE, NULL));
if(startFlag == true)
return;
startFlag = true;
DAQThreadFlag = true;
DSPThreadFlag = true;
DRAWThreadFlag=true;
MultiThreadMaking(); //Make three threads
return;
}
if(iProgStatus == 8)
{
// Stop the threads
startFlag = false;
DAQThreadFlag = false;
DWORD dwRetCode;
DWORD dwExitCode = 0;
dwRetCode = ::WaitForSingleObject(pDAQThread->m_hThread, 3000);
if(dwRetCode == WAIT_OBJECT_0)
{
TRACE("pDAQThread is terminated normally\n");
}
else if(dwRetCode == WAIT_TIMEOUT)
{
::TerminateThread(pDAQThread->m_hThread, 0 );
TRACE("pDAQThread is terminated FORCELY!!!\n");
}
if(pDAQThread)
{
GetExitCodeThread(pDAQThread->m_hThread, &dwExitCode);
TRACE("pDAQThread is terminated(code=%d)\n", dwExitCode);
// delete pThread; // m_bAutoDelete = FALSE의 경우 스레드의 삭제를 잊지말자.
}
dwRetCode = ::WaitForSingleObject(pDSPThread->m_hThread, 3000);
if(dwRetCode == WAIT_OBJECT_0)
{
TRACE("pDSPThread is terminated normally\n");
}
else if(dwRetCode == WAIT_TIMEOUT)
{
::TerminateThread(pDSPThread->m_hThread, 0 );
TRACE("pDSPThread is terminated FORCELY!!!\n");
}
if(pDSPThread)
{
GetExitCodeThread(pDSPThread->m_hThread, &dwExitCode);
TRACE("pDSPThread is terminated(code=%d)\n", dwExitCode);
// delete pThread; // m_bAutoDelete = FALSE의 경우 스레드의 삭제를 잊지말자.
}
dwRetCode = ::WaitForSingleObject(pDRAWThread->m_hThread, 3000);
if(dwRetCode == WAIT_OBJECT_0)
{
TRACE("pDRAWThread is terminated normally\n");
}
else if(dwRetCode == WAIT_TIMEOUT)
{
::TerminateThread(pDRAWThread->m_hThread, 0 );
TRACE("pDRAWThread is terminated FORCELY!!!\n");
}
if(pDRAWThread)
{
GetExitCodeThread(pDRAWThread->m_hThread, &dwExitCode);
TRACE("pDRAWThread is terminated(code=%d)\n", dwExitCode);
// delete pThread; // m_bAutoDelete = FALSE의 경우 스레드의 삭제를 잊지말자.
}
// stop the acquisition
imgSessionAbort(Sid, NULL);
// unlock the buffers in the buffer list
if (Bid != 0)
imgMemUnlock(Bid);
// dispose of the buffers
for (int i = 0; i < NUM_RING_BUFFERS; i++)
if (ImaqBuffers[i] != NULL)
imgDisposeBuffer(ImaqBuffers[i]);
// close this buffer list
if (Bid != 0)
imgDisposeBufList(Bid, FALSE);
// Close the interface and the session
if(Sid != 0)
imgClose (Sid, TRUE);
if(Iid != 0)
imgClose (Iid, TRUE);
// Reset the session variables.
Sid = Bid = Iid = 0;
iProgStatus = 1;
iBufferStatus = 2;
m_wndDlgBar.SetDlgItemText(IDC_PLAY, "PLAY"); // Set caption of button to "PLAY"
return;
}
return;
}
//MainFrm1.cpp end
//ChildView1.cpp start
CChildView::CChildView()
{
hDrawView = DrawDibOpen();
hDrawView1 =DrawDibOpen();
hDrawView2 =DrawDibOpen();
}
CChildView::~CChildView()
{
DrawDibClose(hDrawView);
DrawDibClose(hDrawView1);
DrawDibClose(hDrawView2);
}
void CChildView::OnPaint()
{
CPaintDC dc(this); // device context for painting
// CMemDC pDC(dc);
int iXOffset1=10, iYOffset1=10, iXOffset2=10, iYOffset2=10;
// TODO: Add your message handler code here
BYTE *lpBmp;
LPBITMAPINFOHEADER lpDIBInfo;
int iXOffset=10, iYOffset=10;
int iXWidth, iYHeight;
double fPropotion;
CRect cWndRect;
GetWindowRect(cWndRect);
POINT axis;
axis.x = cWndRect.Width();
axis.y = cWndRect.Height();
if(iProgStatus == 8) { //OCT playing
iXOffset = (int) (0.1*cWndRect.Width());
fPropotion = 0.5*cWndRect.Width()/(lpDIBInfoOCT->biWidth);//0.7
iXWidth = (int) (fPropotion*(lpDIBInfoOCT->biWidth));
iYHeight = (int) 0.3*(lpDIBInfoOCT->biHeight);
fPropotion=1;
if (0.8*axis.y*fPropotion < axis.x*0.8)
{
iYHeight = (int) (axis.y*0.8);
iXWidth = (int) (fPropotion*iYHeight);
}
else
{
iXWidth = (int) (axis.x*0.8);
iYHeight = (int) (iXWidth/fPropotion);
}
iXWidth = (int)(iXWidth/4.0*4);
iYHeight=(int)(iYHeight/4.0*4);
DrawDibDraw(hDrawView, dc.m_hDC, iXOffset, iYOffset, iXWidth,
iYHeight, lpDIBInfoOCT, lpBmpOCT, 0, 0,
lpDIBInfoOCT->biWidth, lpDIBInfoOCT->biHeight, DDF_ANIMATE);
dc.TextOut(1024,200,performance1);
dc.TextOut(1024,250,performance2);
dc.TextOut(1024,300,performance3);
}
// Do not call CWnd::OnPaint() for painting messages
}
//ChildView1.cpp end //////////////////////////////////////