C++ 如何将WM_KEYDOWN消息传递给IWebBrowser2实例?
我正在使用C++ 如何将WM_KEYDOWN消息传递给IWebBrowser2实例?,c++,windows,iwebbrowser2,C++,Windows,Iwebbrowser2,我正在使用IWebBrowser2界面在父应用程序中加载嵌入式浏览器。我的代码编译为dll,即浏览器组件在运行时通过插件接口动态加载 我遇到的问题是,加载我的dll的应用程序捕获了某些键控消息,因此它们没有到达我的IWebBrowser2实例 因此,我使用dll中的SetWindowsHookEx()API捕获这些消息 然后,我如何将WM_KEYDOWN或WM_CHAR消息转发到我的IWebBrowser2实例,例如,它们可以用于在浏览器内的聚焦文本框中输入文本?看起来,这比通常发送消息要复杂一
IWebBrowser2
界面在父应用程序中加载嵌入式浏览器。我的代码编译为dll,即浏览器组件在运行时通过插件接口动态加载
我遇到的问题是,加载我的dll的应用程序捕获了某些键控消息,因此它们没有到达我的IWebBrowser2
实例
因此,我使用dll中的SetWindowsHookEx()
API捕获这些消息
然后,我如何将
WM_KEYDOWN
或WM_CHAR
消息转发到我的IWebBrowser2
实例,例如,它们可以用于在浏览器内的聚焦文本框中输入文本?看起来,这比通常发送消息要复杂一些:
首先,您需要获取web浏览器的在位活动对象(),然后在其上调用TranslateAccelerator
()
一些非常高级的伪代码如下所示:
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:未尝试此代码,使用风险自负:)听起来根本问题在于您的窗口与主机应用程序的窗口位于不同的线程上,这可能会混淆焦点状态。您可以很容易地进入宿主窗口和宿主窗口都认为它们具有焦点的情况 解决方法是在与父窗口相同的线程上创建窗口,如果不可能(例如,由于插件模型或插件在单独的进程中运行),请使用
我已经很多年没有使用web浏览器控件了,但我记得很久以前的一个项目,当我们在另一个过程中将web浏览器控件作为窗口的子项添加时,我们遇到了类似的问题。使用AttachThreadInput解决了很多错误。缺点是任一线程中的bug(如挂起)会有效挂起两个线程。我们还必须小心地在拆卸过程中分离线程。我认为问题在于主机应用程序的消息队列实现,其中一些消息是处理的,而不是传递的,例如实现热键。由于您无法更改它们的代码,因此挂钩消息队列听起来是一种合理的方法 以下代码片段演示了问题和解决方案:
#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()
call,您将无法打印R和t,因为这些键用于加速器表。但是,使用InstallHook()
,钩子强制执行正常的消息队列行为,并且一切正常
建议的钩子代码有以下几个要点:
SendMessage
/PostMessage
当事件丢失时,浏览器控件是否有键盘焦点?@Anders我想是的。事实上,一些事件通过了,而另一些事件被阻止了,这意味着注意力得到了集中,对吗?设置一个挂钩是一个相当大的锤子。我认为您最好尝试理解为什么主机应用程序没有将这些消息传递到您的窗口。你还说有些人要来,但有些人不来。你检测到任何模式了吗?@AdrianMcCarthy是的,主机没有传递消息,因为他们窃取它们是为了自己的键盘快捷键(这是一个音频插件,主机是)。有些主机根本不偷钥匙,有些主机只偷几把(例如,玩空格键),有些主机几乎偷了所有钥匙。主机是真的偷了按键(例如,直接在消息循环中),还是因为他们的窗口有焦点而收到按键?钩住并把它们偷回来似乎注定会让用户的情况变得更糟,要么破坏主机的键盘快捷键,要么让一些按键同时做多种事情。谢谢。IWebBrowser2似乎没有成员
lpVtbl
。在Web上浏览,看起来这是C接口的一部分,你知道C++等价的是什么吗?在代码> > TraseEngeltActuffor()/代码>中,我想你可能误解了这个问题。我不希望像这样将加速键发送到我的IWebBrowser2
组件,而只是常规按键。我可以捕获keydown并将字符打印到调试控制台,但我不知道如何将这些字符传递到web组件。QueryInterface
是IUnknown
的一部分,如果我理解正确的话。固定答案。有一段时间没有进行这种低级Windows黑客攻击,但如果我没记错的话,TranslateAccelerator
收到了包含密钥的msg
。请尝试让我知道它是否有效,不再访问此代码所在的旧代码库…:(好的,调整中的代码,如您所述,我能够调用TranslateAccelerator()
。调用成功(非零返回),但基本上什么也不做。在我的IWedBrowser2
实例中,我没有得到WM_CHAR
或WM_KEYDOWN
。感觉我应该调用它,但我不清楚我应该如何获得相关的HWND
这听起来似乎有道理。问题是,根据Spy++的说法,我的UI与主机应用程序在同一线程上在的窗口上。实际上,我已经通过使用SetWindowsHookEx()
和PostMessage()
的组合使键盘输入正常工作。但没有获得鼠标滚轮事件。啊!谢谢,我接受了这个答案,因为它与我现在的工作非常接近,只是我还使用了PostMessage()
。我会调查伊莱米