Home>

I want to get stylus pen behavior (especially WM_POINTERENTER, WM_POINTERLEAVE) on various browsers of Windows 10 (64bit).

Error message

I tried to get a WM_POINTER * message by setting a global hook in various browser windows using a DLL created with C ++.

Applicable source code

DLL

# include "stdafx.h"

#pragma data_seg (". shareddata")
HWND g_hWnd = 0;
HHOOK hHook = 0;
#pragma data_seg ()
HINSTANCE hInst;
EXPORT_API_ int SetHook (HWND hWnd, DWORD dwThreadId)
{
    hHook = SetWindowsHookEx (WH_GETMESSAGE, HookProc, hInst, dwThreadId);
    if (hHook == NULL) {
        return -1;
    }
    else {
        g_hWnd = hWnd;
    }
    return 0;
}
EXPORT_API_ int ResetHook ()
{
    if (UnhookWindowsHookEx (hHook)! = 0) {
        UnregisterPointerInputTarget (g_hWnd, PT_TOUCH);
    }
    return 0;
}
EXPORT_API_ LRESULT CALLBACK HookProc (int nCode, WPARAM wp, LPARAM lp)
{
    CWPSTRUCT * pcwp;
    if (nCode<0) return CallNextHookEx (0, nCode, wp, lp);
    if (nCode == HC_ACTION) {
        const MSG&msg = * (MSG *) lp;
        if (msg.message == WM_POINTERENTER) {
            SendMessage (g_hWnd, WM_INRANGE, 0, 0);
        }
        else if (msg.message == WM_POINTERLEAVE) {
            SendMessage (g_hWnd, WM_OUTRANGE, 0, 0);
        }
    }
    return CallNextHookEx (hHook, nCode, wp, lp);
}
// entry point
BOOL APIENTRY DllMain (HMODULE hModule,
    DWORD ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        // attach
        hInst = hModule;
        // bSetHook = FALSE;
        break;
    case DLL_PROCESS_DETACH:
        // Detach
        break;
    }
    return TRUE;
}


DLL call

