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

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);

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.

C++ changing image on user input in WinAPI

I am having issues in getting my .bmp image displayed to change to another one on user input. The image can be successfully printed at the start (title.bmp), but is supposed to change when pressing 1 or 2 followed by enter (to introduction.bmp & start.bmp). I must be missing something!
Where this happens is around the bottom of the code from while (running == 1) { so skip down to there.
I am using loadImage("title.bmp"); to print my images (I change the filename appropriately of course), and cin >> menuSelection; to pause the program and wait until the user presses one or two followed by enter.
I've searched many, many pages on how to print and change images in WinAPI, and this is the closest I can get. If there is any other information I have missed please tell me and I will comment it. Thanks in advance for helping!
//These are the libraries (external files) to include at the start.
#include <cstdio>
#include <windows.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <string>
using namespace std;
//Defining the [global] variables that will be used throughout the program
int running = 1;
int menuSelection = 0;
int userInput;
int userInputDummy;
int intPointer;
//Starter variables used in creating a window and printing images. These are global.
HDC imageDC; // the DC to hold our image
HBITMAP imageBmp; // the actual bitmap which contains the image (will be put in the DC)
HBITMAP imageBmpOld; // the DC's old bitmap (for cleanup)
const int screenSize_X = 640;
const int screenSize_Y = 480;
//Functions! Sections of code to re-used in the program
// Function to load the image into our DC so we can draw it to the screen
void loadImage(const char* pathname)
{
imageDC = CreateCompatibleDC(NULL); // create an offscreen DC
imageBmp = (HBITMAP)LoadImageA( // load the bitmap from a file
NULL, // not loading from a module, so this is NULL
pathname, // the path we're loading from
IMAGE_BITMAP, // we are loading a bitmap
0, 0, // don't need to specify width/height
LR_DEFAULTSIZE | LR_LOADFROMFILE// use the default bitmap size (whatever the file is), and load it from a file
);
imageBmpOld = (HBITMAP)SelectObject(imageDC, imageBmp); // put the loaded image into our DC
}
// Function to clean up
void cleanUpImage()
{
SelectObject(imageDC, imageBmpOld); // put the old bmp back in our DC
DeleteObject(imageBmp); // delete the bmp we loaded
DeleteDC(imageDC); // delete the DC we created
}
// The function to draw our image to the display (the given DC is the screen DC)
void drawImage(HDC screen)
{
BitBlt(
screen, // tell it we want to draw to the screen
0, 0, // as position 0,0 (upper-left corner)
screenSize_X, // width of the rect to draw
screenSize_Y, // height of the rect
imageDC, // the DC to get the rect from (our image DC)
0, 0, // take it from position 0,0 in the image DC
SRCCOPY // tell it to do a pixel-by-pixel copy
);
}
// A callback to handle Windows messages as they happen
LRESULT CALLBACK wndProc(HWND wnd, UINT msg, WPARAM w, LPARAM l)
{
// what kind of message is this?
switch (msg)
{
// we are interested in WM_PAINT, as that is how we draw
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC screen = BeginPaint(wnd, &ps); // Get the screen DC
drawImage(screen); // draw our image to our screen DC
EndPaint(wnd, &ps); // clean up
}break;
// we are also interested in the WM_DESTROY message, as that lets us know when to close the window
case WM_DESTROY:
PostQuitMessage(0);
break;
}
// for everything else, let the default window message handler do its thing
return DefWindowProc(wnd, msg, w, l);
}
// A function to create the window and get it set up
HWND createWindow(HINSTANCE inst)
{
WNDCLASSEX wc = { 0 }; // create a WNDCLASSEX struct and zero it
wc.cbSize = sizeof(WNDCLASSEX); // tell windows the size of this struct
wc.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); // tell it to use the normal arrow cursor for this window
wc.hInstance = inst; // give it our program instance
wc.lpfnWndProc = wndProc; // tell it to use our wndProc function to handle messages
wc.lpszClassName = TEXT("DisplayImage"); // give this window class a name.
RegisterClassEx(&wc); // register our window class with Windows
// the style of the window we want... we want a normal window but do not want it resizable.
int style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU; // normal overlapped window with a caption and a system menu (the X to close)
// Figure out how big we need to make the window so that the CLIENT area (the part we will be drawing to) is
// the desired size
RECT rc = { 0,0,screenSize_X,screenSize_Y }; // desired rect
AdjustWindowRect(&rc, style, FALSE); // adjust the rect with the given style, FALSE because there is no menu
return CreateWindow( // create the window
TEXT("DisplayImage"), // the name of the window class to use for this window (the one we just registered)
TEXT("Display an Image"), // the text to appear on the title of the window
style | WS_VISIBLE, // the style of this window (OR it with WS_VISIBLE so it actually becomes visible immediately)
100, 100, // create it at position 100,100
rc.right - rc.left, // width of the window we want
rc.bottom - rc.top, // height of the window
NULL, NULL, // no parent window, no menu
inst, // our program instance
NULL); // no extra parameter
}
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// _________________________________________________________________________________________
// The actual entry point for the program!
// This is Windows' version of the 'main' function:
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmd, int show)
{
// load our image
loadImage("title.bmp");
// create our window
HWND wnd = createWindow(inst);
// Do the message pump! keep polling for messages (and respond to them)
// until the user closes the window.
MSG msg;
while (GetMessage(&msg, wnd, 0, 0)) // while we are getting non-WM_QUIT messages...
TranslateMessage(&msg); // translate them
DispatchMessage(&msg); // and dispatch them (our wndProc will process them)
{
while (running == 1) {
//Welcoming the user to the program, and asking them what they want to do (starts functions)
cin >> menuSelection;
//Selecting the introduction option
if (menuSelection == 1) {
loadImage("introduction.bmp");
cin >> userInputDummy;
menuSelection = 0;
}
//Selecting the start option
else if (menuSelection == 2) {
loadImage("start");
cin >> userInputDummy;
menuSelection = 0;
}
//Selecting the exit option
else if (menuSelection == 3) {
menuSelection = 0;
running = 0;
}
}
// once the user quits....
cleanUpImage();
return 0;
return EXIT_SUCCESS;
}
}
you cannot use cin in win32 use an editbox then get the user's input from it as character string then if you want convert it to an integer value otherwise use the API:
GetDlgItemInt(...);
you are also handling only GetMessage in while-loop while you only handle dispatchmessage outside the loop which means you handle it only once, when getmessage fails (the end of program) so the result is a freezing windows as long as there's no one who takes messages from getmessage to the target windo.
the solution: make DispatchMessage inside while loop:
another thing: you pass hwnd to getmessage the result destroying the window won't make the application exit.
take a look at GetMessage() when passing a non-null value:
link text
the correct thing in your case:
while (GetMessage(&msg, 0, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
* don't load image inside loop just load at the time wanted:
make an edit box with style ES_NUMBER and another button names for example change image so when clicked take the content of edit box convert it to integer, check whether it is 1 or 2 then load imaged depending the value.
you may ask "why I can't use iostream input and output streams in win32" because I ask you "where is console windows?" and if it is here what is the role of while-loop (blocking waiting for messages)?

MFC - UpdateData(False) + Thread + Debug assertion failed

Im using Visual Studio 2010, work with MFC 2008/2010.
I have a problem with THREAD and UPDATEDATA(FALSE)
This is init function
BOOL CBkav_btap2_appDlg::OnInitDialog(){
....
AfxBeginThread (MyThreadProc,(LPVOID)GetSafeHwnd());
return TRUE; // return TRUE unless you set the focus to a control
}
This is my THREAD
UINT __cdecl MyThreadProc( LPVOID pParam )
{
DWORD totalphys;
DWORD availablephys;
DWORD memoload;
CBT2Class* pObject = (CBT2Class*)pParam;
pObject->GetRAMandCPUInfo(totalphys,availablephys,memoload );
CBkav_btap2_appDlg dlgObject;
dlgObject.ec_totalphys = totalphys;
dlgObject.UpdateData(FALSE);<--- Can not update data
return 0;
}
CBT2Class is the class in dll file i created before.
ec_totalphys is just an edit_control.
When i run, it return "Debud Assertion failed". I dont know why. Please help me. Thankss.
p/s: I think i need use SendMessage to update data for Dialog but i search every where but still can't work.
You are passing an HWND as the thread parameter. It is not a pointer and you should not cast it to anything. You can use the HWND to post a custom message to the dialog. This custom message can include data in wParam and lParam. The message handler in the dialog runs in the main thread and can do the UpdateData call. See the example here for posting a custom message to the dialog: http://vcfaq.mvps.org/mfc/12.htm
OK. Thanks all for suggestions :D.
So my particular solution is :
B1. Defined a MESSAGE
#ifdef _DEBUG
#define new DEBUG_NEW
#define WM_MY_MESSAGE (WM_USER+1000) // Cho chay o 2 thoi diem khac nhau
#endif
B2. Signed in a MESSAGE MAP
BEGIN_MESSAGE_MAP(CBkav_btap2_appDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(WM_MY_MESSAGE, TestMessageCall)
ON_WM_TIMER()
END_MESSAGE_MAP()
B33. Create Thread
BOOL CBkav_btap2_appDlg::OnInitDialog()
{
..
// TODO: Add extra initialization here
AfxBeginThread(TestMethodThreadCall, (LPVOID)GetSafeHwnd());
return TRUE; // return TRUE unless you set the focus to a control
}
B4. Create Method
UINT __cdecl TestMethodThreadCall( LPVOID pParam )
{
while(1){
//Ten Chuong trinh dc su dung nhieu nhat
HWND hDlg = (HWND )pParam;
CString nameTestMessage = __T("Call From Message");
::SendMessage(hDlg, WM_MY_MESSAGE, (WPARAM)(&nameTestMessage), 0);
Sleep(5000);
}
return 0;
}
B5. Create Method Message call
LRESULT CBkav_btap2_appDlg::TestMessageCall(WPARAM wpD, LPARAM lpD)
{
CString *pwpD = (CString *)wpD;
ec_nameTestmessage = *pwpD;
UpdateData(FALSE);
return LRESULT();
}

OnPaint is updated too often

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.

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);
}
}