Introduction

Classic Shell is a collection of features that were available in older versions of Windows but not anymore. It brings back the classic Start menu that Windows 7 doesn't support, adds a toolbar for Windows Explorer, replaces the copy UI in Vista and Windows 7 with the classic UI from Windows XP, and adds couple more smaller features.

Classic Start Menu

Classic Start Menu is a clone of the original Start menu, which you can find in all versions of Windows from 95 to Vista. It has a variety of advanced features:

Drag and drop to let you organize your applications.

Options to show Favorites, expand Control Panel, etc.

Shows recently used documents. The number of documents to display is customizable.

Translated in 35 languages, including right-to-left support for Arabic and Hebrew.

Does not disable the original Start menu in Windows. You can access it by Shift+Click on the Start button.

Right-click on an item in the menu to delete, rename, sort, or perform other tasks.

Available for 32 and 64-bit Operating Systems.

Has support for skins, including additional third party skins.

Fully customizable in both looks and functionality.

Support for Microsoft’s Active Accessibility

And last but not least – it's free!

If you have used the Start menu in older versions of Windows, you’ll feel right at home:

Classic Explorer

Classic Explorer is a plug-in for Windows Explorer that:

Adds a toolbar to Explorer for some common operations (Go to parent folder, Cut, Copy, Paste, Delete, Properties, Email). More buttons can be added manually.

Replaces the copy UI in Vista and Windows 7 with the more user-friendly “classic” version similar to Windows XP.

Handles Alt+Enter in the folder panel of Windows Explorer, and shows the properties of the selected folder.

Has options for customizing the folder panel to look more like the Windows XP version or to not fade the expand buttons.

Can show the free disk space and the total file size in the status bar.

Toolbar for Windows Explorer

Windows Explorer in Vista doesn’t have a toolbar like the one in Windows XP. If you want to go to the parent folder, you have to use the breadcrumbs bar. If you want to copy or delete a file with the mouse, you have to right-click and look for the Delete command. The right-click menu gets bigger and bigger the more shell extensions you have installed, and finding the right command can take a while.

To solve the problem, the Classic Explorer plug-in adds a new toolbar:

Hold the Control key when clicking the Up button to open the parent folder in a new Explorer window.

Hold the Shift key when clicking the Delete button to permanently delete a file.

Additional Up Button

Some people have asked if I can make a small Up button and put it next to the Back/Forward buttons in the title bar of Explorer. If Up is the only button you need from the toolbar, this will save you screen space:

Right-click on the button to bring up the Classic Explorer settings.

New Copy UI

In Vista, when you copy files and there is a conflict, you are presented with this:

What’s wrong with it?

Well, for starters, it is half a screen full of text that you have to read. Also, it is not immediately clear what parts of it are clickable. You have to move the mouse around to discover the UI, like in a Lucas Arts adventure game. And finally, the keyboard usability is awful. To tell it “yes, I know what I’m doing, I want to overwrite all files”, you have to press Alt+D, up, up, up, Space! It is harder than performing the Akuma Kara Demon move in Street Fighter 3. There is a time and a place for that stuff, and copying files is not it.

The Classic Explorer plug-in brings back the simpler dialog box from Windows XP:

It is immediately clear what is clickable (clue – the buttons at the bottom), there is easy keyboard navigation (press Y for “Yes”, A to copy all files), and you can still see which file is newer and which is larger. And of course, just like in Windows XP, holding down Shift while clicking on the No button means "No to All" (or just press Shift+N).

If you click on More…, you will get the original dialog from Windows. From there, you will see all the details, and you’ll get an extra option to “Copy, but keep both files”.

Important note: Only the UI is replaced. The underlying system that does the actual copying is not affected.

Alt+Enter in the Folder Panel

Alt+Enter is a universal shortcut across Windows to bring up the properties of the selection. But in Vista and Windows 7, it doesn’t work in the left panel that shows the folders. It works fine on the right where the files are. This is broken compared to Windows XP where Alt+Enter works in both places.

To solve the problem, the Classic Explorer plug-in detects when you press Alt+Enter, and shows the properties for the currently selected folder.

Status Bar

In Windows 7, the status bar in Explorer doesn't show the free disk space and the size of the selected files. Classic Explorer fixes that:

When no files are selected, the total size of all the files in the folder is shown.

Alternative Start Menu Implementations

Before I decided to develop my own Start menu, I tried to look for alternatives. I couldn’t find something that is free, supports reordering of programs, shows the recent documents, etc. This article will not be complete without listing the “competition” with some (hopefully objective) pros and cons:

CSMenu is free, and provides the basic functionality – open the Programs menu, click on a program to run. It lacks advanced functions like keyboard navigation, drag/drop, recent documents, customizability, etc. Also, the localization is not quite right – for example, Help and Support, Calculator, etc., are not localized, and there is no support for right-to-left languages.

