12 Jul 2014



Merged Folders and undocumented interfaces

Hello, and welcome to my weekly report. I apologize for not having a report last weekend, so this week’s report will be longer than usual.

Reading the issues in the community-powered Shell TODO List got me thinking about some issues that I previously thought were too complex for phase 1. Biggest of all, was the implementation of the CMergedFolder class structure. This set of classes is almost completely undocumented, and I suspect it has multiple features that don’t really fit together.

Among them, the primary purpose of the merged folder is the ability to aggregate the contents of multiple shell folders into one virtual folder that includes items from all of the sources, and also merges the child items of the same name into one virtual item. This allows the start menu to show the items from both the common start menu, and the user start menu.

But the start menu also appears to use a different feature of these merged folders, where it “removes” the Programs item from the list, and takes the contents of the merged Programs to use as the contents of the Programs static menu item (the one that appears below the separator).

Ideally, the implementation should replicate all the interfaces used by Windows, but I started thinking we don’t really need such a complex implementation right now. Even if it’s just rudimentary, almost a skeleton of what it is supposed to be, I decided to write a basic implementation of the CMergedFolder class, which exposes the essential interfaces of a shell folder (that is, IShellFolder and IShellFolder2), and allows the start menu to show the combined view as expected by the users. If (or when) some program decides to use these undocumented interfaces, then we’ll know it’s necessary to implement them, and we’ll have the justification of working on it. For now, having the placeholder for the better implementation feels like the better choice.

In my implementation, I take the items from the two sources, sorted by the default sorting algorithm, and add them to a common list. If two items are equal, then I take the info from one of them, and mark the item as shared, so that later I can tell when to use a merged folder for the contents, if the item happens to be a folder.

The implementation should be working perfectly, and it does work well in Windows, but for some reason that escapes my comprehension, the same exact piece of code that works for normal items, fails to work for the SAME items when they are given to the start menu by the merged folder.

Here is why I’m baffled: The start menu has no problems obtaining the name of the items, no problems obtaining the IExtractIcon interface it needs to get the icon, no problems obtaining a context menu for the items, but this context menu is not actually for the given item. At some point, even though I’m giving the function the same exact data it would use without merging folders, the context menu goes wrong, and contains items belonging either to the parent folder, or to the desktop folder.

While I was trying to debug this, I also tried to implement the “dragging” navigation on menus. That is, where you can press the button down over the menubar, then keep it pressed while you move to a menu item in a submenu, and finally release over the menu item to activate it. This required me to change some of the logic of how I treat clicks, and I also had to disconnect the click code from the WM_COMMAND message from the toolbar, since we don’t want to handle the clicking internally anymore. The code is still not fully complete, but it seems that this change should also solve a few other issues once the navigation is working reliably.

I moved on to the issue where tray icons weren’t showing. I decomposed the issue into three different possible issues: first, applications used to show tray icons before, but weren’t showing them anymore; second, the network shell extension manages both the tray icon, and the network connections virtual folder; and lastly, some icons are managed by a different kind of shell extensions, called Shell Service Objects.

First, I fixed a bug in showing tray icons, regardless of where they came from. It was a typo I made when improving the code structure, where I misinterpreted the contents of a pointer. This had the nice effect of also allowing the network icon to show, which means the network connections shell extension is being loaded.

Last, are the shell service objects. Looking at the existing code from the old explorer, I noticed that these objects are just objects that implement IOleCommandTarget, and receive a specific command used to initialize and shutdown the objects. I verified that this was right, by testing the implementation in windows.

While I was touching the tray icons, I decided to implement support for displaying the tooltip text. To achieve some rudimentary tooltip support, I had to change the way mouse input was handled, so that I could track when the mouse is over a tray icon. I call the support rudimentary, because windows shows the tooltips centered on the icon and above it, while my implementation so far shows them at the cursor.

After the weekend, I went back to the merged folders. It took me many long hours but I finally traced the issue to a function that took a stack pointer “for granted”, which only happened to work for the typical usage of this function because it’s common to keep all the usage of the resulting object within the same function call, but in any other case, it would end up accessing a no longer valid stack pointer that could contain any data.

With the merged folders working, I began investigating how Windows filtered the Programs item from the top. Later when I spoke with Giannis about my lack of luck, he suggested I may be looking in the wrong place. It turns out in Windows, it’s the start menu objects that do the filtering: the CMenuSFToolbar object sends a callback to the CStartMenuCallback object, which returns either S_OK or S_FALSE depending on if it wants the item shown, or hidden. Implementing this callback was quick and painless, since I already had most of the supporting code written for other callbacks, and all I had to do was add a condition to the item enumeration in the menu toolbar.

With this working, it meant the start menu now properly shows the Programs folder in the Programs menu item, and both the top items and the programs folder (and any common folder such as Startup), now shows items from both the user start menu, and the common one.

Now that it was working, all that was left was make it more cross-compatible with Windows, which meant implementing the undocumented IAugmentedShellFolder interface. Luckily, the interface was simpler than I expected, and the information of the methods in the virtual function table, enhanced by the info in the PDB files, allowed me to give those methods a name, and their parameters a type.

I had to do some deduction to understand what each parameter meant, but with this interface, now I can switch between our merged folder, and the windows one, to ensure that the items match.

To wrap this up, here is an image of the taskbar and start menu, as seen in ReactOS in the latest version of the code. Until next week!

Start menu with merged Programs

Comments opened in : https://reactos.org/forum/viewtopic.php?f=2&t=13477

This blog post represents the personal opinion of the author and is not representative of the position of the ReactOS Project.