2017-01-28 3 views
3

Я загружаю встроенный браузер в родительское приложение, используя интерфейс IWebBrowser2. Мой код скомпилирован как dll, т. Е. Компонент браузера динамически загружается во время выполнения через интерфейс плагина.Как передать сообщение WM_KEYDOWN экземпляру IWebBrowser2?

Проблема, с которой я сталкиваюсь, заключается в том, что приложения, загружающие мою dll, захватывают определенные сообщения о keydown, и поэтому они не доходят до моего IWebBrowser2 экземпляра.

Поэтому я беру эти сообщения, используя API SetWindowsHookEx() в своей DLL.

Как я могу переслать WM_KEYDOWN или WM_CHAR сообщениям на мой IWebBrowser2 экземпляр таким образом, чтобы они могли, например, использоваться для ввода текста в сфокусированном текстовом поле в браузере?

+0

ли управление браузер имеет фокус клавиатуры, когда е вентиляционные отверстия теряются? – Anders

+0

@ Аnders Я так думаю. Тот факт, что некоторые события проходят, а другие заблокированы, предполагает, что фокус будет получен, правильно? –

+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. Это предотвращает двойное действие сообщений, которые не были перехвачены хостинг приложения
+0

Спасибо, я принял этот ответ потому что он очень близок к тому, что я сейчас работаю, кроме того, что я также использую 'PostMessage()'. Я попытаюсь устранить это, основываясь на вашем 'ProcessHookMessage()' –

0

Кажется, это немного сложнее, чем обычно посылая сообщение:

Во-первых, вам нужно будет получить на месте активного объекта (https://msdn.microsoft.com/en-us/library/windows/desktop/ms691299(v=vs.85).aspx) вашего веб-браузера, а затем вызовите TranslateAccelerator (https://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: Не пробовал этот код, используйте на свой страх и риск :)

+0

Спасибо. У IWebBrowser2, похоже, нет члена 'lpVtbl'. Оглядываясь в Интернете, кажется, что это часть интерфейса C, знаете ли вы, что такое эквивалент C++? –

+0

Ищите немного больше в 'TranslateAccelerator()' Я думаю, что вы, возможно, неправильно поняли вопрос. Я не хочу посылать ключи ускорителя как таковые в свой компонент «IWebBrowser2», а просто обычные нажатия клавиш. Я могу захватить keydown и распечатать символы в консоли отладки, я просто не могу понять, как передать их веб-компоненту. –

+0

'QueryInterface' является частью' IUnknown', если я правильно понял. Исправлен ответ. https://msdn.microsoft.com/en-us/library/ms682521(v=vs.85).aspx – fritzone

1

Похоже, корень проблемы в том, что ваше окно в другом потоке, чем окна хост-приложения, которые могут запутать фокус государство. Вы можете легко попасть в ситуации, когда окно хоста и размещенное окно считают, что у них есть фокус.

Решение этого вопроса заключается в том, чтобы создать окно в том же потоке, что и родительское окно, и если это невозможно (например, из-за модели плагина или из-за того, что плагин запускается в отдельном процессе), используйте AttachThreadInput ,

Я не использовал элемент управления веб-браузером в течение многих лет, но я давно вспоминаю один проект, когда у нас были подобные проблемы, когда мы добавили элемент управления веб-браузера в качестве дочернего элемента окна в другом процессе. Используя AttachThreadInput, было решено множество ошибок. Недостатком было то, что ошибка в любом потоке (например, зависание) эффективно зависает и в потоках. Мы также должны были быть осторожны, чтобы отсоединить резьбы во время разрыва.

+0

. Это звучит правдоподобно. Проблема в том, что Spy ++ мой UI * есть * в том же потоке, что и окно хост-приложения. Мне действительно удается получить клавиатуру вход работает теперь, используя комбинацию 'SetWindowsHookEx()' и 'PostMessage()'. Не получая событий колесика мыши, хотя. Argh! –

 Смежные вопросы

  • Нет связанных вопросов^_^