SendMessage / PostMessage到派生的CView类不适用于MFC应用程序

时间:2015-09-16 08:30:22

标签: visual-c++ mfc sendmessage cview

我正在为传统的基于MFC的应用程序(MFC-app)构建测试应用程序(testApp)。我试图通过它们之间的消息传递来模拟MFC应用程序上的鼠标点击。我能够成功地从MFC-app菜单中调出对话框。但是,当我尝试模拟鼠标单击MFC -app的视图时,它似乎不起作用。

我遇到的主要问题是,在尝试使用SendMessage,PostMessage函数与CView的派生类进行通信时是否存在任何已知限制?另请注意,我正在重复使用ON_COMMAND()处理程序来处理我的消息,因为目标是运行相同的处理程序,通过我的TestApp通过菜单选项单击来调用它。关于我尝试的内容和我得到的错误的更多细节:

尝试1。

TestApp:

:: SendMessage到MFC-app的CMainFrame,要求它调出带有所需输入的CView。 ---->这工作

MFCApp:

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)断言失败。

尝试2

我还尝试让TestApp将消息发送到MFCApp CDesignView而不是它自己的MainFrame,如上所述。所以我使用ON_COPY消息将CDerivedView句柄view_hWnd从上面的代码传递给TestApp。然后TestApp执行:: SendMessage(view_hWnd,WM_CLOSE,NULL,NULL)。得到了同样的错误。 尝试这种方法排除了在SendMessage时CDesignView不是活动窗口的可能性。在这种情况下,我在让TestApp发送消息之前手动单击MFCApp的CView。

这些似乎都不起作用。您可以提供的任何建议将有很大帮助。提前谢谢!

1 个答案:

答案 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_hWndCWnd类中定义为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