Hack fix for Enter key not passed on the ActiveX control in WebBrowserComponent on Windows

On Windows, the WebBrowserComponent uses ActiveXComponent and IWebBrowser2. There is an issue that the ‘Enter’ key is not being handled correctly, i.e. the Windows message never gets to the activeXHookWndProc call. Needless to say, that’s a bit of an issue.

I have a hack fix for it, which I’m not proud of. But I thought in the interest of the Juce community I’d share it here and maybe it can get the proper attention. The fix is:

  • Find the Internet Explorer_Server window handle (a child of the control window created later)
  • insert a hook WINDPROC (similar to wat th ActiveXComponent is doing now)
  • When the Enter message arrives, SetFocus on the control window and pass the event back to the original wndProc

Here’s the code:

static LRESULT CALLBACK browserProcHook(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
	{
		for (auto* ax : ActiveXHelpers::activeXComps)
		{
			if (ax->control != nullptr && ax->control->browserHWND == hwnd)
			{
				if ((message == WM_KEYDOWN || message == WM_KEYUP) && VK_RETURN == wParam)
				{
					SetFocus(ax->control->controlHWND);
				}
				return ax->control->browserWndProc(hwnd, message, wParam, lParam);
			}
		}

		return DefWindowProc(hwnd, message, wParam, lParam);
	}

	// intercepts events going to an activeX control, so we can sneakily use the mouse events
	static LRESULT CALLBACK activeXHookWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
	{
		for (auto* ax : ActiveXHelpers::activeXComps)
		{
			if (ax->control != nullptr && ax->control->controlHWND == hwnd)
			{
				//
				// HACK FIX:
				// The ActiveX component does not receive the Enter key event when using IWebBrowser2
				// https://stackoverflow.com/questions/45919368/iwebbrowser2-doesnt-process-enter-key-in-div-tag
				// https://social.msdn.microsoft.com/Forums/en-US/b682f756-da08-4aee-b764-c7d3c5bf2b33/enter-key-does-not-work-in-iwebbrowser2-control-when-custom-idochostuihandler-implementation-is?forum=vcmfcatl
				// This ugly fix finds the web browser window handle, stores its window procesing function,
				// attaches a hook so it intercepts it's events and when detecting a VK_RETURN sets the 
				// focus to the controlHWND.
				// 
				// I agree this is incredibly ugly. IF a better option is found, please fix accordingly. 
				//
				if (ax->control->browserHWND == nullptr)
				{
					// The browser window is a child to the controlWnd but is not available right away. 
					// It is constructed  after this function has been called several times. 
					std::vector<HWND> children;
					EnumChildWindows(hwnd, EnumWindows, LPARAM(&children));

					for (auto c : children)
					{
						char buffer[256];
						GetClassNameA(c, LPSTR(&buffer), 256);

						if (strcmp(buffer, "Internet Explorer_Server") == 0)
						{
							ax->control->browserHWND = c;
							ax->control->browserWndProc = (WNDPROC)GetWindowLongPtr(c, GWLP_WNDPROC);
							SetWindowLongPtr(c, GWLP_WNDPROC, (LONG_PTR)Pimpl::browserProcHook);
							break;
						}
					}
				}

Ugly, I know. If a better option is available, by all means please fix it.

Thanks!

Nasty! I agree that it seems like an ugly but necessary workaround. How would you propose un-hooking the callback procedure when the window is finished? Seems like you might end up with some code getting called after/during shutdown unless you add some cleanup handling.