Every TFrameWindow, such as TMDIChild, as well as every TView, holds a TMenuDescr, usually assigned at construction. A descriptor inherits from TMenu, and as such, encapsulates a HMENU handle. In some applications, the number of handles held can cause handle exhaustion errors.
Since menu descriptors are only temporarily needed during menu merging, there is little need to hold these handles permanently in every instance. To lessen the contention, the descriptors can be created on demand, shared, and/or reimplemented to not use a menu handle.
Discussion: TMDIChild::GetMenu returns non-handle!
Discussion: TMDIChild::GetMenu returns non-handle!
Discussion: 13ef3d9b89
Discussion: Running out of (menu) handles
Wiki: OWLNext_Roadmap_and_Prereleases
Anonymous
In [r7731], I have committed an initial implementation of a redesign, providing menu descriptors on demand. Please review and give feedback on the approach.
The new API removes TFrameWindow::SetMenuDescriptor and makes GetMenuDescriptor virtual with a new signature returning a unique_ptr:
In general, to provide a menu descriptor, clients must subclass TFrameWindow and override this function. However, I was able to make a convenient hack in the base implementation: It looks at Attr.Menu, and if set, uses the TResId to load a descriptor from resources. This is particularly useful for TMDIChild and subclasses, since Attr.Menu is unused for children.
Let's look at how I adapted the Classes example.
First, to implement GetMenuDescriptor for the main window, I had to create a new subclass TClassesMDIFrame, inheriting from TDecoratedMDIFrame (which was previously used as-is). Conveniently, the implementation encapsulated all menu construction, making AssignMenuBitmaps superfluous:
Second, I had to adapt the opening of TMDIChild instances for the window examples. The command handlers benefited nicely from the default implementation of GetMenuDescriptor inherited from TFrameWindow, hence requiring just a simple change from SetMenuDescriptor to AssignMenu:
However, note that we passed more arguments before, i.e. the detailed popup menu group counts. We cannot specify all this detail when calling AssignMenu, which only sets Attr.Menu (a TResId) for later use. Luckily, it can all be specified in the menu resource instead by carefully inserting five menu item separators demarcating the groups:
This makes popup "Item" appear in group 4 as before.
That's pretty much it!
Regarding TView related changes, let's look at the adaptations in the RichEdit example:
Here, I created a small local class to pass on the menu descriptor from the view to the frame window. Nice and simple, since it works for all the different types of views in the example. Maybe this utility class should be put into the library.
That said, if you don't need the dynamic delegation to the view, and the view just loads a static menu from resources, there is an even easier way to implement EvNewView: Just pass the TResId of the menu from the view to a regular TMDIChild:
As TMDIChild inherits the base implementation of GetMenuDescriptor, it will now just create a menu descriptor from the given TResId, whenever needed for main menu merging.
As for changes to the library, there wasn't much to do. The existing implementation uses the returned pointer just as before, oblivious to the implicit ownership management provided by unique_ptr. A couple of places I had to replace duplicate calls to GetMenuDescriptor by the use of a local variable, but other than that the logic remains the same (except for some minor cleanup and code improvement here and there).
Related
Commit: [r7731]
Last edit: Vidar Hasfjord 2025-04-25
Revision [r7733] removes the vestigial member TFrameWindow::MergeModule. Its getter and setter have been declared deprecated and deleted. Same goes for the GetMenuDescr and SetMenuDescr. Documentation has been added.
Related
Commit: [r7733]
Last edit: Vidar Hasfjord 2025-04-25
Further changes:
Related
Commit: [r8157]
Commit: [r8167]