Intercepting Windows Messages from Webview2 (Edge/Chromium) - c++

I have recently migrated a project of mine to WebView2 and the last part I can't figure out is how to intercept the Windows Messages for the webview. My code is very similar to webview/webview but I was unable to find help on their GitHub.
Previously, I was able to find the hWnd for the webview and use SetWindowSubclass to add my own wndproc to the webview. However, I've used Spy++ and tried SetWindowSubclass on all the windows that showed up there (see below) but none of them had any windows messages in my wndproc other than some window management ones I did not think were useful - The best I got was WM_PARENTYNOTIFY, but I am interested in WM_MOUSEMOVE and WM_NCHITTEST - neither of which I could find.
My goal is to create a borderless, draggable, resizeable WebView2 based app.

The problem is, that the real window that controls and gets all this input is in another process. You just see a window that shows the output in your process.
Look into Spy++. Everything below Chrome_WidgetWin_0 belongs to a new process (MSEDGEWEBVIEW2) and is not part of your process. So you can't subclass such a window with the normal techniques.
So if you want to subclass this window. You need to inject a new DLL into this new process. This DLL might subclass the real window. And this DLL might communicate with you hosting program via any IPC.

Related

CEF closing/resizing issues inside a big application plugin

I'm writing a plugin for a big x64 application in C++. I want the plugin to open a dialog and show a web view of my site.
I'm been able to use WKWebView in macOS and it works well. On Windows I'm evaluating CEF https://bitbucket.org/chromiumembedded/cef (please let me know of any alternative, ideally I would like it to be Webkit-based).
Let's say the application framework that is hosting my plugin has already created a window for my plugin and has it's own message loop, so I can only receive events in a sort of WindowProc. I can also get the HWND of the window.
My implementation is inspired by cefsimple example, because cefclient is way too complicated. I've implemented the subprocess architecture with the external executable and everything works fine until it's rendering the client area of the window. Then I have problems with closing the window (it crashes) and resizing the window interactively (the window frame is resized but the web view in the client area does not resize).
I've tried all possible combinations, but I've run out of ideas. Namely:
If I use CefRunMessageLoop() the web view is rendered correctly but the main application does not process the UI events like close window button. Resize does not work.
If I call CefDoMessageLoopWork() myself once in a while (from WindowProc) the web view is rendered correctly and it processes the close button, but it crashes. Resize does not work.
If I use settings.multi_threaded_message_loop = true the web view is rendered correctly and I can close the window without crash. The destructor of the window calls CefShutdown(). But if I try to reopen the window it crashes! Are CefInitialize and CefShutdown allowed to be called only once?
And resizing still does not work. I don't understand why in the cefsimple example resizing works and in my window it does not work.
Besides message processing issues, probably I'm not closing the browser correctly, any advice? Why is so complicated? WKWebView is so straighforward!
There is no error message, no stack trace, no source code, no OS/CEF version - I doubt this question can be answered.
I can only tell you how to close browser cleanly: call CefShutdown at the right time (see cefclient/cefsimple examples) and do not keep any references to CEF objects when calling shutdown.

Drawing on top of a window of a third party application

Here is a description of my use case:
I have a Windows application that comprised of the main window and it also creates child windows (think of MS Word for instance). What i want to do is draw frames around certain children windows of a given application. Note that when i say window i mean any visible HWND (handle) in the system. This could be a certain window in Excel, or an open pdf document in Abode reader or whatever.
My question is:
Can this be implemented as AN independent API where one would for instance be able to call the following ?
DrawOutline(HWND, OutlineColor, Thikness);
Any pointers as to how to approach this ? Any limitations one might think of? This would not be a plugin to a specific application but a stand alone API. MFC is also OK. Thanx
The Spy++ tool draws frames around any window. Source code for several Spy++ versions is available on the net.

Handling Messages in Console Apps/DLLs in C++ Win32

