I'm using the Accessability API to get the role when focus changes in external apps, for example Microsoft Edge, Firefox or Chrome. Until the October update to Windows the following code was working. After the update the role is always being reported as ROLE_SYSTEM_CLIENT
Am I doing something wrong or is this a bug in the update?
HWINEVENTHOOK objectFocusHook = SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, NULL, &CMonitor::WinEventHookProc, 0, 0, WINEVENT_OUTOFCONTEXT);
then
void CALLBACK CMonitor::WinEventHookProc(HWINEVENTHOOK hook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD idEventThread, DWORD time)
{
if (event == EVENT_OBJECT_FOCUS)
{
IAccessible* pAcc = nullptr;
VARIANT varChild;
if (SUCCEEDED(AccessibleObjectFromEvent(hwnd, idObject, idChild, &pAcc, &varChild)))
{
BSTR bstrName;
VARIANT varRole;
VARIANT varState;
pAcc->get_accName(varChild, &bstrName);
// after October update this always return ROLE_SYSTEM_CLIENT
if (SUCCEEDED(pAcc->get_accRole(varChild, &varRole)))
{
if (varRole.lVal == ROLE_SYSTEM_TEXT)
{
if (SUCCEEDED(pAcc->get_accState(varChild, &varState)))
{
// we do more with this
bool isProtected = (varState.lVal & STATE_SYSTEM_PROTECTED) != 0;
}
}
}
pAcc->Release();
}
}
}
Related
I want to try and run the official SetWinEventHook() example given at https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook:
// Global variable.
HWINEVENTHOOK g_hook;
// Initializes COM and sets up the event hook.
//
void InitializeMSAA()
{
CoInitialize(NULL);
g_hook = SetWinEventHook(
EVENT_SYSTEM_MENUSTART, EVENT_SYSTEM_MENUEND, // Range of events (4 to 5).
NULL, // Handle to DLL.
HandleWinEvent, // The callback.
0, 0, // Process and thread IDs of interest (0 = all)
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); // Flags.
}
// Unhooks the event and shuts down COM.
//
void ShutdownMSAA()
{
UnhookWinEvent(g_hook);
CoUninitialize();
}
// Callback function that handles events.
//
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
LONG idObject, LONG idChild,
DWORD dwEventThread, DWORD dwmsEventTime)
{
IAccessible* pAcc = NULL;
VARIANT varChild;
HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &pAcc, &varChild);
if ((hr == S_OK) && (pAcc != NULL))
{
BSTR bstrName;
pAcc->get_accName(varChild, &bstrName);
if (event == EVENT_SYSTEM_MENUSTART)
{
printf("Begin: ");
}
else if (event == EVENT_SYSTEM_MENUEND)
{
printf("End: ");
}
printf("%S\n", bstrName);
SysFreeString(bstrName);
pAcc->Release();
}
}
I wanted to compile and run this using Visual Studio, so I created a Windows Console Application Project with content:
#include <windows.h>
#include <iostream>
#include <conio.h>
#include <oleacc.h>
// Global variable.
HWINEVENTHOOK g_hook;
// Initializes COM and sets up the event hook.
//
void HandleWinEvent(HWINEVENTHOOK, DWORD, HWND,
LONG, LONG,
DWORD, DWORD);
void InitializeMSAA()
{
HRESULT hrCoInit = CoInitialize(NULL);
g_hook = SetWinEventHook(
EVENT_SYSTEM_MENUSTART, EVENT_SYSTEM_MENUEND, // Range of events (4 to 5).
NULL, // Handle to DLL.
HandleWinEvent, // The callback.
0, 0, // Process and thread IDs of interest (0 = all)
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); // Flags.
}
// Unhooks the event and shuts down COM.
//
void ShutdownMSAA()
{
UnhookWinEvent(g_hook);
CoUninitialize();
}
// Callback function that handles events.
//
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
LONG idObject, LONG idChild,
DWORD dwEventThread, DWORD dwmsEventTime)
{
IAccessible* pAcc = NULL;
VARIANT varChild;
HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &pAcc, &varChild);
if ((hr == S_OK) && (pAcc != NULL))
{
BSTR bstrName;
pAcc->get_accName(varChild, &bstrName);
if (event == EVENT_SYSTEM_MENUSTART)
{
printf("Begin: ");
}
else if (event == EVENT_SYSTEM_MENUEND)
{
printf("End: ");
}
printf("%S\n", bstrName);
SysFreeString(bstrName);
pAcc->Release();
}
}
int main()
{
std::cout << "Hello World!\n";
InitializeMSAA();
MSG msg;
while (1) {
//if (_getch() == 'q') {
// break;
//}
GetMessage(&msg, NULL, 0, 0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
ShutdownMSAA();
return 0;
}
This code is running fine, but is not detecting events. I am trying to generate events by opening and closing the Start Menu.
How can I make this example code work?
According to the explanations for EVENT_SYSTEM_MENUSTART and EVENT_SYSTEM_MENUEND in the official documentation.
The system sends this event for standard menus, which are identified
by HMENU, created using menu-template resources or Win32 menu API
elements.
As #Remy Lebeau said, 'run a separate GUI app that has a menu'.
After your console program starts, you can run a Win32 desktop application with menu.
I modified your code to run in VS2019. Added CALLBACK in front of HandleWinEvent.
#pragma comment(lib,"Oleacc.lib")
// Global variable.
HWINEVENTHOOK g_hook;
// Initializes COM and sets up the event hook.
//
void CALLBACK HandleWinEvent(HWINEVENTHOOK, DWORD, HWND, LONG, LONG, DWORD, DWORD);
Here are my test results,
I'm trying to hook winevents like EVENT_SYSTEM_MOVESIZESTART and EVENT_SYSTEM_MOVESIZEEND with all desktop windows.
For this, I used SetWinEventHook().
Here's my sample code.
BOOL DlmSetEventHook()
{
if (hInstance == NULL)
return FALSE;
CoInitialize(NULL);
hWEventHook = SetWinEventHook(EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, NULL, WinEventProc, NULL, NULL, WINEVENT_OUTOFCONTEXT );
if ( hWEventHook == NULL )
{
return FALSE;
}
return TRUE;
}
It's portion of Installed Hook.
void DlmRemoveEventHook()
{
WaitForSingleObject(hWEventHook, INFINITE);
UnhookWinEvent(hWEventHook);
CoUninitialize();
}
The above is for removing WinEvent Hook.
And this is HookProc Function.
void CALLBACK WinEventProc(
HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime
)
{
IAccessible* pAcc = NULL;
VARIANT varChild;
HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &pAcc, &varChild);
if ((hr == S_OK) && (pAcc != NULL))
{
if (event == EVENT_SYSTEM_MOVESIZESTART)
{
if (!(::GetWindowLong(hPaneWnd, GWL_STYLE) & WS_VISIBLE))
{
::ShowWindow(hPaneWnd, SW_SHOWNOACTIVATE);
::UpdateWindow(hPaneWnd);
}
}
if (event == EVENT_SYSTEM_MOVESIZEEND)
{
if ((::GetWindowLong(hPaneWnd, GWL_STYLE) & WS_VISIBLE))
{
::ShowWindow(hPaneWnd, SW_HIDE);
}
}
pAcc->Release();
}
}
The above codes are all in DLL. And I compiled DLL with x86 Debug method.
And I, of course, compiled client project with x86 Debug method.
My problem is that this hookproc listens for events which caused on only x64 bit apps.(I use Win10 x64).
It cannot listen for winevents of x86 bit apps.
I only know that 32 bit dll can only injected into 32 bit project and same thing on 64 bit dll.
But I think that it's not related to winevent hook.
What's wrong on my code? And Which things do I never know?
I would need to get all elements of webpage displayed in IE from a c++ program.
I tried to see with spy++ but there's only the IEFrame.
So I'm thinking about using the developement tool (F12 in IE), I heard there's a way to automat it, a good idea ?
Thanks
You can get an IHtmlDocument2 reference from an IE's window handle, even out-of-process. This is documented here https://support.microsoft.com/en-us/help/249232/how-to-get-ihtmldocument2-from-a-hwnd, but not really supported by Microsoft.
However it looks like it still works today, I've tested it with a Windows 10 box, and IE is now a frozen app, so not going to change any time soon.
Once you have the proper HWND for Internet Explorer, than you can get the DOM with a code like this. Make sure IE and your program run at the same security level
The DOM is the same as when you're coding IE inprocess (host, activex, etc.), however for security reasons, some things may not work :
void DoSomeDomOperations(HWND hwnd)
{
UINT msg = RegisterWindowMessage(L"WM_HTML_GETOBJECT");
LRESULT result = 0;
SendMessageTimeout(hwnd, msg, NULL, NULL, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&result);
if (!result)
return;
// get main document object
IHTMLDocument2 *doc = NULL;
ObjectFromLresult(result, IID_IHTMLDocument2, NULL, (void**)&doc);
if (!doc)
return;
// get document's url
BSTR url = NULL;
doc->get_URL(&url);
wprintf(L"url:%s\n", url);
SysFreeString(url);
// get body element
IHTMLElement *element = NULL;
doc->get_body(&element);
if (element)
{
BSTR text = NULL;
element->get_innerText(&text);
wprintf(L"text:%s\n", text);
SysFreeString(text);
element->Release();
}
// etc.
// etc.
doc->Release();
}
And here is a full sample console app that scans all current IE processes running:
BOOL CALLBACK GetIEServerWindowProc(HWND hwnd, LPARAM lParam)
{
// enumerate all child windows to find IE's COM server
wchar_t className[100];
GetClassName(hwnd, className, 100);
if (!wcscmp(className, L"Internet Explorer_Server"))
{
*((HWND*)lParam) = hwnd;
return FALSE;
}
return TRUE;
}
HWND GetIEServerWindow(HWND hwnd)
{
HWND serverHwnd = NULL;
EnumChildWindows(hwnd, GetIEServerWindowProc, (LPARAM)&serverHwnd);
return serverHwnd;
}
struct IEServer
{
DWORD processId;
HWND serverHwnd;
};
BOOL CALLBACK GetIEProcessServerWindowProc(HWND hwnd, LPARAM lParam)
{
DWORD processId = ((IEServer*)lParam)->processId;
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
if (pid == processId)
{
HWND serverHwnd = GetIEServerWindow(hwnd);
if (serverHwnd)
{
((IEServer*)lParam)->serverHwnd = serverHwnd;
return FALSE;
}
}
return TRUE;
}
HWND GetIEProcessServerWindow(DWORD processId)
{
IEServer ie = { processId, NULL };
EnumWindows(GetIEProcessServerWindowProc, (LPARAM)&ie);
return ie.serverHwnd;
}
void EnumerateIEProcesses()
{
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (h == INVALID_HANDLE_VALUE)
return;
PROCESSENTRY32 process;
process.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(h, &process))
{
do
{
// we only consider IE processes
if (!wcscmp(process.szExeFile, L"iexplore.exe"))
{
HWND serverHwnd = GetIEProcessServerWindow(process.th32ProcessID);
if (serverHwnd)
{
DoSomeDomOperations(serverHwnd);
}
}
} while (Process32Next(h, &process));
}
CloseHandle(h);
}
int main()
{
CoInitialize(NULL);
EnumerateIEProcesses();
CoUninitialize();
return 0;
}
I use below code to open osk.exe
HINSTANCE Dlg::ExecuteOSK()
{
typedef BOOL (WINAPI * LPFN_IsWow64Process)(HANDLE, PBOOL);
typedef BOOL (WINAPI * LPFN_Wow64DisableWow64FsRedirection)(PVOID *);
typedef BOOL (WINAPI * LPFN_Wow64RevertWow64FsRedirection)(PVOID);
LPFN_IsWow64Process fnIsWow64Process = NULL;
LPFN_Wow64DisableWow64FsRedirection fnWow64DisableWow64FsRedirection = NULL;
LPFN_Wow64RevertWow64FsRedirection fnWow64RevertWow64FsRedirection = NULL;
fnIsWow64Process = (LPFN_IsWow64Process) GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
fnWow64DisableWow64FsRedirection = (LPFN_Wow64DisableWow64FsRedirection) GetProcAddress(GetModuleHandle(TEXT("kernel32")), "Wow64DisableWow64FsRedirection");
fnWow64RevertWow64FsRedirection = (LPFN_Wow64RevertWow64FsRedirection) GetProcAddress(GetModuleHandle(TEXT("kernel32")), "Wow64RevertWow64FsRedirection");
BOOL bIsWow64 = FALSE;
PVOID OldValue = NULL;
HINSTANCE handle;
if (NULL == fnIsWow64Process || NULL == fnWow64DisableWow64FsRedirection || NULL == fnWow64RevertWow64FsRedirection)
{
handle = ::ShellExecute(NULL, "open", "OSK", NULL, NULL, SW_SHOW);
}
else
{
fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
if (TRUE == bIsWow64)
{
fnWow64DisableWow64FsRedirection(&OldValue);
handle = ::ShellExecute(NULL, "open", "OSK", NULL, NULL, SW_SHOW);
fnWow64RevertWow64FsRedirection(OldValue);
}
else
{
handle = ::ShellExecute(NULL, "open", "OSK", NULL, NULL, SW_SHOW);
}
}
return handle;
}
And then, I want to close the osk.exe when user press enter.
BOOL CALLBACK EnumWindowsProc(
_In_ HWND hwnd,
_In_ LPARAM lParam)
{
char name[256];
GetClassName( hwnd, name, sizeof(name) );
if(strcmp(name,"OSKMainClass") == 0)
SendMessage(hwnd, WM_CLOSE , NULL, NULL); //I have tried WM_DESTROY
return TRUE;
}
BOOL Dlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN)
{
int nKey = (int)pMsg->wParam;
switch(nKey)
{
case VK_RETURN:
EnumWindows(EnumWindowsProc, NULL);
}
}
return CBitmapDialog::PreTranslateMessage(pMsg);
}
However, this part of code cannot be used to close osk.exe successful.
I have tried to catch another window, it can be closed successfully.
Does it is an issue on Windows 10?
if you looking to manifest of osk.exe you can view here next - uiAccess="true" this is User Interface Privilege Isolation (UIPI) , also read about similar problem UIAccess in Manifest Files
because osk.exe have uiAccess="true" in manifest it have Mandatory Label\High Mandatory Level in token. but your app, if running under UAC not elevated, usual have Medium Mandatory Level. as result:
A lower-privilege process cannot:
Use SendMessage or PostMessage to application windows running with higher rights. These APIs return success but silently drop the window
message.
but if your application get High Mandatory Level in token - you can close osk.exe by next code:
if (HWND hwnd = FindWindow(L"OSKMainClass", 0))
{
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
so you need have <requestedExecutionLevel level='requireAdministrator' uiAccess='false'/> in manifest or somehow run your application as elevated
I've been searching for some days for a solution to this and I couldn't find to much information.
What I am looking for is a small program to show notification when the mouse clicks in text editable fields like text box in windows forms or browser pages is shown.
The only starting point was this old question from so which leads me to the following:
// Global variable.
HWINEVENTHOOK g_hook;
// Initializes COM and sets up the event hook.
//
void InitializeMSAA()
{
CoInitialize(NULL);
g_hook = SetWinEventHook(
EVENT_SYSTEM_MENUSTART, EVENT_SYSTEM_MENUEND, // Range of events (4 to 5).
NULL, // Handle to DLL.
HandleWinEvent, // The callback.
0, 0, // Process and thread IDs of interest (0 = all)
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); // Flags.
}
// Unhooks the event and shuts down COM.
//
void ShutdownMSAA()
{
UnhookWinEvent(g_hook);
CoUninitialize();
}
// Callback function that handles events.
//
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
LONG idObject, LONG idChild,
DWORD dwEventThread, DWORD dwmsEventTime)
{
IAccessible* pAcc = NULL;
VARIANT varChild;
HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &pAcc, &varChild);
if ((hr == S_OK) && (pAcc != NULL))
{
BSTR bstrName;
pAcc->get_accName(varChild, &bstrName);
if (event == EVENT_SYSTEM_MENUSTART)
{
printf("Begin: ");
}
else if (event == EVENT_SYSTEM_MENUEND)
{
printf("End: ");
}
printf("%S\n", bstrName);
SysFreeString(bstrName);
pAcc->Release();
}
}
Do you have any idea about how I can modify this, in order to get notified when a textbox gets focused ? Any explanation would be greatly appreciated.
The code you provided hooks up on menu open and close events EVENT_SYSTEM_MENUSTART, and EVENT_SYSTEM_MENUEND.
You should try using EVENT_OBJECT_FOCUS (and maybe EVENT_OBJECT_SELECT instead in your SetWinEventHook and HandleWinEvent.
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
LONG idObject, LONG idChild,
DWORD dwEventThread, DWORD dwmsEventTime)
{
IAccessible* pAcc = NULL;
VARIANT varChild;
HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &pAcc, &varChild);
BSTR bstrName;
pAcc->get_accName(varChild, &bstrName);
if (event == EVENT_OBJECT_FOCUS || event == EVENT_OBJECT_SELECTION)
{
printf("%S\n", bstrName);
}
}
void InitializeMSAA()
{
CoInitialize(NULL);
g_hook = SetWinEventHook(
EVENT_OBJECT_FOCUS, EVENT_OBJECT_SELECTION,
NULL, // Handle to DLL.
HandleWinEvent, // The callback.
0, 0, // Process and thread IDs of interest (0 = all)
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); // Flags.
}
Look at the documentation for the list of available events:
SetWinEventHook function
Event Constants