SendMessage/PostMessage 到派生的 CView 类不适用于 MFC 应用程序 [英] SendMessage/PostMessage to a derived CView class not working for MFC application

查看:75
本文介绍了SendMessage/PostMessage 到派生的 CView 类不适用于 MFC 应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

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

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:

::SendMessage 到 MFC 应用程序的 CMainFrame 要求它使用所需的输入调出 CView.----> 这有效

::SendMessage to MFC-app's CMainFrame asking it to bring up the CView with the desired input. ----> This works

CMainFrame:使用此处描述的方法检索 CView (CDesignView) 的派生类及其 HWND 句柄的 ptr:https://support.microsoft.com/en-us/kb/108587使用的代码粘贴如下:

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

-------------------> 此时代码的view_hWnd 和designView 具有非NULL 值.但是,当我将这些用于 SendMessage 时,它​​失败了:

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

designView->PostMessageW(ID_DESIGN_xxx,NULL, NULL) ;

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

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

ON_COMMAND(ID_DESIGN_xxx , OnXXX)

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

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

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.

------------------->为了进一步调试,我还尝试调用本机 WM_xxx 消息,如下所示.

------------------->To further debug I also tried calling the native WM_xxx messages like below.

designView->PostMessageW(WM_CLOSE,NULL, NULL) ;

designView->PostMessageW(WM_CLOSE,NULL, NULL) ;

