Перейти к содержимому

Фотография

Workarounds for C++ (MFC) controls


  • Авторизуйтесь для ответа в теме
Сообщений в теме: 3

#1 abaj

abaj

    Новый участник

  • Members
  • Pip
  • 3 сообщений

Отправлено 04 декабря 2009 - 17:19

Добрый день,

Есть задача:
автоматизировать тестирование приложения, написанного в С++ MFC.
дополнительно есть ActiveX контролы, но только аналоги Button и StaticText.

Автотест тулза еще не выбрана, и один из главных критериев - чтобы тулза или сама хорошо работала с MFC, или было легко реализовать воркэраунд.

Вопрос: Какие есть способы реализовать воркэраунд для C++ MFC контрола ?

1. MFC контролы отзываються на SendMessage

Большинство средств автоматизации позволяют подгружать dll, брать handle обьекта и вызывать что-то типа
::SendMessageW(hWnd, WM_GETTEXT, lParam, (LPARAM)pStr);
Но даже стандартные MFC контролы поддерживают не все мессаги.

Пример:
Генерим в VisualStudio (2008 trial) MFC MDI application
И пытаемся работать с CMFCToolBar из TestComplete (7, trial) и AutoIt.

TestComplete 7 (в отличие от TestComplete 5) может и записать клики на кнопки тулбара, и брать проперти wButtonBounds.
AutoIt может работать с "обычным" тулбаром через функции _GUICtrlToolbar_, реализованные через SendMessage. Но CMFCToolBar не берет.

Например TB_GETRECT возвращает пустой RECT
Та же проблема и если делать вызов из какого-нибудь C++ приложения
wParam = 35809; //TB button id
	UINT Msg = TB_GETRECT;
	RECT * tRect = new RECT();
	LPARAM lp = (LPARAM)tRect;
	::SendMessageW(hWnd, Msg, wParam, lp);

Хотя может неправильно отсылаю мессагу.

(Можно еще заставить программистов реазовать/исправить обработку нужной мессаги.
Но это может не получиться, например программистам пока не удается реализовать обработку WM_GETTEXT для custom CComboBox, мессага где-то теряется)

2. Может быть можно написать какие-то другие wrappers в C++ или в чем то еще ?

3. Любые другие способы

Спасибо

PS: этот же вопрос уже задан программистам конкретного приложения, пока думают

  • 1

#2 vass

vass

    Опытный участник

  • Members
  • PipPipPipPip
  • 298 сообщений
  • ФИО:Василий

Отправлено 07 декабря 2009 - 13:05

1) Поведение может быть реализовано не в самом контроле, а в его контейнере.
Т.о. нужно пробовать отсылать нажатия как контролу, так и его контейнеру (или контейнеру более высокого уровня, вплоть до главного окна) установив фокус ввода в нужный контрол. Регулируется это соответствующим значением hWnd.
2) Кроме вызова сообщений, в Windows можно воспользоваться взятием указателя на объект. Т.е. можно получить hWnd , а из него - *CWnd , который тем или иным образом привести к нужному объекту. После чего, подключаем библиотеку объекта и можно использовать высокоуровневые public методы и данные. Единственная лажа - нельзя вызывать/получать static-и. Или объекты, которые используют static данные (например CString) :(

вот пример куска dll с dynamic_cast, но можно на самом деле делать и статическое приведение, только тогда поправку на смещения придется делать руками

CTreeListCtrl* GetTreeListControl(HWND hWnd)
{
   CTreeListCtrl* pControl = dynamic_cast<CTreeListCtrl*>(CWnd::FromHandlePermanent(hWnd));
   if(!pControl)
   {
//
// Add any error handlig here
//
   }

   return pControl;
}

extern "C" _declspec(dllexport) int TreeListView_Test(HWND hWnd)
{
	return GetTreeListControl(hWnd) != 0 ? 1 : 0;
}

extern "C" _declspec(dllexport) int TreeListView_GetVisibleItemCount(HWND hWnd)
{
   return GetTreeListControl(hWnd)->GetVisibleCount();
}

extern "C" _declspec(dllexport) int TreeListView_GetItemCount(HWND hWnd)
{
   return GetTreeListControl(hWnd)->GetItemCount();
}

extern "C" _declspec(dllexport) LONG TreeListView_GetNextItem(HWND hWnd, LONG pItem, UINT iCode)
{
	return (LONG)GetTreeListControl(hWnd)->GetNextItem((CTreeListItem*)pItem, iCode);
}

3) Включить дополнительный код в сам контрол при компиляции. Например у Силка есть ExtensionKit его можно использовать для добавления интерфейса над любыми контролами, которые пишутся на С/С++ или могут давать доступ к себе, используя С++.