Update: Looks like development has stopped on this project and it has been abandoned by the authors.

Classic Windows Start Menu is also free, and only has basic functionality. No drag/drop, very few languages are supported, and doesn’t quite work correctly when the taskbar is not at the bottom.

Update: The latest beta supports drag/drop, and better handles the different positions of the taskbar. There is still room for improvement (drag/drop is a bit buggy, Unicode support is lacking), but looks like the project is active, and effort is being made to fix the existing problems.

Classic Start Menu, while not free (20 bucks), has a variety of advanced features. You can use drag/drop to rearrange your menus (I found it to be a bit buggy), and it has two skins to choose from (Aero and Classic). On the negative side, there is almost no keyboard navigation because there is a search box that steals all typed characters. There is some sort of shortcut system using the numeric keys, but I couldn’t get it to work reliably. There is no proper "Recent documents" menu. Also, the localization is a bit off, and right-to-left support is a bit lacking.

Vista Start Menu is another version done by the same guy as Classic Start Menu above. It tries to be much fancier, but the UI was way too busy for my taste. I gave it a quick look and found the keyboard shortcut system to work a bit more reliably. There is a free version, and a PRO version (20 bucks) that adds some more customization features. I would rate this one the highest of the bunch because of its features, if you are into this sort of UI.

Seven Classic Start is probably the worst of the bunch. It is the most expensive (25 bucks!) and only offers basic functionality. Even though it is advertised as “Complete with everything that makes the original Start menu beloved by so many users”, there is no drag and drop, expanding Control Panel, Recent documents, localization, or right-to-left support.

P.S. When you try to uninstall the trial, you get an offer to use it for free if you agree to try some other software as well.

If you are not a programmer, you can stop reading now. Just download the binaries and install them.

The rest of the article discusses how the different features are implemented.

I’ll try to keep it short and not repeat information that is readily available in other CodeProject articles or the MSDN.

How Classic Start Menu Works

So, how do we create a Start menu replacement? Let’s start from the beginning.

Implementing the Menu

Just like with the original Start menu, our implementation uses a vertical toolbar control. This gives us some advantages over a regular menu:

the toolbar supports images directly without the need to fiddle with owner-drawn menus

the toolbar can be placed in a pager control to make it scrollable if the items don’t fit on screen

you can right-click on a toolbar but not on a menu

the toolbar offers some drag/drop functionality that will come handy later

Of course, there are downsides. We need to simulate parts of the menu behavior ourselves. We need to take care of opening a sub-menu when the mouse hovers over an item, handle focus, activation, and Z-order issues, etc.

There are some problems with the toolbar control that I haven't solved yet:

The toolbar is not compatible with glass. When it draws itself, it messes up the alpha channel. This makes it impossible to make a transparent menu.

The toolbar doesn't handle the WM_PRINTCLIENT message correctly when used in right-to-left mode. Makes things like AnimateWindow difficult.

Having multiple toolbars in one window to simulate a multi-column menu confuses screen readers like JAWS.

Because of these problems, I'm seriously considering creating my own control for the next version of Classic Shell.

Replacing the Standard Menu

The next problem we need to solve is how to show our menu instead of the built-in standard menu. There are two ways for the user to activate the menu – by pressing the Win key, and by clicking on the Start button (the orb). After doing some sniffing around with Spy++, you will notice a few things:

The taskbar is a window of class Shell_TrayWnd and no name.

The Start button is a window in the same thread as the taskbar. The text of the button is localized, and depends on the current OS language.

When you click on the Start button, it receives WM_LBUTTONDOWN just like any other window.

When you click on the area around the start button, the taskbar receives the WM_NCLBUTTONDOWN message.

There is also a window named Program Manager and class Progman. It is in the same process as the taskbar, but in another thread.

When you press the Win button, the Progman window receives a message WM_SYSCOMMAND with wParam = SC_TASKLIST.

So the task is simple – install WH_GETMESSAGE hooks for the threads of the Start button and the Progman window. Intercept the messages that activate the Start menu, and show our start menu instead.

The hooks can be installed by a shell extension that is auto-loaded by Explorer or by an external EXE. I chose an external EXE for a few reasons. First, it is simpler since it doesn’t require registering a shell extension with all the hassles that come with it. Second, when the EXE is killed, it cleans up its hooks automatically and the Explorer is restored to its original state. And finally, when Explorer is restarted, the EXE gets a message TaskbarCreated and it can re-install the hooks.

Of course, there are more details to consider. For example, when the mouse is over the start button, a tooltip pops up with the text “Start”. We don’t want this text to show up when our Start menu is opened. So while the menu is visible, temporarily disable the tooltip.

