我正在为传统的基于MFC的应用程序(MFC-app)构建测试应用程序(testApp)。我试图通过它们之间的消息传递来模拟MFC应用程序上的鼠标点击。我能够成功地从MFC-app菜单中调出对话框。但是,当我尝试模拟鼠标单击MFC -app的视图时,它似乎不起作用。
我遇到的主要问题是,在尝试使用SendMessage,PostMessage函数与CView的派生类进行通信时是否存在任何已知限制?另请注意,我正在重复使用ON_COMMAND()处理程序来处理我的消息,因为目标是运行相同的处理程序,通过我的TestApp通过菜单选项单击来调用它。关于我尝试的内容和我得到的错误的更多细节:
:: SendMessage到MFC-app的CMainFrame,要求它调出带有所需输入的CView。 ---->这工作
CMainFrame:使用此处描述的方法检索ptr到CView(CDesignView)及其HWND句柄的派生类的ptr:https://support.microsoft.com/en-us/kb/108587 使用的代码粘贴在下面:
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;
}
------------------->此时,代码具有view_hWnd和designView的非NULL值。但是,当我将它们用于SendMessage时,它失败了:
designView-> PostMessageW(ID_DESIGN_xxx,NULL,NULL);
- >这不起作用,即app中没有变化,好像从未发送过mesg一样。永远不会调用ID_DESIGN_xxx处理程序。 处理程序在CDesignView消息映射中声明如下:
ON_COMMAND(ID_DESIGN_xxx,OnXXX)
(注意:我正在重新使用MFCApp已用于CDesignView上与此函数对应的菜单选项的处理程序,因为目标是测试它)
-------------------->当我用直接调用处理程序替换它时,如下所示:
designView-> OnStarOrder();
然而,这不是我想要的行为,因为它涉及将太多的View处理程序暴露为公共,并且还违背了密切模拟实际使用模型的测试应用程序的目的。
------------------->为了进一步调试,我还尝试调用如下的本机WM_xxx消息。
designView-> PostMessageW(WM_CLOSE,NULL,NULL);
这次检查失败了:IsKindOf(RUNTIME_CLASS(CView)断言失败。
我还尝试让TestApp将消息发送到MFCApp CDesignView而不是它自己的MainFrame,如上所述。所以我使用ON_COPY消息将CDerivedView句柄view_hWnd从上面的代码传递给TestApp。然后TestApp执行:: SendMessage(view_hWnd,WM_CLOSE,NULL,NULL)。得到了同样的错误。 尝试这种方法排除了在SendMessage时CDesignView不是活动窗口的可能性。在这种情况下,我在让TestApp发送消息之前手动单击MFCApp的CView。
这些似乎都不起作用。您可以提供的任何建议将有很大帮助。提前谢谢!
答案 0 :(得分:1)
关于“尝试使用SendMessage是否存在任何已知限制,PostMessage函数与CView的派生类进行通信”的主要问题,答案是否定的。函数SendMessage()
和PostMessage()
是标准的Win32 API函数,用于向定义了窗口句柄的任何窗口提供消息。
关于MFC的一些背景知识
包装窗口的大多数MFC类都是从某个点CWnd
派生的。 CWnd
类用于包装Windows窗口和用于窗口的Win32 API。许多采用窗口句柄的Win32 API函数都有一个模拟CWnd
类方法。
如果查看CView
的声明,您可以看到它来自CWnd
,它具有这两个函数的版本作为方法。但是CWnd
的方法与Win32 API版本的接口不同,因为它们消除了窗口句柄作为第一个参数。
CWnd
类声明看起来像
LRESULT CWnd::SendMessage(UINT message, WPARAM wParam = 0, LPARAM lParam = 0);
在CWnd
类中实现此方法可能类似于:
LRESULT CWnd::SendMessage(UINT message, WPARAM wParam, LPARAM lParam)
{
return ::SendMessage (m_hWnd, message, wParam, lParam);
}
其中m_hWnd
在CWnd
类中定义为HWND m_hWnd;
,并且是CWnd
类正在包装的窗口句柄。
什么是消息地图
在MFC窗口类文件中,例如派生自CView
的类,将有一组类似于以下内容的源代码行:
BEGIN_MESSAGE_MAP(CPCSampleApp, CWinApp)
//{{AFX_MSG_MAP(CPCSampleApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
ON_COMMAND(ID_APP_EXIT, OnAppExit)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
是包含文件中定义的一组预处理器宏,允许创建MFC消息映射。
BEGIN_MESSAGE_MAP
宏看起来像:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
PTM_WARNING_DISABLE \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return GetThisMessageMap(); } \
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
{ \
typedef theClass ThisClass; \
typedef baseClass TheBaseClass; \
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
{
创建一组函数以及一个数组,用于将各种消息映射条目存储到。
END_MESSAGE_MAP
宏提供了消息映射条目数组的结尾,如下所示:
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
static const AFX_MSGMAP messageMap = \
{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
return &messageMap; \
} \
PTM_WARNING_RESTORE
实际的数组元素是struct AFX_MSGMAP_ENTRY
,如下所示:
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT_PTR nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
在MFC的引擎下是一系列查找函数,它们接收Windows消息,然后遍历消息映射数组中声明的Windows消息列表,以查看是否存在匹配。
如果找到Windows消息id和相应wParam
值的匹配项,则它通过函数指针调用该函数,该函数指针提供了匹配消息映射条目的接口规范的正确参数。
包含数组条目源的ON_COMMAND
宏如下所示:
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \
static_cast<AFX_PMSG> (memberFxn) },
// ON_COMMAND(id, OnBar) is the same as
// ON_CONTROL(0, id, OnBar) or ON_BN_CLICKED(0, id, OnBar)
如果查看ON_COMMAND
的定义,则Windows消息标识符被硬编码为WM_COMMAND
,因此为了触发ON_COMMAND
条目,Windows消息必须指定{{ 1}}消息标识符。
MFC运行时知道它是调用没有参数的消息处理程序,因为签名类型是WM_COMMAND
,枚举AfxSigCmd_v
中的值用于通知MFC运行时什么消息处理程序的接口看起来像。
如果查看AfxSig
处理程序的接口规范,则没有参数,因此当MFC运行时调用指定的函数指针时,它不提供任何参数。
因此,使用ON_COMMAND
类'方法ClassView
发送Windows消息以触发SendMessage()
ON_COMMAND(ID_DESIGN_xxx , OnXXX)
对象变量ClassView
的消息映射条目你需要使用:
viewObject
或者你可以使用Win32 API:
viewObject->SendMessage(WM_COMMAND, ID_DESIGN_xxx, 0);
另一个例子:ON_NOTIFY_EX
另一个不同的消息映射宏是::SendMessage (viewObject->m_hWnd, WM_COMMAND, ID_DESIGN_xxx, 0);
宏。它看起来像:
ON_NOTIFY_EX
并将在消息地图中显示为:
#define ON_NOTIFY_EX(wNotifyCode, id, memberFxn) \
{ WM_NOTIFY, (WORD)(int)wNotifyCode, (WORD)id, (WORD)id, AfxSigNotify_EX, \
(AFX_PMSG) \
(static_cast< BOOL (AFX_MSG_CALL CCmdTarget::*)(UINT, NMHDR*, LRESULT*) > \
(memberFxn)) },
触发此消息映射条目时将调用的函数具有如下界面:
ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipText)
要触发此操作,您需要发送消息,例如:
BOOL CPCSampleView::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
,因为specification for a WM_NOTIFY
Windows message是:
的wParam
发送消息的公共控件的标识符。这个 标识符不保证是唯一的。应用程序应该使用 NMHDR结构的hwndFrom或idFrom成员(作为 lParam参数)来识别控件。
lParam的
指向包含通知的NMHDR结构的指针 代码和其他信息。对于一些通知消息,这个 参数指向具有NMHDR结构的较大结构 它的第一个成员。
还有一个TOOLTIPTEXT myToolTipInfo = {0};
// fill in the necessary data fields to identify the tool tip properly
myToolTipInfo.hdr.idFrom = ID_CONNECT_LAN_ON; // set the id for which tool text to fetch
myToolTipInfo.hdr.code = TTN_NEEDTEXT; // set the notify code
// ... other fields as appropriate
viewObject->SendMessage(WM_NOTIFY, idcControlId, &myToolTipInfo);
消息映射宏,它具有与ON_NOTIFY
宏不同的签名类型AfxSigNotify_v
,并且消息处理程序具有与用于ON_NOTIFY_EX
宏。但是两者都使用ON_NOTIFY_EX
Windows消息ID。它看起来像:
WM_NOTIFY