OnPaint is updated too often - c++

I have a problem with the OnPaint method of CFrameWnd, and I cant seem to figure out what is happening. OnPaint is called approx every 10 ms, which causes the computer to freeze. Checked CPU usage and this app takes up 50%!
The application is a very simple MFC app, which is written in one file.
// Includes are done here...
class MFC_Tutorial_Window : public CFrameWnd
{
std::string data;
public:
MFC_Tutorial_Window()
{
this->data = "";
Create(NULL, "Data Win"); // Create window
}
void OnPaint()
{
CDC* pDC = GetDC();
CString s = CString(this->data.c_str());
RECT rc;
HWND hwnd = this->m_hWnd;
if(hwnd != NULL) {
::GetWindowRect(hwnd, &rc);
rc.top = rc.bottom/2;
if(pDC != NULL && pDC->m_hDC != NULL) {
pDC->DrawText(s, &rc, DT_CENTER);
}
}
}
void UpdateWithNewData(std::string up) {
this->data = up;
Invalidate();
}
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MFC_Tutorial_Window, CFrameWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
// App class
class MyApp :public CWinApp
{
MFC_Tutorial_Window *wnd;
BOOL InitInstance()
{
wnd = new MFC_Tutorial_Window();
m_pMainWnd = wnd;
m_pMainWnd->ShowWindow(3);
wnd->UpdateWithNewData("Hello world!");
return 1;
}
};
Does anyone know why OnPaint is spammed by the system? Have been staring at this code for ages and I just can't find it.

The CPaintDC destructor has to be called for the repainting flag to be reset. You need to call beginPaint(); and endPaint(); on your CDC which should actually be changed to a CPaintDC. More importantly, not calling endPaint(); will cause the context to be repainted no matter what.

A WM_PAINT message is generated whenever there are no other messages in the message queue and the window's update region (see InvalidateRect) is non-empty. When handling a WM_PAINT message an application signals that the update region has been repainted by calling EndPaint. Failing to call EndPaint will not mark the update region as handled, so the next time an application asks for a message, WM_PAINT is a valid candidate.
In MFC the functionality to call BeginPaint and EndPaint is encapsulated in the CPaintDC Class. The standard MFC message handler for WM_PAINT looks like this:
void OnPaint()
{
CPaintDC dc(this); // calls BeginPaint()
// Perform rendering operations on dc
// ...
} // CPaintDC::~CPaintDC() calls EndPaint()
More detailed information on using device contexts can be found at Device Contexts.

Related

MFC Debug Assertation Failed!! wincore.cpp Line 972

I have created an MFC Dialog box in a DLL for use in multiple projects and it has functionalities such as:
Getting Listbox data from the main application using the DLL. I can push string data through the main application to the MFC Dialog box but I am getting Assertation errors after compilation.
This process happens in a thread where one thread keeps the Dialog box active and another pushes data as shown in the code below.
void dbox(CDialogClass *dlg)
{
dlg->ShowDlg();
}
void input(CDialogClass *dlg)
{
string str1= "";
while (1)
{
getline(cin, str1);
//cin >> str1;
dlg->SetData(str1);
}
}
int main()
{
HMODULE hModule = ::GetModuleHandle(NULL);
if (hModule != NULL)
{
// initialize MFC and print and error on failure
if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
{
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
}
else
{
CDialogClass *dlg = new CDialogClass("Title Dbox",300.0f, 300.0f, 0);
thread t1(input, dlg);
thread t2(dbox, dlg);
t1.join();
t2.join();
}
}
return 0;
}
Here dbox() invokes a ShowDlg function which is in an MFC DLL as below:
void CDialogClass::ShowDlg()
{
dlgg->title = title;
dlgg->dialogWidth = D_width;
dlgg->dialogHeight = D_height;
dlgg->pos = pos;
dlgg->Create(IDD_DIALOG1);
dlgg->ShowWindow(SW_SHOWNORMAL);
dlgg->RunModalLoop();
//dlgg->DoModal();
}
SetData() is called by thread input() and it has the below code in the DLL:
void CDialogClass::SetData(string data)
{
p_text = data;
dlgg->calldata(data);
}
Below is the code for my Dialog class in the DLL just for reference if needed-
#include "stdafx.h"
#include "DlgDisp.h"
#include "afxdialogex.h"
#include "Resource.h"
#include <fstream>
#include <Thread>
IMPLEMENT_DYNAMIC(CDlgDisp, CDialogEx)
CDlgDisp::CDlgDisp(CWnd* pParent /*=NULL*/)
: CDialogEx(CDlgDisp::IDD, pParent)
{
}
CDlgDisp::~CDlgDisp()
{
}
void CDlgDisp::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST1, m_listbox);
}
BOOL CDlgDisp::OnInitDialog()
{
//Title manipulations
char *str_title;
str_title = &title[0];
SetWindowText((CAtlString)str_title);
//Size manipulations
CWnd* pctrl = GetDlgItem(IDC_LIST1);
CRect rectctrl;
SetWindowPos(NULL, 0, 0, dialogWidth, dialogHeight, SWP_NOMOVE | SWP_NOZORDER);
pctrl->GetWindowRect(rectctrl);
pctrl->SetWindowPos(NULL, 20, 20, dialogWidth-120, dialogHeight-80, SWP_NOMOVE | SWP_NOZORDER);
UpdateData(FALSE);
return TRUE; // return TRUE unless you set the focus to a control
}
BEGIN_MESSAGE_MAP(CDlgDisp, CDialogEx)
END_MESSAGE_MAP()
void CDlgDisp::calldata(string strdata)
{
char *str_parameter;
str_parameter = &strdata[0];
CString param = _T("");
param.Format(_T("%s"), (CAtlString)str_parameter);
if (pos == 0)
{
m_listbox.InsertString(0, param);
}
else
m_listbox.AddString(param);
UpdateData(FALSE);
}
the flow of the program for references:
CDlgDisp class is the Dialog class derived from CDialogEx class.
CDialogClass is for interaction with external applications which is derived from CDialog class.
CDialogClass has a public member variable of CDlgDisp class.
external application -> object.CdialogClass -> object.CDlgdisp class
when I execute the program it runs well, and I get an error when I try to input data through the console. It does get printed in the Listbox dynamically but then it shows the Assertation Error.
Here is an image after execution
[enter image description here][1]
and here is the image after I enter the data in console and press enter
[enter image description here][2]
what do you guys think the problem is?
[1]: https://i.stack.imgur.com/pXFMD.png
[2]: https://i.stack.imgur.com/eUXZ7.png
Look into the source where the ASSERT ion take place
You find this comment:
// Note: if either of the above asserts fire and you are
// writing a multithreaded application, it is likely that
// you have passed a C++ object from one thread to another
// and have used that object in a way that was not intended.
// (only simple inline wrapper functions should be used)
//
// In general, CWnd objects should be passed by HWND from
// one thread to another. The receiving thread can wrap
// the HWND with a CWnd object by using CWnd::FromHandle.
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.
MFC window objects use handle maps per thread. This usually doesn't allow you to use the objects for some functions from other threads. It is possible but this is discussed in many other threads.
The way you want to use the MFC isn't possible. Think about synchronisation. If the functions are thread safe that you want to use with the other window you may use SendMessage and the m_hWnd handle directly.
Thank you guys for being first responders to my problem. All the comments were useful and helped me in understanding the problem.
Problem: Since MFC is thread-safe therefore using an object to SetData was creating a memory sharing conflict between both the threads.
Solution: I used a custom message to pass information to be displayed dynamically. Below Links helped completely-
https://blog.csdn.net/xust999/article/details/6267216
On sending end:
::PostMessage(HWND_BROADCAST, WM_UPDATE_DATA, 0, 0);
On receiving end in the header file:
const UINT WM_UPDATE_DATA = ::RegisterWindowMessage(_T("UpdateData"));
Also, in the header file in the Dialog class:
afx_msg LRESULT OnUpdateData(WPARAM wParam, LPARAM lParam);
The above function will be called when the message is posted and all functionalities to be added to it such as-
LRESULT CDlgDisp::OnUpdateData(WPARAM wParam, LPARAM lParam)
{
char *str_parameter;
str_parameter = &parameter[0];
CString param = _T("");
param.Format(_T("%s"), (CAtlString)str_parameter);
if (pos == 0)
{
m_listbox.InsertString(0, param);
}
else
m_listbox.AddString(param);
return 1;
}
Thank you, Everyone.