The next thing to keep in mind is drag and drop. When the user drags a program to add to the Start menu, he will hover over the Start button and expect the menu to open. One way to support this is to replace the IDropTarget object associated with the Start button. We get the old drop target from the OleDropTargetInterface property of the Start button window, set a new drop target, and when it’s time to unhook Explorer, we restore the original drop target:

Icons

The Start menu needs to display icons for all the shell items, as well as for command items like Run, Shutdown, etc.

For shell items, we can get the icons using the IExtractIcon interface. First call IExtractIcon::GetIconLocation, then pass the received location to IExtractIcon::Extract. If Extract returns S_FALSE, you have to additionally call the ExtractIconEx function.

Extracting icons can be expensive because the containing exe or DLL needs to be loaded in memory first. The Control Panel is the worst offender because it contains a long list of items, each in its own file, and they are all needed at the same time.

For the command items, we can extract the icons from shell32.dll. It is already loaded in the Explorer process:

Programs is # 326

Settings is # 330

Run is # 328

This is a bit hacky because the icon resources can certainly change between Windows versions. I have verified that the icons we need have the same resource IDs for Vista and Windows 7. We’ll see if the next version of Windows (or the next Service Pack) breaks that. The documented way to access the shell icons is with the SHGetStockIconInfo function. Unfortunately, it doesn’t give us all the icons we need. Also, the interesting icons like SIID_STFIND and SIID_STRUN are marked as “Do not use” in the documentation. In the latest online docs, they are not even listed! So for now, looking at shell32.dll with a resource viewer seems to be the only workable solution (short of drawing our own icons).

It is possible for multiple items to share the same icon – for example, all text files use the same text file icon. To reuse icons, the Start menu has a global cache CIconManager that associates each icon with a key value that is a hash of the icon’s location and index. To improve performance, the icon manager starts a background thread that crawls through the shell and pre-caches the icons. So, by the time you need to open the Control Panel, all icons should already be loaded.

The Programs Menu

The Programs menu is a combination of two folders – one for the current user and one shared by all users. The Start menu should combine the two folder trees and present them as one tree. Items with identical names should be combined into one item.

It is important to compare the internal names of the items as returned by GetDisplayNameOf(SHGDN_INFOLDER|SHGDN_FORPARSING) but show the normal display name GetDisplayNameOf(SHGDN_INFOLDER|SHGDN_NORMAL) in the UI. Some items can have the same display name, but should be treated as separate items - for example, foo.exe and foo.exe.lnk. The opposite is also true - some items can have the same internal name but different display names - for example, the display name of the user's Startup folder will be translated to the current language, but the common Startup folder will not be. Even though their display names differ, the two folders should be combined.

Why not use IShellMenu?

The shell has support for displaying a menu for a given shell folder. You create an instance of IShellMenu, give it IShellFolder, and it works quite great. The icons show up properly, drag and drop works, everything is rosy, everybody is happy. Not quite.

There are a few downsides for which I was unable to find a workaround even after a week of trying. First is that we want to show a combination of two shell folders – one for the user’s programs and one for the common programs. I created a virtual IShellFolder that presented two folders as one. I had to use my own private PIDL structure, and the IShellMenu didn’t quite like that. It assumes the PIDLs it gets are compatible with the shell file system. Another problem is that the Start menu is much more than just the Programs menu. It has recent documents, system commands, separators, etc. Shoehorning that into a single IShellFolder hierarchy turned out to be an impossible task.

So, I gave up on IShellMenu and decided to implement the menu control from scratch. On the bright side, since we are implementing the menu ourselves, the possibilities for tweaking the look and feel are endless.

Recent Documents

Windows stores links to the recently accessed documents in the %APPDATA%\Microsoft\Windows\Recent folder. Not every link in the folder points to a document though. Some point to recently accessed folders. So, we need to filter those out. How do we distinguish between links to files and links to folders? Get an IShellLink object, convert it to IPersistFile, load the contents of the link with IPersistFile::Load, get the target path and attributes with IShellLink::GetPath, and check if it is a file or a folder.

Also, there can be quite a few items, and enumerating them using the IShellFolder API can be very slow. In my case, it was taking about 5-8 seconds. I found it is much faster to go through them using the FindNextFile API, sort them by time, and pick the first 15 documents.

System Commands

Besides the Programs menu, the Start menu contains many items that execute specific commands. We should try to implement as many as possible. Here’s the current list:

This one is a bit trickier. Its implementation is part of Windows Mail. The command line is %ProgramFiles%\Windows Mail\wab.exe /find.

Drag and Drop

This is one of the most useful features of the Start menu. It lets the user rearrange the installed programs and make them easier to find.

How is it done? When the user drags a menu item, the toolbar sends the TBN_DRAGOUT notification. We have to create a data object and a drop source and run the SHDoDragDrop function. We can get the data object from IShellFolder::GetUIObjectOf(IID_IDataObject). The drop source has nothing special about it.

