c++windowsiwebbrowser2

How to pass WM_KEYDOWN message to IWebBrowser2 instance?


I'm loading an embedded browser within a parent application using the IWebBrowser2 interface. My code is compiled as a dll, i.e. the browser component is dynamically loaded at runtime via a plugin interface.

The problem I'm having is that applications that load my dll are trapping certain keydown messages, and they are therefore not reaching my IWebBrowser2 instance.

I am therefore capturing these messages using the SetWindowsHookEx() API in my dll.

How can I then forward the WM_KEYDOWN or WM_CHAR messages to my IWebBrowser2 instance such that they could e.g. be used to enter text in a focused text box within the browser?


Solution

  • I believe the problem sits in host application's message queue implementation, where some messages are handled instead of delivering, for example to implement hotkeys. Since you can't change their code, hooking the message queue sounds like a reasonable approach.

    The following code snippet demonstrates both problem and solution:

    #define WINDOW_CLASS _T("StackOverflow_41911104")
    
    HINSTANCE   g_Instance = 0;
    HHOOK       g_Hook = 0;
    HWND        g_TargetWindow = 0;
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    
    HWND CreateMainWindow()
    {
        WNDCLASSEXW wcex;
        wcex.cbSize = sizeof(WNDCLASSEX);
        wcex.style          = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc    = WndProc;
        wcex.cbClsExtra     = 0;
        wcex.cbWndExtra     = 0;
        wcex.hInstance      = g_Instance;
        wcex.hIcon          = nullptr;
        wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
        wcex.lpszMenuName   = nullptr;
        wcex.lpszClassName  = WINDOW_CLASS;
        wcex.hIconSm        = nullptr;
    
        ATOM windowClass    = RegisterClassExW(&wcex);
        HWND mainWindow     = CreateWindowW(WINDOW_CLASS, WINDOW_CLASS, WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400, nullptr, nullptr, g_Instance, nullptr);
    
        g_TargetWindow      = CreateWindow(_T("Edit"), nullptr, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE, 0, 0, 300, 300, mainWindow, (HMENU)1000, g_Instance, nullptr);
    
        return mainWindow;
    }
    
    HACCEL CreateAccelerators()
    {
        ACCEL acceleratorsList[] =
        {
            {FVIRTKEY, 'R', 1000},
            {FVIRTKEY, 'T', 1001},
        };
    
        return CreateAcceleratorTable(acceleratorsList, _countof(acceleratorsList));
    }
    
    void ProcessHookMessage(MSG* a_Message)
    {
        // Only affect our window and its children
        if ((g_TargetWindow != a_Message->hwnd) && !IsChild(g_TargetWindow, a_Message->hwnd))
            return;
    
        // Deliver the message directly
        TranslateMessage(a_Message);
        DispatchMessage(a_Message);
    
        // Do not allow to process this message the second time
        a_Message->message = WM_NULL;
    }
    
    LRESULT CALLBACK Hook_GetMsgProc(int a_Code, WPARAM a_WParam, LPARAM a_LParam)
    {
        if ((HC_ACTION == a_Code) && (PM_REMOVE == a_WParam))
            ProcessHookMessage((MSG*)a_LParam);
    
        return CallNextHookEx(g_Hook, a_Code, a_WParam, a_LParam);
    }
    
    void InstallHook()
    {
        g_Hook = SetWindowsHookEx(WH_GETMESSAGE, Hook_GetMsgProc, g_Instance, GetCurrentThreadId());
    }
    
    int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int)
    {
        g_Instance = hInstance;
    
        HWND mainWindow = CreateMainWindow();
        HACCEL hAccelTable = CreateAccelerators();
        InstallHook();
    
        MSG msg;
        while (GetMessage(&msg, nullptr, 0, 0))
        {
            // The problem lurks here: some messages are handled directly and never reach the target window
            if (TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
                continue;
    
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return 0;
    }
    

    In this code snippet, if you comment out InstallHook() call, you won't be able to print R and T, because these keys are used for accelerator table. However, with InstallHook(), the hook forces normal message queue behavior and everything works as normal.

    The suggested hook code have the following points of interest:

    1. It only affects your window and nothing else
    2. It works the same way as the usual message queue would, instead of messing with SendMessage / PostMessage
    3. It prevents double-effect of messages that were not intercepted by hosting application