Я использую Visual Studio 2017. Я добавил два проекта в решение. Один проект на C # с WPF. Другой в VC ++ с ATL.
Из C # я вызываю функцию в проекте VC ++, которая устанавливает низкоуровневый хук мыши. Часть кода в низкоуровневой мышиной процедуре выглядит следующим образом:
MSLLHOOKSTRUCT stMouse = *(MSLLHOOKSTRUCT*)(lParam);
POINT pt = stMouse.pt;
IAccessible* pAcc;
VARIANT varChild;
HRESULT hr = AccessibleObjectFromPoint(pt, &pAcc, &varChild);
VARIANT varRole;
hr = pAcc->get_accRole(varChild, &varRole);
Когда я тестирую, установив флажок на вкладке «Вид» в MS Word 2013, я получаю роль клиента для сообщений WM_LBUTTONDOWN и WM_LBUTTONUP. Но я должен получить роль в качестве флажка.
Я проверил Inspect.exe, который поставляется с Windows 10 SDK. Inspect.exe показывает роль правильно, как флажок. Inspect.exe имеет два параметра — один для просмотра свойств автоматизации пользовательского интерфейса, а другой — для просмотра свойств MSAA. Я вижу свойства MSAA.
Как я могу получить правильную роль? Какой метод deos Inspect.exe использует?
Microsoft Active Accessibility / MSAA (на основе IAccessible
интерфейс) является устаревшим API. Вы должны теперь использовать Автоматизация Windows. МСА основана на COM и использует интерфейсы, наиболее важным из которых является IUIAutomationElement. Inspect.exe использует UIA или MSAA.
Примечание. NET совместим с UIA, и WPF может довольно легко представить свои элементы интерфейса для UIA, используя Класс AutomationPeer а также Метод UIElement.OnCreateAutomationPeer метод. WPF предоставляет реализацию по умолчанию, которую можно настроить при необходимости.
Вот пример, аналогичный вашему в C ++, с использованием API-интерфейса UIA вместо MSAA (мы могли бы написать то же самое с помощью C #):
#include "stdafx.h" // needs <uiautomation.h>
class CCoInitialize { // https://blogs.msdn.microsoft.com/oldnewthing/20040520-00/?p=39243
public:
CCoInitialize() : m_hr(CoInitialize(NULL)) { }
~CCoInitialize() { if (SUCCEEDED(m_hr)) CoUninitialize(); }
operator HRESULT() const { return m_hr; }
HRESULT m_hr;
};
// this is a poor-man COM object class
class CHandler : public IUIAutomationEventHandler
{
public:
HRESULT QueryInterface(REFIID riid, LPVOID * ppv)
{
if (!ppv) return E_INVALIDARG;
*ppv = NULL;
if (riid == IID_IUnknown || riid == IID_IUIAutomationEventHandler)
{
*ppv = this;
return NOERROR;
}
return E_NOINTERFACE;
}
ULONG AddRef() { return 1; }
ULONG Release() { return 1; }
// this will be called by UIA
HRESULT HandleAutomationEvent(IUIAutomationElement *sender, EVENTID eventId)
{
wprintf(L"Event id: %u\n", eventId);
return S_OK;
}
};
int main()
{
CCoInitialize init;
// this sample uses Visual Studio's ATL smart pointers, but it's not mandatory at all
CComPtr<IUIAutomation> uia;
if (SUCCEEDED(uia.CoCreateInstance(CLSID_CUIAutomation)))
{
// get mouse pos now
POINT pt;
GetCursorPos(&pt);
// find what type of "control" was under the mouse
CComPtr<IUIAutomationElement> element;
if (SUCCEEDED(uia->ElementFromPoint(pt, &element)))
{
CComBSTR type;
element->get_CurrentLocalizedControlType(&type);
wprintf(L"type at %u,%u: %s\n", pt.x, pt.y, type.m_str);
}
// get root
CComPtr<IUIAutomationElement> root;
if (SUCCEEDED(uia->GetRootElement(&root)))
{
// add a handler that will trigger every time you open any window on the desktop
// in the real world, you'll need to delete this pointer to avoid memory leaks of course
CHandler *pHandler = new CHandler();
if (SUCCEEDED(uia->AddAutomationEventHandler(UIA_Window_WindowOpenedEventId, root, TreeScope::TreeScope_Children, NULL, pHandler)))
{
// since this is a console app, we need to run the Windows message loop otherwise, you'll never get any UIA events
// for this sample, just press CTRL-C to stop the program. in the real world, you'll need to get out properly
// if you have a standard windows app, the loop will be already there
while (TRUE)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
}
return 0;
}
Других решений пока нет …