Forcing a redraw of Direct2D

I have a combo-box in MFC C++ application that I use for drawing. Now I am experimenting with Direct2D for drawing. However, I am not able to re-draw content after the initial drawing. Using the code below, my "OnAfxDraw2D" method is being called when the application starts and the Direct2D content appears as intended.
Later, when I want to return my combo box to the initial state, I call the DoD2D() method. If the method is implemented using the first option, it works as intended, but it sometimes produce an assertion error in Visual Studio (Winhand.cpp Line 208). If I use the second option of DoD2D() method - nothing happens (the graphical content is not drawn).
Am I missing something here?
RenderArea::RenderArea() : CButton()
{
BOOL bUseDCRenderTarget = TRUE;
EnableD2DSupport(TRUE, bUseDCRenderTarget);
}
void RenderArea::DoDataExchange(CDataExchange* pDX)
{
CButton::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(RenderArea, CButton)
ON_REGISTERED_MESSAGE(AFX_WM_DRAW2D, &RenderArea::OnAfxDraw2D)
END_MESSAGE_MAP()
void RenderArea::DoD2D()
{
if (!IsD2DSupportEnabled())
return;
// Option 1) Works, but fails an assertion in debug mode
Invalidate();
return;
// Option 2) Does not work.
CDCRenderTarget *pRenderTarget = GetDCRenderTarget();
pRenderTarget->BeginDraw();
DrawD2DResource(pRenderTarget);
pRenderTarget->EndDraw();
}
void RenderArea::DrawD2DResource(CDCRenderTarget* pDCRenderTarget)
{
ASSERT_VALID(pDCRenderTarget);
D2D1_COLOR_F crBack =
CRenderTarget::COLORREF_TO_D2DCOLOR(::GetSysColor(COLOR_WINDOW));
pDCRenderTarget->Clear(crBack);
CD2DSizeF sizeTrans(0.f, 105.f);
pDCRenderTarget->SetTransform(D2D1::Matrix3x2F::Translation(sizeTrans));
CD2DEllipse ellipse(CD2DRectF(10.f, 10.f, 200.f, 100.f));
CD2DSolidColorBrush brush(pDCRenderTarget, ::GetSysColor(COLOR_WINDOWTEXT));
pDCRenderTarget->DrawEllipse(ellipse, &brush);
CD2DPointF pointFrom(10.f, 10.f), pointTo(200.f, 100.f);
pDCRenderTarget->DrawLine(pointFrom, pointTo, &brush);
pDCRenderTarget->SetTransform(D2D1::IdentityMatrix());
return;
}
LRESULT RenderArea::OnAfxDraw2D(WPARAM wParam, LPARAM lParam)
{
CDCRenderTarget* pDCRenderTarget = (CDCRenderTarget*)lParam;
ASSERT_KINDOF(CDCRenderTarget, pDCRenderTarget);
DrawD2DResource(pDCRenderTarget);
HRESULT hr = pDCRenderTarget->EndDraw();
return static_cast<LRESULT>(TRUE);
}

Open a MFC dialog in a std::thread

I would like to show a dialog to inform the user that the application is busy. To avoid blocking of the main thread, I was thinking to use a std::thread to show the dialog. Consider the following code:
InProcDlg inProcess;
std::thread t([ &inProcess ] {
inProcess.DoModal();
delete inProcess;
});
// wait till process has finished
::PostMessage(inProcess.m_hWnd, WM_USER + 1, 0, 0);
if (t.joinable()){
t.join();
}
InProcDlg.cpp
BEGIN_MESSAGE_MAP(InProcDlg, CDialogEx)
...
ON_MESSAGE(WM_USER + 1, &InProcDlg::close)
END_MESSAGE_MAP()
LRESULT InProcDlg::close(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam, lParam);
EndDialog(1);
return 0;
}
Running this code the dialog is shown properly. The dialog is also closed, but the main dialog is not shown, the application hangs in CreateRunDlgIndirect(). Trying to step in, while setting some breakpoints the main dialog is shown properly back again. Very strange. I would be very happy for any advices where I have to dive deeper in.
In the next step I would also like to show the process to the user, by sending an integer indicating the current state of process.
int *percent;
::PostMessage(inProcess.m_hWnd, WM_USER + 2, 0, reinterpret_cast<LPARAM>(percent));
How I can gain evidence that the dialog is already existing, before sending or posting a message?
I'm using Visual Studio 2013.
I can think of two ways to do that:
Modeless dialog
https://www.codeproject.com/Articles/1651/Tutorial-Modeless-Dialogs-with-MFC
User thread (UI thread)
Creating a brother to the main UI thread (CWinApp) by using CWinThread. Most important is to assign CWinThread::m_pMainWnd member, with a pointer to a Dialog. If the dialog is Modal you return FALSE right after the call to DoModal, and return TRUE for Modeless.
class CMainFrame : public CFrameWnd {
// pointer to thread
CWinThread* m_pUserThread;
}
start thread
m_pUserThread = AfxBeginThread(RUNTIME_CLASS(CUserThread), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED );
m_pUserThread->m_bAutoDelete = TRUE;
m_pUserThread->ResumeThread();
headr file**
class CUserThread : public CWinThread
{
DECLARE_DYNCREATE(CUserThread)
public:
CUserThread();
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CUserThread)
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
//}}AFX_VIRTUAL
protected:
virtual ~CUserThread();
// Generated message map functions
//{{AFX_MSG(CUserThread)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
CUserMsg* m_pDlg;
}
source file
#define DO_MODAL
BOOL CUserThread::InitInstance()
{
#ifdef DO_MODAL
// create and assign Modeless dialog
CUserMsg dlg;
m_pMainWnd = &m_pDlg;
dlg.DoModal();
return FALSE;
#endif
#ifdnef DO_MODAL
// create and assign Modal dialog
m_pDlg = new CUserMsg();
m_pDlg->Create( IDD_USER_DLG, AfxGetMainWnd() );
m_pMainWnd = m_pDlg;
return TRUE;
#endif
}
int CUserThread::ExitInstance()
{
// check for null
m_pDlg->SendMessage( WM_CLOSE ) ;
delete m_pDlg;
return CWinThread::ExitInstance();
}
to terminate the thread
// post quit message to thread
m_pUserThread->PostThreadMessage(WM_QUIT,0,0);
// wait until thread termineates
::WaitForSingleObject(m_pUserThread->m_hThread,INFINITE);
For both ways I would highly recommend making the dialog as a top most window:
BOOL CLicenseGenDlg::OnInitDialog() {
CDialog::OnInitDialog();
// TODO: Add extra initialization here
SetWindowPos( &wndTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | WS_EX_TOPMOST );
return TRUE;
}

