I am building a test application(testApp) for a legacy MFC based application (MFC-app). I am trying to simulate mouse clicks on the MFC-app using message-passing between them. I was able to do this successfully for bring up dialog boxes from the MFC-app menu. However when I am trying to simulate a mouse click on the View of the MFC -app it doesn't seem to work.
The main question I have is whether there are any known limitations in trying to use SendMessage,PostMessage functions to communicate to a derived class of CView ? Also note that I am reusing the ON_COMMAND() handlers for handling my messages since the goal is to exercise the same handler which gets called through Menu option clicks via my TestApp. More details about what I tried and the errors I am getting:
Attempt 1.
TestApp:
::SendMessage to MFC-app's CMainFrame asking it to bring up the CView with the desired input. ----> This works
MFCApp:
CMainFrame: Retrieves a ptr to the derived class of CView (CDesignView) and its HWND handle using the approach described here: https://support.microsoft.com/en-us/kb/108587 Code used is pasted below:
CMDIChildWnd * pChild = MDIGetActive();
if ( !pChild )
return -1;
CView *pView = pChild->GetActiveView();
if (!pView) {
MessageBox(_T("Could not get a handle to the design"), _T("Test2 Error"), MB_OK);
return -1;
}
// Fail if view is of wrong kind
if ( !pView->IsKindOf( RUNTIME_CLASS(CDesignView) ) ) {
MessageBox(_T("View obtained is not of type DesignView"), _T("Test2 Error"), MB_OK);
return -1;
}
CDesignView* designView = (CDesignView*)pView ;
HWND view_hWnd = designView->m_hWnd ;
if (!view_hWnd) {
MessageBox(_T("designView handle could not be obtained"), _T("Test2 Error"), MB_OK);
return -1;
}
-------------------> At this point the code has non-NULL values for view_hWnd and designView. However when I use these for SendMessage it fails:
designView->PostMessageW(ID_DESIGN_xxx,NULL, NULL) ;
--> This does NOT work i.e no change in app as if the mesg was never sent. The ID_DESIGN_xxx handler is never called. The handler is declared as below in the CDesignView Message Map:
ON_COMMAND(ID_DESIGN_xxx , OnXXX)
(Note: I am re-using the handler which the MFCApp had already used for the menu option corresponding to this function on the CDesignView since the goal is to test it out)
-------------------->When I replaced it with a direct call to the handler as below it works:
designView->OnStarOrder() ;
However that is not the behavior I want since it involves exposing too many View handlers as public and also defeats the purpose of a test-app closely simulating the actual use model.
------------------->To further debug I also tried calling the native WM_xxx messages like below.
designView->PostMessageW(WM_CLOSE,NULL, NULL) ;
This gave an exception failure in this check : IsKindOf( RUNTIME_CLASS(CView) assertion fail.
Attempt 2
I also tried to make the TestApp send the messages to the MFCApp CDesignView instead of its own MainFrame doing it as described above. So I passed the CDerivedView handle view_hWnd from above code to TestApp using a ON_COPY message. Then TestApp does a ::SendMessage(view_hWnd,WM_CLOSE,NULL, NULL). The same error was got. This approach was tried to rule out the possibility of the CDesignView not being an active window at the time of the SendMessage. In this case, I manually click on the CView of MFCApp before letting the TestApp send the message.
None of these seem to work. Any suggestions you can provide to proceed will be of great help. Thanks in advance!
Concerning your main question of "whether there are any known limitations in trying to use SendMessage,PostMessage functions to communicate to a derived class of CView" the answer is no. The functions
SendMessage()andPostMessage()are standard Win32 API functions for providing a message to any window whose window handle is defined.A bit of background on MFC
Most of the MFC classes that wrap a window are derived from at some point
CWnd. TheCWndclass is used to wrap around a Windows window and the Win32 API used with a window. So many of the Win32 API functions that take a window handle have an analogueCWndclass method.If you look at the declaration for
CViewyou can see it is derived fromCWndwhich has a version of these two functions as methods. However the methods ofCWndhave a different interface than the Win32 API version as they eliminate the window handle as the first argument.The
CWndclass declaration looks likeLRESULT CWnd::SendMessage(UINT message, WPARAM wParam = 0, LPARAM lParam = 0);The implementation of this method within the
CWndclass is probably something along the lines of:where
m_hWndis defined in theCWndclass asHWND m_hWnd;and is the window handle that theCWndclass is wrapping.What is a message map
In an MFC window class file such as for a class derived from
CViewthere will be a set of source lines similar to:which are a set of preprocessor macros defined in an include file that allow an MFC message map be to created.
The
BEGIN_MESSAGE_MAPmacro looks like:which is creating a set of functions along with an array to store the various message map entries into.
The
END_MESSAGE_MAPmacro provides the end of the array of message map entries and looks like:The actual array elements are of a
struct AFX_MSGMAP_ENTRYwhich looks like this:Under the hood of MFC is a series of lookup functions that take a Windows message and then iterates over the list of Windows messages declared in the message map array to see if there is a match.
If it finds a match of the Windows message id and the appropriate
wParamvalue, then it calls the function through the function pointer provided with the proper arguments of the interface specification for the matching message map entry.The
ON_COMMANDmacro, which contains the source for an array entry looks like:If you look at the definition of an
ON_COMMANDthe windows message identifier is hard coded toWM_COMMANDso in order to trigger theON_COMMANDentry, a Windows message must specify theWM_COMMANDmessage identifier.The MFC Run Time knows that it is to call the message handler with no arguments because the signature type is
AfxSigCmd_v, a value in the enumerationAfxSigwhich is used to inform the MFC Run Time what the interface to the message handler looks like.If you look at the interface specification for an
ON_COMMANDhandler, there are no arguments so when the MFC run time calls the designated function pointer, it does not provide any arguments.So to use the
ClassViewclass' methodSendMessage()to send a Windows message to trigger a message map entry ofON_COMMAND(ID_DESIGN_xxx , OnXXX)of aClassViewobject variable ofviewObjectyou would need to use:or you could use the Win32 API with:
Another example: ON_NOTIFY_EX
Another message map macro that is different is the
ON_NOTIFY_EXmacro. It looks like:and would appear in the message map as:
The function that will be called when this message map entry is triggered has an interface that looks like:
To trigger this you would need to send a message such as:
as the specification for a
WM_NOTIFYWindows message is:There is also an
ON_NOTIFYmessage map macro which has a different type of signature,AfxSigNotify_v, than theON_NOTIFY_EXmacro and the message handler has a different interface than what is used for theON_NOTIFY_EXmacro. However both use theWM_NOTIFYWindows message id. It looks like: