add custom context menu to hosted web browser control - c++

I am hosting a web browser control, and want to provide my own context menu.
Ideally, I want to present my own context menu, that contains the original browser's context menu (with all addins etc.) as a sub menu.
If that's not possible / to tricky, I'd be ok with e.g. normally showing my context menu, and showing the original one when the user presses SHIFT.
Do I need to implement IDocHostUIHandler? if yes, how do I specify a custom context menu, how can I force the original one? How do I get the control to use my implementation?
The control is created as such (error handling omitted):
HRESULT hr=AtlAxCreateControlEx(
L"Shell.Explorer",m_wndWebCtrl.m_hWnd,
NULL,NULL,(IUnknown**)&unk,
IID_IWebBrowser2, NULL); // (IPersistStreamInit*)this);
hr = AtlAdviseSinkMap( this, true);
IUnknownPtr unk;
AtlAxGetControl(m_wndWebCtrl.m_hWnd, &unk);
IWebBrowser2Ptr browser2 = unk;

Yes, you do need to implement IDocHostUIHandler.
Ok, i guess you could intercept right-clicks, keystrokes, and that other message that'll normally display a context menu... But that's probably gonna break badly sooner or later; at very least, i'd expect it to break accessibility.
Once you've intercepted IDocHostUIHandler::ShowContextMenu(), you have the option of returning S_OK to squelch the built-in menu after showing your own. You can use the normal Win32 menu routines for this purpose, a custom control, or even fancy HTML if that's what does it for you. Per the documentation, enough context is provided to allow you to determine what element context is requested for, and what the default context menu would be.
Unfortunately, I know of no way to get a handle to the built-in menu. You could probably fake it by showing your context menu and then returning S_FALSE if the user chose the "original" option, but even then there's no way to attach the resulting menu to an existing popup menu (which really should be gone by the time you return anyway if you're running the modal-loop common to such popups). It is possible to add options to the built-in menus.
You should be able to use GetKeyboardState() to determine the state of the shift key when the menu was requested.
Assuming you only want a subset of the normal browser functionality anyway, you might be better served by just re-implementing the options you want (back, forward, print) and invoking the appropriate command if the user chooses them. Alternately, if you only want normal menus in a very specific scenario (for instance: editing commands in a textarea), use the ShowContextMenu() arguments to identify this and only then return S_FALSE to trigger the default. I've had pretty good luck with this latter technique; after all, they are supposed to be context menus...

I am adding the following information how to "inject" your own DocHostUI implementation into the browser control:
The default way is to implement IOleClientSite, and support the IDocHostUIHandler on the implementing object.
There is a simpler way: In the DocumentComplete handler, query the IHTMLDocumennt for ICustomDoc, which allows you to supply your interface from a standalone object. This allows you to keep whatever IOleClientSite implementation your environment provides.
More details can be found here.

Related

How to get a user input on a MessageBox on C++?

I have an application on C++ (on Windows API) and I ask the user to approve a task using MessageBox. However, as it's a bit sensible task and nobody reads the message, I want to change it to have an input box and the user type "I agree".
Does anybody know a simple way to do that? I find DialogBoxParam() which can do it, but it's overkilling for my needs, can you think on something more simple (or a simple way to use it)?
I found Prompting a user with an input box? [C++] quite similar to my question, but there is no satisfactory answer for me (using another lib is not an option).
You would have to write your own dialog for that. The MessageBox and related APIs do not offer such functionality. You could use the task dialog API (introduced in Vista) to show a dialog box with a button having customised caption. That might be a little better than plain MessageBox with its limited set of buttons.
I'm a little cynical about what you are trying to achieve in any case. If you force the users to type I agree they will ignore the content of the dialog box and type what you ask them to type.
The difference in outcome between your typing dialog and a standard button press dialog is that the user will take longer to get past the dialog, and will dislike your software, but the still not have read the content of the dialog. In other words, the only thing you will achieve by doing this is to hold up the user.
At some point you have to accept that the user takes responsibility for their actions. If you give them a helpful message and they choose to ignore it, ultimately that is on them.

Add support to print & preview HTML in a dialog-based MFC app

I have a two-part question. I need to add supporting for printing to an existing dialog-based MFC project. The document being printed is composed using HTML. I know that I can add HTML-based dialog but how do you add a capability for printing to it?
PS. I need this to be able to set the print page size according to a program's needs.
Inspired by the excellent Marc's Durdin's article, I've done some more spelunking.
There actually appears to be an easier way to supply custom DEVMODE and DEVNAMES and print without using an HTML dialog or a custom IE print template. That, in turn, should allow to set custom printer, paper size, orientation, etc.
I have a playground WebBrowser ActiveX host project in C++, similar to this. I implement IOleCommandTarget interface on my OLE site object (IOleClientSite). Now here's the interesting part, when printing gets invoked (via Ctrl-P or via IDM_PRINT), the browser control calls back the site object as IOleCommandTarget::Exec(&CGID_DocHostCommandHandler, OLECMDID_PRINT2, &VARIANT(VT_UNKNOWN), NULL). The 3rd parameter contains an object which is passed as IUnknown, but when queried for IDispatch it supports all the same __IE_* properties, available via IDispatch::Invoke:
__IE_TemplateUrl (VT_EMPTY)
__IE_ParentHWND (VT_UINT)
__IE_HeaderString (VT_BSTR)
__IE_FooterString (VT_BSTR)
__IE_OutlookHeader (VT_UNKNOWN)
__IE_BaseLineScale (VT_INT)
__IE_uPrintFlags (VT_UINT)
__IE_ContentDocumentUrl (VT_BSTR)
__IE_ContentSelectionUrl (VT_BSTR)
__IE_PrinterCMD_Printer (VT_BSTR)
__IE_PrinterCMD_Device (VT_BSTR)
__IE_PrinterCMD_Port (VT_BSTR)
__IE_BrowseDocument (VT_UNKNOWN)
__IE_TemporaryFiles (VT_ARRAY)
__IE_PrinterCMD_DevNames (VT_I4)
__IE_PrinterCMD_DevMode (VT_I4)
__IE_PrintType (VT_BSTR)
I haven't taken this further yet, but I think it should be possible to alter any of the them and return S_OK from IOleCommandTarget::Exec, and expect the browser control to take in the changes.
I expect it to work in a similar way for IDM_PRINTPREVIEW / OLECMDID_PRINTPREVIEW2, but I haven't verified that yet. I'll play with this a bit more, as time allows. Meanwhile, you're welcome to try it out and share your results.

To customize a messagebox, should I hook the messagebox or use CreateWindowEx to create an imitation?

I've been trying to create a MessageBox with changeable, readable text and customizable buttons, I think making a hook for it would probably be easy for this but I read at MSDN:
"Hooks tend to slow down the system because they increase the amount of processing the system must perform for each message. You should install a hook only when necessary, and remove it as soon as possible."
I'm wondering whether it's really worth it when I can just use CreateWindowEx to make an imitation MessageBox that does all I want. How much do hooks really slow the system down, is it worth it or should I be going with CreateWindowEx instead?
Depending on your target systems, have you considered using one of the new message boxes (Task Dialogs) available starting with Windows Vista?
These allow you to customize the dialog layout of the box with custom buttons, etc. without requiring any hooks or custom windows (as in "create from scratch").
Otherwise I'd probably go with the hooks (you only need them to catch your box, so you could setup them before showing the box, and removing them once you've been successful).

Retrieving Menu in Explorer

As the context menu for the desktop and explorer windows is disabled, I wanted to make a little something to bring back some functionality. My idea was to just list out things in a context menu (copy, paste, new, open with, etc) whenever a user right-clicks one of these windows, and then just simulate the appropriate event in the actual menu (file->new, edit->copy, etc). It wouldn't look perfectly pretty, but it would hopefully allow for the use of right-clicking.
The problem is that I cannot seem to get the actual menu. I opened My Documents and tried going down the child list towards SysListView32, calling GetMenuItemCount each time. Most returned -1, and the only other return value than that was 0.
How am I supposed to get a handle to the (file, edit, view...) menu?
If this isn't possible, is there a way I could simulate the user clicking something on the normal context menu, even if it's disabled?
Also, is there a way of making this work for the desktop? You can get the same type of thing if you view it in the explorer window, so I figured there might be a way.
I'm running Windows XP and any help is appreciated.
As per David Heffernan's comment,
As for your question, you are on the wrong track.
GetMenuItemCount needs an HMENU but you've been feeding it HWND.
That won't work. It also won't work from a different process.
You could possibly write a program that use the shell COM APIs
to show a context menu for a shell item. But your basic problem
is the bone-headed group policy. You really need to get that fixed.
Tell the IT guy that takes the decision that I said he was a fool
and was stopping you doing any useful work. ;-)
This led me onto the path of using the correct alternative method to achieve my goal.

Extend an external application's menu

I am currently writing a plugin for a third party application.
As the plugin framework does not provide any way to access the UI I am now trying to do this manually via the WinAPI.
More specifically, I want to add a custom menu item for my plugin in the "File" menu.
My first attempt using FindWindow to retrieve the handle of the main window and the using GetMenu was not successful, as GetMenu simply returned NULL.
My next step was to use EnumChildWindows and search for a child having the text "&File" (I really don't like this approach as it makes localization quite terrible). However, I only found out the handle of the menu item, but I need the corresponding HMENU to use AppendMenu then, don't I?
Simply casting does not work and results in an "Invalid menu handle".
Is it actually possible to achieve what I am trying? How?
Thanks for your ideas in advance!
It more than likely just isn't a HMENU. Custom menu implementations are common, the one Window provides is dated and inflexible. Compare to Windows Forms' MenuStrip for example.
Of course, that blows a gaping hole in your approach.