简体   繁体   中英

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, ie 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 eg be used to enter text in a focused text box within the browser?

It sounds like the root problem is that your window is on a different thread than the host application's window, which can confuse the focus state. You can easily get into situations where the host window and the hosted window both believe they have the focus.

The solution to that is to create your window on the same thread as the parent window, and, if that's not possible (eg, because of the plugin model or because the plugin is run in a separate process), use AttachThreadInput .

I haven't used a web browser control in many years, but I recall one project long ago, where we had similar issues when we added the web browser control as a child of a window in another process. Using AttachThreadInput there solved a lot of bugs. The drawback was that a bug in either thread (like a hang) effectively hangs both threads. We also had to be careful to detach the threads during teardown.

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

It seems, this is a bit trickier than the usual sending a message:

Firstly, you will need to obtain the In place active object ( https://msdn.microsoft.com/en-us/library/windows/desktop/ms691299(v=vs.85).aspx ) of your web browser and then call the TranslateAccelerator ( https://msdn.microsoft.com/en-us/library/windows/desktop/ms693360(v=vs.85).aspx ) on it.

Some very high level pseudocode would look like:

HRESULT hr;
IOleInPlaceActiveObject* pIOIPAO;

hr = webBrowser2->QueryInterface(webBrowser2,
           &IID_IOleInPlaceActiveObject, (LPVOID*)&pIOIPAO);

if (SUCCEEDED(hr))
{
     result = pIOIPAO->lpVtbl->TranslateAccelerator(pIOIPAO, msg);
}

where msg is the message ( MSG ) you should populate accordingly, and webBrowser2 is your IWebBrowser2 .

PS: Didn't try this code, use at your own risk :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM