2

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

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

尝试1。

测试应用:

::SendMessage 到 MFC 应用程序的 CMainFrame 要求它以所需的输入调出 CView。----> 这行得通

MFC应用程序:

CMainFrame:使用此处描述的方法检索 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) ;

--> 这不起作用,即应用程序没有变化,就好像从未发送过消息一样。ID_DESIGN_xxx 处理程序永远不会被调用。该处理程序在 CDesignView 消息映射中声明如下:

ON_COMMAND(ID_DESIGN_xxx , OnXXX)

(注意:我正在重新使用 MFCApp 已经用于 CDesignView 上与此功能对应的菜单选项的处理程序,因为目标是对其进行测试)

-------------------->当我将其替换为直接调用处理程序时,如下所示:

设计视图->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)。得到了同样的错误。这种方法试图排除在发送消息时 CDesignView 不是活动窗口的可能性。在这种情况下,我在让 TestApp 发送消息之前手动单击 MFCApp 的 CView。

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

4

1 回答 1

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

where在类m_hWnd中定义为和 是类包装的窗口句柄。CWndHWND 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_COMMANDWindows 消息标识符的定义是硬编码的,WM_COMMAND那么为了触发ON_COMMAND条目,Windows 消息必须指定WM_COMMAND消息标识符。

MFC 运行时知道它将调用不带参数的消息处理程序,因为签名类型是AfxSigCmd_v,枚举AfxSig中的一个值,用于通知 MFC 运行时消息处理程序的接口是什么样的。

如果查看ON_COMMAND处理程序的接口规范,则没有参数,因此当 MFC 运行时调用指定的函数指针时,它不提供任何参数。

因此,要使用ClassView类的方法SendMessage()发送 Windows 消息来触发对象变量的ON_COMMAND(ID_DESIGN_xxx , OnXXX)消息映射条目,您需要使用:ClassViewviewObject

viewObject->SendMessage(WM_COMMAND, ID_DESIGN_xxx, 0);

或者您可以将 Win32 API 用于:

::SendMessage (viewObject->m_hWnd, WM_COMMAND, ID_DESIGN_xxx, 0);

另一个例子:ON_NOTIFY_EX

另一个不同的消息映射宏是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 )

要触发此操作,您需要发送一条消息,例如:

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

因为Windows 消息的规范WM_NOTIFY是:

参数

发送消息的公共控件的标识符。不保证此标识符是唯一的。应用程序应使用 NMHDR 结构的 hwndFrom 或 idFrom 成员(作为 lParam 参数传递)来标识控件。

参数

指向包含通知代码和附加信息的 NMHDR 结构的指针。对于某些通知消息,此参数指向一个较大的结构,该结构将 NMHDR 结构作为其第一个成员。

还有一个ON_NOTIFY消息映射宏,其签名类型与宏不同AfxSigNotify_vON_NOTIFY_EX消息处理程序具有与宏所用接口不同的接口ON_NOTIFY_EX。但是,两者都使用WM_NOTIFYWindows 消息 ID。看起来像:

#define ON_NOTIFY(wNotifyCode, id, memberFxn) \
    { WM_NOTIFY, (WORD)(int)wNotifyCode, (WORD)id, (WORD)id, AfxSigNotify_v, \
        (AFX_PMSG) \
        (static_cast< void (AFX_MSG_CALL CCmdTarget::*)(NMHDR*, LRESULT*) > \
        (memberFxn)) },
于 2018-06-04T20:29:14.403 回答