Problems with sending and getting messages from one class to another class. MFC program

Here is my code:
This function is called when i click the left button of the mouse and send te message:
#define WM_MYMESSAGE WM_USER+7
void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
counter=0;
CWnd::OnLButtonDown(nFlags, point);
CRect wdRect;
GetClientRect(&wdRect);
HWND hwnd;
hwnd=::FindWindow(NULL,"Client");
if(wdRect.PtInRect(point))
{
counter++;
PostMessage(WM_MYMESSAGE,point.x,point.y);
}
}
in another file Mainfraim.cpp with the help of ON_MESSAGE(WM_MYMESSAGE, OnNameMsg) i send message to ONNameMsg function.This function opens the bmp file. The problem is that the function OnNameMsg does not respond to the message and this function does not work. What should i do to make this function respond on this message. Can you help me with this problem?? Here is the code.
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_WM_SETFOCUS()
ON_MESSAGE(WM_MYMESSAGE, OnNameMsg)
ON_COMMAND(ID_EDIT_LINE, OnEditLine)
END_MESSAGE_MAP()
afx_msg LRESULT CMainFrame::OnNameMsg(WPARAM wParam,LPARAM IParam)
{
MSG msg;
char FileName[500];
char FileTitle[100];
FileName[0]='\0';
GetMessage(&msg,NULL,WM_MOUSEFIRST,0);
CFileDialog file(TRUE);
file.m_ofn.lpstrFilter=TEXT("Bitmap picture files *.bmp\0*.bmp\0All Files *.*\0*.*\0\0");
file.m_ofn.lpstrFileTitle=FileTitle;
file.m_ofn.lpstrFile=FileName;
file.m_ofn.lpstrTitle="Open BMP File";
file.DoModal();
//if (FileName[0]=='\0')return;
SetWindowText(FileTitle);
HANDLE hdibCurrent1 = OpenDIB(FileName);
hbm=0;
hbm=BitmapFromDib(hdibCurrent1,0);
GetObject(hbm,sizeof(BITMAP),(LPSTR)&bm);
CRect wdRect;
GetClientRect(&wdRect);
ClientToScreen(&wdRect);
int j=wdRect.Height();
int i=wdRect.Width();
//SetWindowPos(NULL,wdRect.left,wdRect.top, i,j,NULL);
if(hbm) { CClientDC dc(this);
HDC hdc=::GetDC(m_hWnd);
HDC hdcBits=::CreateCompatibleDC(hdc);
SelectObject(hdcBits,hbm);
//CRect wdRect;
GetClientRect(&wdRect);
CBrush brush;
brush.CreateSolidBrush(RGB(0,0,0));
dc.FillRect(&wdRect,&brush);
BitBlt(hdc, 0, 0, bm.bmWidth,bm.bmHeight,hdcBits,0,0, SRCCOPY);
DeleteDC(hdcBits);
::ReleaseDC(m_hWnd,hdc);
}
return 1;
}
You are sending the message to CChildView, and not to the CMainFrame. Since you want to send message to main frame, and handling it there, you must send (PostMessage) to the window handle of main frame.
The PostMessage you are calling is method is from CWnd, which is not same as ::PostMessage API, and hence it is sending to this. You need a pointer of CMainFrame, and call through that pointer. Let's assume you get main frame's pointer into pMainFrame, and then you can call:
pMainFrame->PostMessage(WM_MYMESSAGE,point.x,point.y);