// hook_exe.cpp: Defines the application entry point.
//
#include "stdafx.h"
#include "hook_exe.h"
#define MAX_LOADSTRING 100
// Global variable:
HINSTANCE hInst;// current interface
WCHAR szTitle [MAX_LOADSTRING];// title bar text
WCHAR szWindowClass [MAX_LOADSTRING];// Main window class name
HMODULE hDll;
// Forward the function declarations contained in this code module:
ATOM MyRegisterClass (HINSTANCE hInstance);
BOOL InitInstance (HINSTANCE, int);
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About (HWND, UINT, WPARAM, LPARAM);
int _is_hooked = 0;
// Shared segment
#pragma data_seg (". shareddata")
HHOOK hKeyHook = 0;
HWND g_hWnd = 0;// Window handle to send key code to
#pragma data_seg ()
int APIENTRY wWinMain (_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR lpCmdLine,
                     _In_int nCmdShow)
{
    UNREFERENCED_PARAMETER (hPrevInstance);
    UNREFERENCED_PARAMETER (lpCmdLine);
    // TODO: Insert code here.
    // Initializing global string.
    LoadStringW (hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW (hInstance, IDC_HOOKEXE, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass (hInstance);
    // Perform application initialization:
    if (! InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    HACCEL hAccelTable = LoadAccelerators (hInstance, MAKEINTRESOURCE (IDC_HOOKEXE));
    MSG msg;
    // Main message loop:while (GetMessage (&msg, nullptr, 0, 0))
    {
        if (! TranslateAccelerator (msg.hwnd, hAccelTable,&msg))
        {
            TranslateMessage (&msg);
            DispatchMessage (&msg);
        }
    }
    return (int) msg.wParam;
}

ATOM MyRegisterClass (HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;
    wcex.cbSize = sizeof (WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_HOOKEXE));
    wcex.hCursor = LoadCursor (nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCEW (IDC_HOOKEXE);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon (wcex.hInstance, MAKEINTRESOURCE (IDI_SMALL));
    return RegisterClassExW (&wcex);
}
BOOL InitInstance (HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance;// Store the instance process in a global variable.
   HWND hWnd = CreateWindowW (szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
   if (! hWnd)
   {
      return FALSE;
   }
   RegisterTouchWindow (hWnd, 0);
   ShowWindow (hWnd, nCmdShow);
   UpdateWindow (hWnd);
   return TRUE;
}
LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_TEST:
        _is_hooked = 2;
        InvalidateRect (hWnd, NULL, TRUE);
        break;
    case WM_INRANGE:
        _is_hooked = 3;
        InvalidateRect (hWnd, NULL, TRUE);
        break;
    case WM_OUTRANGE:
        _is_hooked = 4;
        InvalidateRect (hWnd, NULL, TRUE);
        break;
    case WM_HOOKSTART:
        hook_start (hWnd);
        _is_hooked = 1;
        InvalidateRect (hWnd, NULL, TRUE);
        break;
    case WM_HOOKEND:
        // TODO: End of hook
        hook_end ();
        _is_hooked = 0;
        InvalidateRect (hWnd, NULL, TRUE);
        break;
    case WM_COMMAND:
        {
            int wmId = LOWORD (wParam);
            // Parse selected menu:
            switch (wmId)
            {
            case IDM_TEST:
                // Receive test hook start menu
                SendMessage (hWnd, WM_HOOKSTART, 0, 0);
                break;
            case IDM_ABOUT:
                DialogBox (hInst, MAKEINTRESOURCE (IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow (hWnd);
                break;
            default:
                return DefWindowProc (hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_CREATE:
        // Load DLL
        hDll = LoadLibrary (_T ("hook.dll"));
        InvalidateRect (hWnd, NULL, TRUE);
        break;
    case WM_DESTROY:
        // DLL release
        hook_end ();
        FreeLibrary (hDll);
        PostQuitMessage (0);
        break;
    default:
        return DefWindowProc (hWnd, message, wParam, lParam);
    }
    return 0;
}
// Message handler for the version information box.
INT_PTR CALLBACK About (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){
    UNREFERENCED_PARAMETER (lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR) TRUE;
    case WM_COMMAND:
        if (LOWORD (wParam) == IDOK || LOWORD (wParam) == IDCANCEL)
        {
            EndDialog (hDlg, LOWORD (wParam));
            return (INT_PTR) TRUE;
        }
        break;
    }
    return (INT_PTR) FALSE;
}
std :: vector<int>hook_start (HWND hWnd) {
    int ret = 0;
    std :: vector<int>ret_ary;
    FUNC_SetHook lpFunc = (FUNC_SetHook) GetProcAddress (hDll, "SetHook");
    DWORD process_id_ary [1000] = {};
    FindProcessId ("firefox.exe", process_id_ary);
    for (int ii = 0;ii<1000;ii ++) {
        DWORD process_id = process_id_ary [ii];
        if (process_id == NULL) {
            break;
        }
        HWND hwnd_ary [1000] = {};
        GetWindowHandle (process_id, hwnd_ary);
        for (int jj = 0;jj<1000;jj ++) {
            HWND hwnd = hwnd_ary [jj];
            if (hwnd == NULL) {
                break;
            }
            DWORD dwPid = 0;
            DWORD thread_id = GetWindowThreadProcessId (hwnd,&dwPid);
            ret = lpFunc (hWnd, thread_id);
            ret_ary.push_back (ret);
        }
    }
    return ret_ary;
}
int hook_end () {
    // TODO: unhook
    FUNC_ResetHook lpFunc = (FUNC_ResetHook) GetProcAddress (hDll, "ResetHook");
    return 0;
}
void FindProcessId (const char * processname, DWORD * process_id_ary)
{
    HANDLE hProcessSnap;
    PROCESSENTRY32 pe32;
    // DWORD result_ary [1000];
    int idx = 0;
    // Take a snapshot of all processes in the system.
    hProcessSnap = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hProcessSnap) {
        // return (FALSE);
        return;
    }
    pe32.dwSize = sizeof (PROCESSENTRY32);//<----- IMPORTANT
                                          // Retrieve information about the first process,
                                          // and exit if unsuccessful
    if (! Process32First (hProcessSnap,&pe32))
    {
        CloseHandle (hProcessSnap);// clean the snapshot object
        return;
    }
    do
    {
        if (0 == strcmp (processname, _bstr_t (pe32.szExeFile)))
        {
            process_id_ary [idx] = pe32.th32ProcessID;
            idx ++;
        }
    } while (Process32Next (hProcessSnap,&pe32));
    process_id_ary [idx] = NULL;
    CloseHandle (hProcessSnap);
}
void GetWindowHandle (const DWORD TargetID, HWND * hwnd_ary)
{
    int idx = 0;
    HWND hWnd = GetTopWindow (NULL);
    do {
        // if (GetWindowLong (hWnd, GWL_HWNDPARENT)! = 0 ||! IsWindowVisible (hWnd))
            // continue;
        DWORD ProcessID;
        GetWindowThreadProcessId (hWnd,&ProcessID);
        if (TargetID == ProcessID) {
            hwnd_ary [idx] = hWnd;
            idx ++;
            // return hWnd;
        }
    } while ((hWnd = GetNextWindow (hWnd, GW_HWNDNEXT))! = NULL);
    hwnd_ary [idx] = NULL;
}
Tried
  • I checked with Spyxx, but I could not get WM_POINTER messages even if I touched the windows of various browsers (Edge, Firefox) with a stylus pen.
    WM_POINTER messages can be obtained on the VisualStudio window.
  • We thought about getting the pen behavior from JavaScript on the browser side, but abandoned it because there is little information on the behavior.
Supplemental information (FW/tool version etc.)

The method of obtaining the window handle and setting the global hook are also suspicious, but thank you.

Append
  • The reason why you can't get messages with Spyxx was because you were trying to monitor a 64-bit process with 32-bit Spyxx.
    When monitored by Spyxx (amd64), the message of WM_POINTER was confirmed in the browser window.
  • Answer # 1

    Isn't it an error because dwThreadId of another process is specified for SetWindowsHookEx?
    Check if SetWindowsHookEx is successful.

    SetWindowsHookEx

      

    An error may occur if the hMod parameter is NULL and the dwThreadId parameter is zero or specifies the identifier of a thread created by another process.

  • Answer # 2

    When I recreated the project, the hooks were set correctly.
    I think that something was wrong with the project structure.
    I apologize to you for a fuss over.