这在此检查中导致异常失败:IsKindOf( RUNTIME_CLASS(CView) 断言失败.

This gave an exception failure in this check : IsKindOf( RUNTIME_CLASS(CView) assertion fail.

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

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!

推荐答案

关于您的主要问题在尝试使用 SendMessage、PostMessage 函数与 CView 的派生类进行通信时是否存在任何已知限制",答案是否定的.函数 SendMessage()PostMessage() 是标准的 Win32 API 函数,用于向定义了窗口句柄的任何窗口提供消息.

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() and PostMessage() are standard Win32 API functions for providing a message to any window whose window handle is defined.

MFC 的一些背景

大多数包装窗口的 MFC 类都是从某个点 CWnd 派生而来的.CWnd 类用于环绕 Windows 窗口和与窗口一起使用的 Win32 API.许多采用窗口句柄的 Win32 API 函数都有一个类似的 CWnd 类方法.

Most of the MFC classes that wrap a window are derived from at some point CWnd. The CWnd class 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 analogue CWnd class method.

如果您查看 CView 的声明,您会发现它是从 CWnd 派生而来的,它具有这两个函数的版本作为方法.然而,CWnd 的方法具有与 Win32 API 版本不同的接口,因为它们消除了窗口句柄作为第一个参数.

If you look at the declaration for CView you can see it is derived from CWnd which has a version of these two functions as methods. However the methods of CWnd have a different interface than the Win32 API version as they eliminate the window handle as the first argument.

CWnd 类声明看起来像

LRESULT CWnd::SendMessage(UINT message, WPARAM wParam = 0, LPARAM lParam = 0);

这个方法在 CWnd 类中的实现大概是这样的:

The implementation of this method within the CWnd class is probably something along the lines of:

LRESULT CWnd::SendMessage(UINT message, WPARAM wParam, LPARAM lParam)
{
    return ::SendMessage (m_hWnd, message, wParam, lParam);
}

其中 m_hWndCWnd 类中定义为 HWND m_hWnd; 并且是 CWnd 的窗口句柄> 课程正在结束.

where m_hWnd is defined in the CWnd class as HWND m_hWnd; and is the window handle that the CWnd class is wrapping.

什么是消息映射

在 MFC 窗口类文件中,例如从 CView 派生的类,将有一组类似于以下内容的源代码行:

In an MFC window class file such as for a class derived from CView there will be a set of source lines similar to:

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 消息映射.

which are a set of preprocessor macros defined in an include file that allow an MFC message map be to created.

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[] =  \
        {

它正在创建一组函数以及一个数组来存储各种消息映射条目.

which is creating a set of functions along with an array to store the various message map entries into.

END_MESSAGE_MAP 宏提供消息映射条目数组的结尾,如下所示:

The END_MESSAGE_MAP macro provides the end of the array of message map entries and looks like:

#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,如下所示:

The actual array elements are of a struct AFX_MSGMAP_ENTRY which looks like this:

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 消息列表,以查看是否存在匹配项.

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.

如果它找到了 Windows 消息 ID 和相应的 wParam 值的匹配项,则它通过为匹配消息映射条目提供接口规范的正确参数的函数指针调用该函数.

If it finds a match of the Windows message id and the appropriate wParam value, then it calls the function through the function pointer provided with the proper arguments of the interface specification for the matching message map entry.

ON_COMMAND 宏,其中包含数组条目的源代码,如下所示:

The ON_COMMAND macro, which contains the source for an array entry looks like:

#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 消息必须指定 WM_COMMAND 消息标识符.

If you look at the definition of an ON_COMMAND the windows message identifier is hard coded to WM_COMMAND so in order to trigger the ON_COMMAND entry, a Windows message must specify the WM_COMMAND message identifier.

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

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 enumeration AfxSig which is used to inform the MFC Run Time what the interface to the message handler looks like.

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

If you look at the interface specification for an ON_COMMAND handler, there are no arguments so when the MFC run time calls the designated function pointer, it does not provide any arguments.

所以使用ClassView类的方法SendMessage()发送Windows消息触发ON_COMMAND(ID_DESIGN_xxx, OnXXX)<您需要使用的 viewObjectClassView 对象变量的/code> :

So to use the ClassView class' method SendMessage() to send a Windows message to trigger a message map entry of ON_COMMAND(ID_DESIGN_xxx , OnXXX) of a ClassView object variable of viewObject you would need to use:

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

或者你可以使用 Win32 API:

or you could use the Win32 API with:

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

另一个例子:ON_NOTIFY_EX

另一个不同的消息映射宏是ON_NOTIFY_EX 宏.它看起来像:

Another message map macro that is different is the ON_NOTIFY_EX macro. It looks like:

#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)) },

并将在消息映射中显示为:

and would appear in the message map as:

ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipText)

触发此消息映射条目时将调用的函数具有如下所示的界面:

The function that will be called when this message map entry is triggered has an interface that looks like:

BOOL CPCSampleView::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )

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

To trigger this you would need to send a message such as:

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

作为 规范WM_NOTIFY Windows 消息是:

as the specification for a WM_NOTIFY Windows message is:

wParam

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

The identifier of the common control sending the message. This identifier is not guaranteed to be unique. An application should use the hwndFrom or idFrom member of the NMHDR structure (passed as the lParam parameter) to identify the control.

lParam

指向包含通知的 NMHDR 结构的指针代码和附加信息.对于某些通知消息,这参数指向具有 NMHDR 结构的更大结构,如它的第一个成员.

A pointer to an NMHDR structure that contains the notification code and additional information. For some notification messages, this parameter points to a larger structure that has the NMHDR structure as its first member.

还有一个 ON_NOTIFY 消息映射宏,它具有与 ON_NOTIFY_EX 宏和消息处理程序不同的签名类型,AfxSigNotify_v具有与用于 ON_NOTIFY_EX 宏的接口不同的接口.但是,两者都使用 WM_NOTIFY Windows 消息 ID.它看起来像:

There is also an ON_NOTIFY message map macro which has a different type of signature, AfxSigNotify_v, than the ON_NOTIFY_EX macro and the message handler has a different interface than what is used for the ON_NOTIFY_EX macro. However both use the WM_NOTIFY Windows message id. It looks like:

#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)) },

这篇关于SendMessage/PostMessage 到派生的 CView 类不适用于 MFC 应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