To enable dropping items on the menu, we use the TBSTYLE_REGISTERDROP style. This causes the toolbar to send the TBN_GETOBJECT notification to request an IDropTarget interface from us and use it during drag/drop. Our IDropTarget must take care of things such as:

Showing the insert mark where an item can be dropped (use the TB_SETINSERTMARK message).

Detect when the mouse hovers over a sub-menu and open it after some delay.

Reorder the menu when an item is dragged and dropped into the same menu.

Move/copy the item if it is dropped in a different menu. For that, we can get IDropTarget from the target folder, and manually call IDropTarget::DragOver and IDropTarget::Drop to get it to perform the operation.

Usually, the drop operation is asynchronous and happens in a background thread. This is not good for us because we want to know immediately when an item is moved so we can update the menu. The simplest solution is to disable asynchronous operations (that's what Windows' own Start menu does):

This is not a big problem because the Start menu contains mostly shortcuts and they are fast to copy. I can think of two alternative solutions. Ideally, we can add an IAdviseSink to the data object and be notified when the drag operation is complete. Unfortunately, the shell data objects don't support such notifications (IDataObject::DAdvise returns OLE_E_ADVISENOTSUPPORTED). The other way is to install a directory watcher with FindFirstChangeNotification. This will work, but is way too complex for such little benefit.

Context Menu

The standard Start menu has another feature worth supporting. The user can right-click on an item, get its shell context menu, and click on commands. It is quite tricky to host the context menu correctly; fortunately, Raymond Chen has covered that in great detail on his blog: The Old New Thing: How to host an IContextMenu.

Of course, there is still work to do because we want some custom behavior. We want special handling for the “rename”, “delete”, and “link” commands. For “rename”, we are going to show our own renaming dialog box because the default implementation does nothing. For “delete” and “link”, we want to refresh the menu after the operation finishes.

Skins

Version 0.9.8 of Classic Shell supports skins for the Start menu. The skin determines the background image for the main menu and settings such as font size, color, glow, and more.

Every skin is a resource DLL that contains the skin description (a text file) and the bitmaps used to construct the menu image. Read more about skins here: How to Skin a Classic Start Menu.

Accessibility

Since we are using a toolbar instead of a menu, screen readers like Narrator say "toolbar with 11 buttons" instead of "menu with 11 items". To fix that, we have to create our own implementation of IAccessible and implement get_accRole to return ROLE_SYSTEM_MENUPOPUP, ROLE_SYSTEM_MENUITEM, and ROLE_SYSTEM_SEPARATOR for the individual accessible pieces.

Check out the Accessibility.cpp file for the complete implementation.

How Classic Explorer Works

Classic Explorer is a single DLL that registers as three different shell extensions – a drag and drop handler, a browser helper object, and a desk band. The drag and drop handler is used for the copy UI, the browser helper object hooks into the folder tree view, and the desk band adds a toolbar to the Explorer.

Classic Copy

Classic Copy replaces the dialog box that Explorer shows when there is a conflict during a copy/move operation. It uses a drag and drop handler to ensure the DLL is loaded when a copy operation is in progress. Once loaded, the DLL installs WH_CBT hooks into all threads of the process and waits for a dialog box with a specific title to be created.

When Explorer creates a dialog box with the title "Copy File" or "Move File", we hide it before it has a chance to be displayed and we show our own dialog box. After the user has picked the selection (Yes, No, Cancel, etc.), we have to communicate that selection to the original dialog box.

The original dialog is not an ordinary window but rather a task dialog box. This makes it very difficult to control programmatically because instead of regular buttons with control IDs, it uses windowless buttons that are painted directly on the dialog's surface. I have found the only way to control the task dialog is through the Active Accessibility API. We locate the IAccessible interface for each item, find the important buttons by their label, and activate the right button using IAccessible::accDoDefaultAction. It would have been so much easier if the buttons had another feature to distinguish them besides their label, but I couldn’t find any. The labels, of course, depend on the currently selected language. So, we can’t just look for a button called “Don’t Copy”. We have to find the text for the current language. The text is located in the string table of the shell32.dll.mui file. For example, “Don’t Copy” is string 13606, "Move" is 13610, etc.

So there you have it – hide the original dialog, show our own instead, and then use the Accessibility API to control the original dialog based on the user selection. It’s that simple.

Alt+Enter in the Folder View

This was easy. We subclass the tree view and listen for the WM_SYSKEYDOWN message with wParam = VK_RETURN. When the message comes, we find the selected item. The user data stored in the tree control item is the PIDL of the corresponding shell item. We need to go up the tree hierarchy and assemble a full PIDL. Finally, we call ShellExecuteEx with the “properties” verb to display the properties:

Tweaking the Folder View Look and Feel

Since we are subclassing the folder view for the Alt+Enter feature, let's see if we can get it to look like the folder view in Windows XP:

To get the classic look, add the TVS_HASLINES style, remove the TVS_SINGLEEXPAND and TVS_TRACKSELECT styles, and remove the TVS_EX_FADEINOUTEXPANDOS and TVS_EX_AUTOHSCROLL extended styles.

To get the simple look, add the TVS_SINGLEEXPAND and TVS_TRACKSELECT styles, remove the TVS_HASLINES style, and remove the TVS_EX_FADEINOUTEXPANDOS and TVS_EX_AUTOHSCROLL extended styles.

Or, if you simply don't want the buttons to fade, just remove the TVS_EX_FADEINOUTEXPANDOS extended style.

Adding the Toolbar

The Classic Explorer Bar is a simple desk band with a toolbar inside. You can find out how to create desk bands from this article: Internet Explorer Toolbar (Deskband) Tutorial. There are two tricky pieces that I needed to discover by myself.

First, I want the desk band to be hosted in Windows Explorer but not Internet Explorer. You do that by checking the exe name in DllMain, and returning FALSE if the exe is named “iexplore.exe”. Why can’t we just return TRUE only for “explorer.exe”? Our DLL may need to be loaded by other executables like regsvr32.exe or msiexec.exe. So, it’s better to exclude iexplore.exe specifically, than to provide a specific list of allowed hosts.

Second, there appears to be a bug in Windows 7. Each desk band is forced to be on its own row in Windows Explorer. The Explorer forces the RBBS_BREAK style for every band. To combat that, the solution is to subclass the rebar control and force a specific value for the RBBS_BREAK style. We remember the state when the band is hidden (IDockingWindow::ShowDW is called with FALSE) so we can restore it correctly the next time Explorer is opened or the band is shown. All this is specific to Windows 7. None of that hackery is needed for Vista because it restores the band states correctly.

Now that we have a toolbar, we have to write the code for each button. The Up button navigates to the parent folder:

The Cut, Copy, Paste, Delete, and other buttons simply send WM_COMMAND messages to Explorer. You can discover the command codes with Spy++. The command code is different if you want to operate on a folder in the tree view or a file in the list view:

Adding Up a Button in the Title Bar

This turned out to be much easier than expected. The title bar of Explorer contains a rebar control. All we need to do is make a small toolbar with one button and add it as a rebar band. We need to replace the entire rendering of the toolbar for two reasons. First, for Aero modes, the background needs to be cleared to make it transparent. And second, we want our icon to take the whole space of the button, with no button border. That's done by handling the NM_CUSTOMDRAW notification in the toolbar's parent.

The Status Bar

Windows 7 doesn't show the total size of the selected files in the status bar. Instead, you have to look at the Details pane, but it is limited to 15 files. If you select more than 15 files, you have to press "More details" to get the total size. The size disappears as soon as you select one more file. This is pretty annoying. Let's try to fix the status bar then.

First, we locate the status bar control using IShellBrowser::GetControlWindow(FCW_STATUS) and subclass it. When Explorer wants to update the text "10 items selected", it sends the message SB_SETTEXT with LOWORD(wParam)=0. We catch that message and append the total disk size to the end, so it becomes "10 items selected (Disk free space: 358 GB)". Then, we calculate the total size of the selection and show it in the status bar's second part:

Of course, calculating the size every time the selection changes can be expensive. The selection can change frequently if you are selecting multiple files by holding down the Shift+Down arrow in a large folder. So, when the selection changes, we just start a timer and do the heavy calculation after 10 ms. If the selection changes in the mean time, it will restart the timer.

Note: Windows 7 has an annoying bug. When you open a new Explorer window, often the status bar has only one part. If you resize the window, the status bar resets to its correct 3-part state. To workaround the problem, the code resizes the window by one pixel, then back to the original size. Sometimes, even that is not enough. If the code detects that the status bar still has one part, it sends the Refresh command. This usually fixes the problem.

Scroll Problems in Explorer

Another bug in Windows 7 Explorer involves expanding a folder in the navigation pane. If you select a folder that hasn't been expanded before, then expand it, the navigation pane will scroll all the way up to the top, then all the way down to the selected item. The result is that the selected item ends up near the bottom (not near the top as you would expect). What happens under the covers is that Explorer sends two TVM_ENSUREVISIBLE messages - first for the top item and second for the selected item. I have no clue what requires the first message to be sent. Maybe there is some weird situation where the scrolling to the top actually helps, but I haven't seen any.

To counter this bug, we can make the tree control ignore the first message:

Basically, it ignores TVM_ENSUREVISIBLE for the top level item if it is not selected. The assumption is that if Explorer really wants to focus on the top item, it will select it first.