4) Локальные графические методы - GetPixel-s по ключевым точкам или небольшые скриншоты "вокруг курсора", с тем, чтобы проевести небольшой анализ и распознать текущее состояние тестируемого контрола (изменение цвета, добавление графических эл-тов и т.д.)
  • 0

#3 abaj

abaj

    Новый участник

  • Members
  • Pip
  • 3 сообщений

Отправлено 09 декабря 2009 - 20:35

Спасибо,

И теперь могу добавить две вариации на тему SendMessage, которые предложили программисты тестируемого приложения

1) отсылать контейнеру сообщение WM_COMMAND с параметром Control ID - ид-шником контрола, который должен стандартно отработать.
т.е. такой вариант успешно нажал на кнопку тулбара:
SendMessage( hWndParent, WM_COMMAND, iBtnID);
где iBtnID - ид-шник кнопки на тулбаре

2) посмотреть в хедер файле конкретного MFC контрола - какие мессаджи отрабатываютсья. так в afxtoolbar.h не было TB_GETRECT, но был TB_GETITEMRECT.
Дальше нужно посмотреть на значение, присвоенное этому месседжу. Если оно больше значения WM_USER - то просто так отправить не получиться, потому что нужно еще делать marshalling для передаваемых параметров.
см. http://www.piotrkalu...cs/ch02s04.html

в этом случае отработал такой вариант получения координат кнопки тулбара
HWND hWndParent = FindWindow(NULL, L"MFCToolbarTest - MFCToolbarTest1" + char(0));
HWND hWndParent1 = FindWindowEx(hWndParent,NULL,L"Afx:DockPane:400000:8:10011:10" + char(0), L"" + char(0));
HWND hWnd = FindWindowEx(hWndParent1,NULL,L"Afx:ToolBar:400000:8:10011:10" + char(0), L"Menu Bar" + char(0));

RECT tRect;
DWORD pid = 0;
GetWindowThreadProcessId( hWndParent, &pid );
HANDLE hProcHnd = OpenProcess( PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid );

LPVOID pRECT = VirtualAllocEx( hProcHnd, NULL, sizeof( RECT ), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
tRect.bottom = 0;
tRect.left = 0;
tRect.right = 0;
tRect.top = 0;

DWORD copied = 0;

WriteProcessMemory( hProcHnd, pRECT, &tRect, sizeof( RECT ), &copied );
SendMessage( hWnd, TB_GETITEMRECT, (WPARAM)3, (LPARAM)pRECT );
ReadProcessMemory( hProcHnd, pRECT, (LPVOID)&tRect, sizeof( RECT ), &copied );
VirtualFreeEx( hProcHnd, pRECT, 0, MEM_RELEASE );
CloseHandle (hProcHnd);
std::wcout << tRect.right << std::endl;

  • 1

#4 vass

vass

    Опытный участник

  • Members
  • PipPipPipPip
  • 298 сообщений
  • ФИО:Василий

Отправлено 11 декабря 2009 - 09:22

... при таком разнообразии библиотек, поневоле начинаешь задумываться, что тулы, сравнивающие изображения, не так уж и наивны :)))
  • 0


Количество пользователей, читающих эту тему: 0

0 пользователей, 0 гостей, 0 анонимных