I would like to have the ability to process Win32 messages in a console app and/or inside a standalone DLL.
I have been able to do it in .NET with the following article and it works great in C# inside a console app and standalone DLL
http://msdn.microsoft.com/en-us/magazine/cc163417.aspx
Is there a way to do the equivalent with C/C++ Win32 APIs? I have tried doing RegisterClassEx(...) and CreateWindow(...) even passing in HWND_MESSAGE to hWndParent but the trouble is that after the "invisible" window is created messages are not being processed probably due to the lack of a message pump.
Where would the message pump go if you had a DLL entry point? I have tried creating another thread in a DLL and put while(GetMesage(..)) there but that did not work either.
Any ideas?
You need a message pump yes. The window also has thread affinity so it needs to be created on the same thread that you're running the message pump on. The basic approach is sound, if you include more code it may become clear what the problem is.
In addition to what Logan Capaldo said, you also have the problem that, as a DLL, you don't know at compile time what kind of process is going to be loading you at runtime.
If you are being loaded by a console application (/SUBSYSTEM:CONSOLE), then creating a hidden window of your own and setting up a message pump on that same thread will work fine (as long as you are the first window created).
If you are being loaded by a windows app (/SUBSYSTEM:WINDOWS) then you might run into problems getting messages. They will be sent to the top-level window in the hierarchy, which you didn't create. You'll need to get the hWnd of the main process and subclass it (if you aren't already).
If you are being loaded by a service, then you aren't going to get window messages at all. You instead need to use the RegisterServiceCtrlHandlerEx Function

How to mix C++ and external buttons on seperate window?

I want to make a C++ button on Start>Run i.e but when I do it will not do signalled event?
Im sorry I have seen that you do not get the question.
Ok basically when you create a button with CreateWindowEx(); I want to do that but put on a different window with SetPArent which I have already done now the button does not work so I need my program to someone get when it is clicked from the Run window as example!
And yes you have it, but it's not making the button is the problem it's getting when it's clicked with my program since it does not belong to it anymore!
You need to apply the ancient but still-supported technique known in Windows as subclassing; it is well explained here (15-years-old article, but still quite valid;-). As this article puts it,
Subclassing is a technique that allows
an application to intercept messages
destined for another window. An
application can augment, monitor, or
modify the default behavior of a
window by intercepting messages meant
for another window.
You'll want "instance subclassing", since you're interested only in a single window (either your new button, or, the one you've SetParented your new button to); if you decide to subclass a window belonging to another process, you'll also need to use the injection techniques explained in the article, such as, injecting your DLL into system processes and watching over events with a WH_CBT hook, and the like. But I think you could keep the button in your own process even though you're SetParenting it to a window belonging to a different process, in which case you can do your instance subclassing entirely within your own process, which is much simpler if feasible.
"Superclassing" is an alternative to "subclassing", also explained in the article, but doesn't seem to offer that many advantages when compared to instance subclassing (though it may compared with global subclassing... but, that's not what you need here anyway).
You'll find other interesting articles on such topics here, here, and here (developing a big, rich C++ library for subclassing -- but, also showing a simpler approach based on hooks which you might prefer). Each article has a pretty difference stance and code examples, so I think that having many to look at may help you find the right mindset and code for your specific needs!
OK, I'll do my very best - as I understand you, you're trying to inject a button into some existing window. That meaning: Your tool creates a button in some window that does not belong to your application. Now, you want to be notified when that button is pressed. Am I correct so far?
To be notified about the button being pressed, you need to get the respective window message, which will only work if you also "inject" a different WndProc into the window. Actually I have no idea how that should work, but I faintly remember functions like GetWindowLong and SetWindowLong. Maybe they will help?
EDIT
I've searched MSDN a little: While you can get the address of a window's WndProc using GetWindowLong, you can not set the WndProc using SetWindowLong on Windows NT/2000/XP (and up I suppose). See here (MSDN).
So what you could do is install a global message hook that intercepts all window messages, filter those for the window you've injected the button into and then find your message. If you have trouble with this, however, I'm the wrong person to ask, because it's been years ago since I've done anything like that, but it would be stuff for a new question.
EDIT 2
Please see Alex Martinellis answer for how to define the hook. I think he's describing the technique I was referring to when I talked about defining global message hooks to intercept the window messages for the window you injected your button into.

DLL plugin that creates a parented window doesn't handle messages correctly

I'm creating a plugin framework, where my application loads a series of plugin DLL's, then creates a new window and pass this new window's handle to the plugin. The plugin can, then, use this handle to create their own GUI.
Everything seems to be working very well. The only problem is that when I press TAB on a plugin widget (An editbox, for example), it doen't jump to another widget. I figured out that some Windows messages are passed, and some others aren't. The WM_KEYDOWN is passed for other keys, because I can type on the editbox, but this message doesn't handle TAB key.
Hope somebody has a hint.
I'm using Borland VCL with CBuilder, but I think I could use any framework under WIN32 to create these plugins, since they never know how their parent windows were created.
It's very complex matter indeed.
When you hit TAB focus jumps to another control only when these controls belong to a Modal Dialog Box. In fact there are some buttons like ESC, LEFT, RIGHT, DOWN, UP, TAB which modal dialog message function treats in a special way. If you want these keys to behave in similar way with modeless dialog box or any other window you should change you message processing function and use IsDialogMessage inside. You'll find more information about IsDialogMessage function in MSDN also to better understand this stuff you may check as well Dialog Boxes section.
And, as was mentioned before, you should set WS_TABSTOP and WS_GROUP styles when needed.
Good luck!
I believe you'll have to take the following steps:
Subclass your edit controls (and other controls as needed).
Capture the WM_KEYDOWN message in your edit control's WndProc.
Check to see if the shift key is currently held down (using GetKeyState or similar).
Call GetWindow, passing in a handle to your edit control and either GW_HWNDPREV or GW_HWNDNEXT depending on whether shift is held down. This will give you the handle to the window that should receive focus.
Call SetFocus and pass in the window handle you got in step 4.
Make sure you handle the case where your edit controls are multiline, as you might want to have a real tab character appear instead of moving to the next control.
Hope that helps!
I believe you suffer from having a different instance of the VCL in each of your dlls and exes. Classes from the dll are not the same as the ones from your exe, even if they are called the same. Also global variables (Application, Screen) are not shared between them. Neither is the memory since they both have their own memory manager.
The solution is to have the dlls and the exe share the VCL library and the memory manager. I am not a BCB developer, but a Delphi developer. In Delphi we would just use the rtl and the vcl as runtime packages. Maybe you could do the BCB equivalent.
A DLL has its own TApplication object.
to provide uniform key handling. when the DLL Loads.
assign the DLL::TApplication to the EXE::TApplication
Be sure to do the reverse on exit.
--
Michael