Rant: First, there is the RBBS_BREAK bug, then status bar problems, then this. What's happening here? Did Microsoft hire an intern to finish Explorer in Windows 7? Was QA sleeping on the job? Explorer is the most commonly used application for Windows. It deserves more attention than that! I remember when Windows XP came out, the only bug in Explorer was that it didn't correctly redraw the corner between the horizontal and vertical scroll bars. Ah, the good old days. Now, I'm crossing my fingers and waiting for Win 7 SP1 :)

Localization

Windows Vista and Windows 7 support 35 languages (if you have the Ultimate version, you can install more than one). We want to localize the UI to make it integrate seamlessly into the OS.

The first task is to localize the text. Classic Shell has two files that contain localization data: ExplorerL10N.ini has the text for Classic Explorer, and StartMenuL10N.ini has the text for the Start menu. Each language has its own section:

The name of the section ([en-US], [bg-BG], etc.) comes from the GetThreadPreferredUILanguages function. It returns a list of language names in the order of preference. To get a localized string, call the FindTranslation function:

If a string is not found in the first preferred language, the next language is used. If none of the languages is recognized, the [default] section is used. And, if the text is nowhere to be found, FindTranslation returns the def parameter.

Part of the localization is to support right-to-left languages like Arabic and Hebrew. We check if the language is RTL with this code:

The dialog boxes need to be mirrored. This is done by having a separate dialog resource with the RTL style.

The Start menu needs to be mirrored. So, we set the RTL style for the menu container window. The toolbar is smart enough to reverse its orientation.

Since the toolbars are reversed, all icons are mirrored. We don’t want that, so we need to set the ILC_MIRROR flag for the toolbar’s image list to mirror the icons back to normal.

Context menus also need to be mirrored. This is done by passing the TPM_LAYOUTRTL flag to TrackPopupMenu.

AnimateWindow has some problems with RTL layout. It uses the WM_PRINT and WM_PRINTCLIENT messages, and they don’t play nice with RTL. Unfortunately, I haven’t found a fix, so for now, the menu animation is disabled for RTL languages

The Installer

The installer for Classic Shell is split into three projects. ClassicShellSetup32 builds a 32-bit MSI file, ClassicShellSetup64 builds a 64-bit MSI file. The 64-bit package contains both 32-bit and 64-bit versions of the ClassicExplorer.dll because you can run a 32-bit Explorer on 64-bit Windows.

ClassicShellSetup builds an exe that combines the two MSI files into one convenient package. Certainly, it is possible to distribute two separate MSI files, but the exe gives us some more advantages besides just being a convenient package:

The EXE can check if we are running on older version of Windows and complain about it.

The EXE can check if the OS is 32-bit or 64-bit. and will run the correct MSI.

After installation, we want to launch the Start menu exe. It is not possible to do so from the MSI package because it runs in an elevated environment and the Start menu needs to run as the current user.

The EXE can have a nice non-default icon.

Building the Solution

The included solution is for Visual Studio 2008. It needs to be built in two steps.

First, do a full build for the Setup|Win32 configuration. That will build the 32-bit modules and create the 32-bit MSI file. Second, do a full build for the Setup|x64 configuration. That will build the 64-bit modules, create the 64-bit MSI file, and build the final package ClassicShellSetup\Release\ClassicShellSetup.exe. Run the EXE to install.

Important note: The first time you build the setup projects, you may get some warning about adding dependencies for some system files like OLEACC.dll and UxTheme.dll. Make sure all Detected Dependencies are excluded, and build again. If you leave some dependencies like dwmapi.dll, they will be installed on the target machine. If it happens to be a different version of Windows (like 32-bit vs. 64-bit, or Vista vs. Windows 7), the Classic Shell will fail to start!

And Finally, Some Tips for Developers

Debugging the Start menu will be easier if you disable the #define HOOK_EXPLORER line in ClassicStartMenu.cpp. Then, the menu will run in its own process, and will not interfere with the Explorer process. Some of the functionality will be disabled, but it is good enough for most purposes.

Building Classic Explorer for Release or Debug configuration will register it as a shell extension. The next time you open an Explorer window, it will be activated. You can attach a debugger to the explorer.exe process and debug the shell extension.

During development, it is inconvenient that the DLLs are loaded by Explorer and can't be rebuilt. So, we need a way to restart Explorer. You may restart Explorer by killing it in Task Manager and running explorer.exe again. But, an easier way to restart Explorer is to make it crash. In Release and Debug, if you hold down Shift and click on the Settings button, it will force a crash in Explorer.

Another workaround for restarting Explorer is available if you develop on 64-bit Vista. You can run the 32-bit Explorer and close it when you are done. Run C:\Windows\SysWOW64\Explorer.exe directly from Visual Studio. This doesn't work on Windows 7. The 32-bit Explorer launches the 64-bit version, and exits immediately.

Room for Improvement

Of course, there is always room for improvement. Here’s what I have planned for the future:

Better accessibility for the Start menu. It is half-way there, but there is still work to do.

