SendMessage/PostMessage к производному классу CView, не работающему для приложения MFC
Я создаю тестовое приложение (testApp) для устаревшего приложения на основе MFC (MFC-app). Я пытаюсь смоделировать щелчки мышью на MFC-приложении, используя обмен сообщениями между ними. Мне удалось сделать это успешно для вызова диалоговых окон из меню приложения MFC. Однако, когда я пытаюсь смоделировать щелчок мышью по представлению MFC-app, это не работает.
Главный вопрос, который у меня есть, заключается в том, существуют ли какие-либо известные ограничения при попытке использовать функции SendMessage,PostMessage для связи с производным классом CView? Также обратите внимание, что я повторно использую обработчики ON_COMMAND() для обработки моих сообщений, поскольку цель состоит в том, чтобы использовать тот же обработчик, который вызывается через щелчки опций меню через мое TestApp. Подробнее о том, что я пробовал и какие ошибки я получаю:
Попытка 1.
TestApp:
:: SendMessage к CMainFrame MFC-приложения с просьбой вызвать CView с желаемым вводом. ----> Это работает
MFCApp:
CMainFrame: извлекает ptr для производного класса CView (CDesignView) и его дескриптора HWND, используя подход, описанный здесь: 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. Однако, когда я использую их для SendMessage, это терпит неудачу:
designView-> PostMessageW (ID_DESIGN_xxx, NULL, NULL);
-> Это НЕ работает, т.е. никаких изменений в приложении, как будто сообщение никогда не отправлялось. Обработчик 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, как описано выше. Поэтому я передал дескриптор CDerivedView view_hWnd из приведенного выше кода в TestApp с помощью сообщения ON_COPY. Затем TestApp выполняет::SendMessage(view_hWnd,WM_CLOSE,NULL, NULL). Та же ошибка была получена. Этот подход пытался исключить возможность того, что CDesignView не будет активным окном во время SendMessage. В этом случае я вручную нажимаю на CView MFCApp, прежде чем позволить TestApp отправить сообщение.
Ни один из них, кажется, не работает. Любые предложения, которые вы можете предоставить, будут очень полезны. Заранее спасибо!
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 и соответствующего 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 должно быть указано WM_COMMAND
идентификатор сообщения.
MFC Run Time знает, что это вызов обработчика сообщений без аргументов, потому что тип подписи AfxSigCmd_v
значение в перечислении AfxSig
который используется для информирования MFC Run Time о том, как выглядит интерфейс к обработчику сообщений.
Если вы посмотрите на спецификацию интерфейса для ON_COMMAND
обработчик, аргументов нет, поэтому, когда MFC во время выполнения вызывает обозначенный указатель функции, он не предоставляет никаких аргументов.
Таким образом, чтобы использовать ClassView
метод класса SendMessage()
отправить сообщение Windows, чтобы вызвать запись карты сообщения ON_COMMAND(ID_DESIGN_xxx , OnXXX)
из ClassView
переменная объекта viewObject
вам нужно будет использовать:
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);
в качестве спецификации для WM_NOTIFY
Сообщение Windows:
WPARAM
Идентификатор общего элемента управления, отправляющего сообщение. Этот идентификатор не гарантированно будет уникальным. Приложение должно использовать член hwndFrom или idFrom структуры NMHDR (передаваемый как параметр lParam) для идентификации элемента управления.
LPARAM
Указатель на структуру NMHDR, которая содержит код уведомления и дополнительную информацию. Для некоторых уведомлений этот параметр указывает на более крупную структуру, которая имеет структуру NMHDR в качестве первого члена.
Также есть ON_NOTIFY
макрос карты сообщений с другим типом подписи, AfxSigNotify_v
, чем ON_NOTIFY_EX
макрос и обработчик сообщений имеет интерфейс, отличный от того, который используется для ON_NOTIFY_EX
макро. Однако оба используют WM_NOTIFY
Идентификатор сообщения Windows. Это выглядит как:
#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)) },