如何将WM_KEYDOWN消息传递给IWebBrowser2实例?

时间:2017-01-28 14:48:51

标签: c++ windows iwebbrowser2

我正在使用IWebBrowser2界面在父应用程序中加载嵌入式浏览器。我的代码被编译为dll,即浏览器组件在运行时通过插件接口动态加载。

我遇到的问题是加载我的dll的应用程序正在捕获某些keydown消息,因此它们没有到达我的IWebBrowser2实例。

因此,我正在使用我的dll中的SetWindowsHookEx() API捕获这些消息。

如何将WM_KEYDOWNWM_CHAR邮件转发到我的IWebBrowser2个实例,以便他们可以用于在浏览器中的焦点文本框中输入文本?

3 个答案:

答案 0 :(得分:1)

听起来根本问题是你的窗口与主机应用程序的窗口位于不同的线程上,这会混淆焦点状态。您可以轻松地进入主机窗口和托管窗口都认为它们具有焦点的情况。

解决方法是在与父窗口相同的线程上创建窗口,如果这不可能(例如,由于插件模型或插件是在单独的进程中运行),请使用{{ 3}}

我多年没有使用过网络浏览器控件,但我记得很久以前的一个项目,当我们在另一个进程中添加Web浏览器控件作为窗口的子项时,我们遇到了类似的问题。使用AttachThreadInput解决了很多错误。缺点是任一线程中的错误(如挂起)都会有效地挂起两个线程。在拆解过程中我们还必须小心拆卸螺纹。

答案 1 :(得分:1)

我认为问题在于主机应用程序的消息队列实现,其中处理一些消息而不是传递,例如实现热键。由于您无法更改其代码,因此挂钩消息队列听起来像是一种合理的方法。

以下代码段演示了问题和解决方案:

#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;
}

在此代码段中,如果您注释掉InstallHook()来电,则您无法打印 R T ,因为这些键用于加速器表。但是,对于InstallHook(),钩子强制正常的消息队列行为,一切正常。

建议的钩子代码有以下几点:

  1. 它只会影响你的窗户,而不会影响其他任何东西
  2. 它的工作方式与通常的邮件队列相同,而不是弄乱SendMessage / PostMessage
  3. 它可以防止未通过托管应用程序拦截的邮件的双重效果

答案 2 :(得分:0)

看起来,这比通常发送消息有点棘手:

首先,您需要获取网络浏览器的原位有效对象(https://msdn.microsoft.com/en-us/library/windows/desktop/ms691299(v=vs.85).aspx),然后在其上调用TranslateAcceleratorhttps://msdn.microsoft.com/en-us/library/windows/desktop/ms693360(v=vs.85).aspx)。

一些非常高级的伪代码看起来像:

HRESULT hr;
IOleInPlaceActiveObject* pIOIPAO;

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

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

其中msg是消息(MSG),您应该相应地填充,webBrowser2是您的IWebBrowser2

PS:没试过这个代码,使用风险自负:)