Receiving WM_MOUSEMOVE from child controls on MFC CDialog

I have my dialog derived from CDialog and I want to close it once user moves mouse cursor away from it. To do so, I've added OnMouseLeave handler which calls OnCancel(). As I understand, for WM_MOUSELEAVE events to be sent in time, TrackMouseEvent must be called inside OnMouseMove routine. So the whole code is as following:
void CDlgMain::OnMouseLeave()
{
CDialog::OnMouseLeave();
// Close dialog when cursor is going out of it
OnCancel();
}
void CDlgMain::OnMouseMove(UINT nFlags, CPoint point)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE;
tme.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&tme);
CDialog::OnMouseMove(nFlags, point);
}
It works fine, but the dialog is closed when user hovers some of its child controls (like buttons he wants to click on :) ). It is because child controls do not send WM_MOUSEMOVE to the parent dialog.
The only function I found to "propagate" WM_MOUSEMOVE messages from child controls is SetCapture(). And it does the job, but 1) user cannot click any button after that and 2) mouse icon changes to hourglasses. So this is not an option.
Any suggestions?
Update I placed TrackMouseEvent call to the PreTranslateMessage routine which is called correctly on any mouse move events (even hovering the child controls). The strange thing is WM_MOUSELEAVE is still generated when user hovers child control! Seems like TrackMouseEvent knows what control is hovered now. Any ideas how to fix this?
I would try CDialog::PreTranslateMessage() if this is a modal dialog. If you still cannot detect mouse movements inside the children, the only option left is SetWindowsHookEx + WH_MOUSE.
When 2 dialog get event, then rise child dialog's WM_MOUSELEAVE event by force.
see the code, below
void CDlgParent::OnMouseMove(UINT nFlags, CPoint point)
{
CWnd* cwnd = this->GetDlgItem(IDC_CHILDRENNAME);
::SendMessage(cwnd->m_hWnd, WM_MouseLeave());
CDialog::OnMouseMove(nFlags, point);
}
void CDlgMain::OnMouseMove(UINT nFlags, CPoint point)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE;
tme.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&tme);
::SetFocus(this->mhWnd);
CDialog::OnMouseMove(nFlags, point);
}
How do you think?
I think I understand the problem now. It's indeed a bit tricky. I think you need a timer to guarantee that the subsequent WM_MOUSEMOVE message is handled (you have to test this).
BOOL CTestDgDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_MOUSEMOVE)
{
TCHAR buffer[255];
::GetWindowText(pMsg->hwnd, buffer, 255);
TRACE(_T("WM_MOUSEMOVE: %s\n"), buffer);
}
return CDialogEx::PreTranslateInput(pMsg);
}
Handle WM_MOUSELEAVE, wait for WM_MOUSEMOVE. Did it arrive? No -> dismiss dialog. Yes -> restart.
Thanks for all your help, guys. I wasn't able to make TrackMouseEvent properly, so I end up implementing solution with timer. On each tick I check position of mouse cursor to be inside my dialog area and ensure it is still foreground. That works perfect for me, though it is a little hack.
void CALLBACK EXPORT TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
// This is a little hack, but suggested solution with TrackMouseEvent is quite
// unreliable to generate WM_MOUSELEAVE events
POINT pt;
RECT rect;
GetCursorPos(&pt);
GetWindowRect(hWnd, &rect);
HWND hFGW = GetForegroundWindow();
// Send leave message if cursor moves out of window rect or window
// stops being foreground
if (!PtInRect(&rect, pt) || hFGW != hWnd)
{
PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
}
}