The Start menu may track the recently used programs and provide an easier access to them.

Better support for Glass in the Start menu, including behind the menu items and in the sub-menus.

The Start menu should use the system sounds for opening a sub-menu or activating a menu item.

Signing off

I plan this to be the last update for the article. Its main purpose was to show how to do some tricky things with Explorer - replace the Start menu, add a toolbar, workaround bugs, that kind of stuff. The features I have planned for the future are mostly improvements of the Classic Shell internals. They do not involve interactions with Explorer, and will not make for an interesting article. Also, the article is getting pretty long. If any new feature is worth sharing, I think it's best to write a new article.

I will still be answering questions in the comments section, and will be posting updates when a new version comes out.

History

These are just the highlights of each version. For a complete list of changes, check out the history page: Classic Shell History.

Version 1.0.1 general release (Feb, 2010)

This is a bugfix-only release. Fixes a few rare crashes in the Start menu.

Version 1.0.0 general release (Feb, 2010)

Added Up button to the Explorer title bar.

The installer supports command line options for logging or unattended install.

Version 0.9.10 release candidate (Jan, 2010) - make your own toolbar

The Explorer toolbar can be customized with new icons and additional buttons.

License

Share

About the Author

Ivo started programming in 1985 on an Apple ][ clone. He graduated from Sofia University, Bulgaria with a MSCS degree. Ivo has been working as a professional programmer for over 12 years, and as a professional game programmer for over 10. He is currently employed in Pandemic Studios, a video game company in Los Angeles, California.

I'm tring 1.3. I want to open start and have a program above the line that I can exacute. I have done that. Now I want to create a folder that and be able to move the program on top of the folder. This way I can either exactue the program or click on the folder icon to open it to related files. Is that posable. I'm using Win 7 64.

On the start menu by default. There is a button called "help and support". There was also a folder called "programs". I mention these two only to explain. They are not the case in point. I would like to have a folder associated with "help support". So if I would be able to drag help support on to the folder call "programs". I would have what I want to do.

This would be my result:
someone goes to the "start menu"and sees a program called "Excel" and a folder directly associated with that name. In other words, the executable Excel would be where our way you would find an icon for a folder. The folder would hold programs associated with Excel. It might be the most recent files used by Excel or possibly a hard coded list of the most important files.

So you could execute Excel, or find a program in a folder related to Excel. Bottom line, you can execute Excel or if you're not sure what files are associated. You could click on the folder icon and open the folder that is directly under Excel and make a selection.

Another way to do this, which is not as organized would be to have Excel on the menu and create a folder directly underneath the word as in above example. What I just mentioned would be directly under as you would think of and looking at a list as opposed to my example of the folder being directed the underneath Excel, as you would think of ground under grass. Sorry about the bad analogy. But anyhow it's the ground under grass I am looking for.

OK, just to make it clear:
- You want to add a new item called "Excel"
- You want to place it in the main menu between "Programs" and "Help & Support"
- When you single-click on the item you want a sub-menu to open, with some files in it
- When you double-click on the item you want Excel to run

But if you want to do exactly what I described, it is done this way:
Open the StartMenuItems.ini file
Add a new menu item, lets call it "ExcelItem" to the MAIN_MENU.Items list (don't forget to remove the semicolon from the beginning of the line)
Describe the new item:

ExcelItem.Command=<the path to the exe you want to run when double-clicked>
ExcelItem.Link=<the path to the folder where you'll keep the files or shortcuts>
ExcelItem.Icon=<optional icon file. if not set, the icon from the folder will be used>
ExcelItem.Name=<optional label. if not set, the name of the folder will be used>

P.S. A new version 1.9.1 was just released. You should upgrade to it before you do any of this.

When I posted my question I wasn't referring to your program but thought that you, as an expert on writing writing shell extensions, might know why only ATL registration works?

What might cause a shell DLL extension application that doesn't use the ATL method of registration to cause the "run as administrator" link NOT to work? The link is there but when you selct "Run as adminitrator" nothing happens and it works again when you un-register the Shell DLL.

Any ideas what might be happening?

Thanks!

By the way I can't believe that anybody would give your Classic Shell anything but a 5 -- it is a anamazing piece of work... Congratulations!

Well, I'm no expert in shell extensions. When I want to do something I read the docs and follow the instructions. If I'm lucky it will work

Maybe when you write your shell extension you can try both methods (MFC and ATL) and compare what's written in the registry. You can investigate with Process Monitor which keys are affected during the registration process.

Edit: BTW, have you tried this on multiple machines? Maybe something is screwy with your system.

I did compare the keys that are written and they are identical so I was thinking it has to be in the "permissions" granted when you use ATL?

On my desktop I have a link to Visual Studio and when I right-mouse click I can select "Run as administrator" and it will launch Visual Studio. After I register any shell DLL without using ATL if I do the same thing Visual Studio will NOT launch. Selecting "Run as adminisrator" no longer launches Visual Studio so what is happening is that "Run as administrator" is disable somehow. I did look in the literature and on the Internet and was amazed that nobody else reported this Bug... I tried it on 3 different computers and bug is always there....

OK, next thing I would try is to start deleting (or even better, renaming) the registry keys one by one and try after each. This way you can isolate the problematic key, or determine that it is something else entirely. Once you have a single key that's causing the problem you can examine its permissions and compare them with another shell extension that works fine.

You are clearly an expert in coding this shell stuff and that is why I thought I would share with you something I discovered about writing shell extensions that I have never seen published anywhere--it is a serious bug....

To illustrate this bug, conside an article that Dan Madden wrote on CodeProject called "File Stream Encryption (using a Context Menu)" at:

This bug applies to ALL similar shell DLLs and his code is just one example. His code works fine on all versions of Windows EXCEPT Vista and Windows 7 where, like all similar shell DLLs something serious happens.

His code uses use the MFC, non-ATL, approach to registering the COM components as follows:

...
//register the CLSID entries
REGSTRUCT ShExClsidEntries[] = {

// this is the HKEY_CLASSES_ROOT/CLSID/<our CLSID> entry. this is where we place info about our shell ext.
// our CLSID (generated by GUIDGEN) is how the system refers to our shell extension.
HKEY_CLASSES_ROOT, TEXT("CLSID\\%s"), NULL, TEXT(SHELLEXNAME),

// HKEY_CLASSES_ROOT/CLSID/<our CLSID>/InProcServer . here, we tell the system where our DLL is located
HKEY_CLASSES_ROOT, TEXT("CLSID\\%s\\InProcServer32"), NULL, TEXT("%s"),

Registering the entries in his DLL and ALL DLLs using this method result in a fascinating and serious BUG, namely, it disables "Run as administrator" on the desktop.

I have tested this over and over again and in all samples projects that try to register the COM componets directly, without using ATL, in ever case, the "Run as administrator" command only on the desktop is disabled.

Do you know of any way to use the OLD MFC approach to register the componts that will NOT disable the "Run as administrator" command on teh desktop? Or do you have any guess as to WHY this happens?

I don't understand. Are you saying that without Classic Shell installed, you can click on a file on the Desktop and see "Run as administrator"? And after you install Classic Shell the "Run as administrator" is gone or disabled?

I tried this on Vista 64-bit with UAC turned OFF, and Windows 7 32-bit with UAC turned ON, and both show me "Run as administrator". Classic Shell is installed on both.

BTW, Classic Shell uses ATL and not MFC. Maybe MFC has some bug but it is not related to Classic Shell.

I forgot to test your Classic Shell to see if it also fails to allow programs to be launched from teh desktop as administrator--the link appears but when selected the program doesn't launch.... that is why I asked you this generic problem.

Let me explain.

Take any Shell DLL that creates a context menu and that registers the COM components directly like the sample Dan Maden wrote--but this is true for any shell DLL that isn't registering the compoents using:

In other words, if I try to register without using ATL above, then what happens is that on the desktop if you right-mouse click on any link and select "Run as administrator" nothing happens and as soon as you un-register the shell dll, then you can use "Run as administrator" again.

What is the difference in the 2 methods of registering the DLL? If I use ATL and the method above then "Run as adinistrator" is NOT disbabled, whereas if I register directly using the registry commands directly, then "Run as administrator" is disbaled but only on desktop links?

It compiles and you will notice that as soon as you compile it, all the links on your desktop NO longer allow you to use "Run as administrator"--and when you un-register the dll the links return to normal.

I tried this with literally a dozen shell DLLs that register the compents directly in the registry and in every case "Run as adinistrator" is disabled on the desktop. If you use ATL to register then the problem doesn't appear... BUT what would cause this?

I am going to install your classic shel again and see what happens --- "run as adnistrator" appears BUT when you try to run a desktop link as administrator NOTHING happens--the program fails to launch?

I'm very sorry and I apologize for my mistake !
---------------------------------------------------I can confirm that indeed all the shortcuts(.LNK files) on my desktop no longer work when right-click, then 'Run as administrator'

The OP was talking about a generic problem with some COM DLLs, but said he didn't try Classic Shell specifically.

Since you are having this problem with Classic Shell for real, maybe we can get somewhere.
1) Is your UAC setting on or off?
2) Are you running as admin?
3) Does "Run as administrator" work in other places, like Explorer or the start menu?
4) Can you try to unregister the ClassicExplorer32.dll and see if the problem is resolved immediately? You do that by going to the Classic Shell folder and typing "regsvr32 /u ClassicExplorer32.dll". Verify that registering the DLL introduces the problem again (the command is "regsvr32 ClassicExplorer32.dll".