c ++ - Hvordan overføres WM\_KEYDOWN besked til IWebBrowser2-forekomst?

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg læser en indlejret browser i et forældersprogram ved hjælp af grænsefladen IWebBrowser2. Min kode er kompileret som en dll, dvs. browserkomponenten er dynamisk indlæst i runtime via en plugin-grænseflade.


Problemet jeg har er, at de programmer, der indlæser min dll, indfanger bestemte keydown-meddelelser, og de når derfor ikke min IWebBrowser2 forekomst.


Jeg indfanger derfor disse meddelelser ved hjælp af API'en SetWindowsHookEx() i min dll.


Hvordan kan jeg så videresende WM\_KEYDOWN eller WM\_CHAR beskederne til min IWebBrowser2 forekomst, således at de f.eks. bruges til at indtaste tekst i en fokuseret tekstboks i browseren?

Bedste reference


Jeg tror, ​​at problemet ligger i værtsprogrammets implementering af meddelelseskø, hvor nogle meddelelser håndteres i stedet for at levere, for eksempel at implementere genvejstaster. Da du ikke kan ændre deres kode, lyder det at høre på meddelelseskøen som en rimelig tilgang.


Følgende kodestykke demonstrerer både problem og løsning:


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


I denne kodestykke, hvis du kommenterer InstallHook() opkald, kan du ikke udskrive R og T , fordi disse taster bruges til acceleratorbordet . Men med InstallHook() styrker krogen normal meddelelse kø adfærd og alt fungerer som normalt.


Den foreslåede krogkode har følgende interessepunkter:



  1. Det påvirker kun dit vindue og intet andet

  2. Det fungerer på samme måde som den sædvanlige meddelelseskø ville, i stedet for at messe med SendMessage/PostMessage

  3. Det forhindrer dobbelteffekt af meddelelser, der ikke blev opsnappet af hosting-applikationen


Andre referencer 1


Det lyder som rodproblemet er, at dit vindue er på en anden tråd end værtsprogrammets vindue, som kan forvirre fokustilstanden. Du kan nemt komme ind i situationer, hvor værtsvinduet og det hostede vindue begge tror, ​​at de har fokus .


Løsningen er at skabe dit vindue på samme tråd som overordnede vindue, og hvis det ikke er muligt (f.eks. På grund af pluginmodellen eller fordi plugin'en køres i en separat proces), skal du bruge AttachThreadInput. [18]


Jeg har ikke brugt en webbrowser kontrol i mange år, men jeg husker et projekt for længe siden, hvor vi havde lignende problemer, da vi tilføjede webbrowser kontrol som et barn i et vindue i en anden proces. Ved hjælp af AttachThreadInput solgte der meget bugs. Ulempen var, at en bug i enten tråd (som en hæng) effektivt hænger begge tråde. Vi var også nødt til at være forsigtige med at løsne trådene under nedrivning.

Andre referencer 2


Det ser ud til, det er lidt sværere end det sædvanlige at sende en besked:


For det første skal du få den aktive aktive genstand (https://msdn.microsoft.com/en-us/library/windows/desktop/ms691299(v=vs.85).aspx) til din webbrowser og derefter ring til TranslateAccelerator (https://msdn.microsoft.com/en-us/library/windows/desktop/ms693360(v=vs.85).aspx) på den. [19] [20]


Nogle meget højniveau pseudokode ville se ud:


HRESULT hr;
IOleInPlaceActiveObject* pIOIPAO;

hr = webBrowser2->QueryInterface(webBrowser2,
           &IID\_IOleInPlaceActiveObject, (LPVOID*)&pIOIPAO);

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


hvor msg er meddelelsen (MSG) du bør udfylde i overensstemmelse hermed, og webBrowser2 er din IWebBrowser2.


PS: Prøv ikke denne kode, brug på egen risiko :)