Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / Win32

Windows Explorer in XP: Now with Checkboxes and Fullrowselect

Rate me:
Please Sign up or sign in to vote.
4.13/5 (9 votes)
8 Jan 2009CPOL5 min read 53.7K   578   37   12
Customizing XP with Vista features
TrayProdder_1

Introduction

Windows Explorer is a great little program. Millions use it, few have any complaints. But unless you've moved to Vista, there are things you can't do in Windows Explorer:

  • Checkboxes: you can't get a checkbox column to select files with. So you either select one file at a time, use a selection rectangle to "lasso" some adjoining files, or you Multi-Select. Which stops working when you accidentally lift the Ctrl finger and have to start all over again. Tedious.
  • Fullrowselect: you can't get the selection color to encompass all columns in the file list. Which would have been helpful if your selection criteria depends on, for instance, the file date.

With TrayProdder, these two features are no longer missing.

Background

The ListView control of the Win32 API is heavily used in any number of run-of-the-mill Windows applications. It is of course also used in Windows Explorer. And CheckBoxes and FullRowSelect have been ListView properties since time immemorial, as far as I know. So it irked me that none of them can be changed in the settings for Windows Explorer (pre-Vista, that is). I scoured the web for some arcane registry key, but nooo....

What to do? Well, roll your own, of course!

Using TrayProdder

trayprod_mainfrm3.png

Normally you will have TrayProdder running in the system tray (i.e the form above will be invisible). To show the form, just double-click the TP icon in the tray.

Then:

  • check none, any or both of the two checkboxes Checkboxes and Full row select. This will prod any running instances of Windows Explorer to attain the desired features.
  • click any of the eight buttons to make full use of both checkboxes and selection status. This will speed file selection to no end.

You can also click the ? button to show some overall information and - more importantly - command line options.

Please note: TrayProdder does not change the Windows Explorer executable or installation in any way, shape or form.

When you quit TrayProdder, everything resorts back to its original state. Nothing is permanently changed. You may well ask "how is it then even possible to make Explorer get up on its hind legs and do tricks like a circus pony?" The answer: TrayProdder does it's magic by lobbing messages over the process boundry to Explorer, right at the ListView controls in it. In the absence of hooks (technically "better" but way more invasive), a 5-times-a-second timer keeps Explorer reasonably synched to options chosen in TrayProdder.

Points of Interest

Using enums to Pass Task Information

The main form (class TrpMain) is the controller for all the action of TrayProdder. Here we have a timer object/event that keeps checkboxes and fullrowselects active as new Explorer instances appear, and the event handlers for the eight button actions possible.

However, for the sake of modularization, the main form is kept clean of any actual low-level interaction with Explorer. That stuff is contained in an "engine" class named TrpBase.

So, we now need a way for TrpMain to tell TrpBase what to do. What better way than to define a couple of enums:

C#
public enum enAction
{
    MaintainStyle,      // maintain wanted visibility of fullrowselect and
                        // checkboxes (timer)
    HitCheckedRows,     // perform action based on checked items
    HitSelectedRows     // perform action based on selected items
}//enAction

public enum enChangeState
{
    TrueMirror,  // set "mirror" to true if hit
    FalseMirror, // set "mirror" to false if hit
    InverseSelf, // toggle self from true to false or vice versa
    ClearSelf,   // set self to false
    Dummy        // used by enAction.MaintainStyle (none above applies)
}//enChangeState

The enAction enum defines the major task to be performed (at timer tick or button press). The enChangeState is in effect an argument to enAction, giving one of four possible things to do once you have hit on either a checked row or a selected row. The "mirror" moniker is just an awkward way of saying that if we hit a SELECTED row, we want to do something with it's CHECKED status. And vice versa. In effect, the two properties mirror each other.

The timer task (MaintainSyle) does not need any supplemental argument but is given one (Dummy), just for the sake of symmetry.

The Innards of the Engine

Now that we've defined an enum interface between the GUI (trpMain) and the engine (trpBase), we need a public method in trpBase that can be called by trpMain, using the enums above as arguments.

C#
public void fnActionTop ( enAction nAction, enChangeState nChangeState )
{
 if ( !m_bBusy ) //one at a time....
     {
         m_bBusy = true;
         if (nAction != enAction.MaintainStyle)
         {
             m_hMainForm.Enabled = false;
             Application.DoEvents();
         }
         fnActionWinClass ( "CabinetWClass", nAction, nChangeState );
         fnActionWinClass ( "ExploreWClass", nAction, nChangeState );
         if (nAction != enAction.MaintainStyle)
         {
             m_hMainForm.Enabled = true;
             Application.DoEvents();
         }
         m_bBusy = false;
     }//!Busy
}

Here we have a busy flag to ensure that the last action was completely taken care of, before any new action is possible. Since actions (except MaintainStyle) may be time-consuming (say in lists of 4000 rows or more), we disable the main form until tbe action has ended. The duo "CabinetWClass" and "ExploreWClass" stems from the fact that any one of these can exist as a top level container in an instance of Explorer, depending on the way it's been invoked.

Finding ListView in a Nest of Containers

Here is what may be the hairiest piece of the TrayProdder source. It's called by fnActionTop (above) to find the ListView lurking somewhere in the container structure of the specified top window class, and then taking the indicated action.

C#
private void fnActionWinClass ( string sWinClass, enAction nAction,
    enChangeState nChangeState )
{
    int hMain = 0; int hShell = 0; int hList = 0;
    int hLevel1 = 0; int hLevel2 = 0; int hLevel3 = 0;

    while ( ( hMain = FindWindowEx ( 0, hMain, sWinClass, null ) ) > 0 )
        {//winclass found
            while ( ( hShell = FindWindowEx ( hMain, hShell,
                "SHELLDLL_DefView", null ) ) > 0 )
                {//shell found
                // if listview is not found directly under the shell,
                // look three levels down
                   bool bListFound = false;
                   while ( ( hList = FindWindowEx ( hShell, hList,
                       "SysListView32", null ) ) > 0 )
                        {
                        bListFound = true;
                        fnStyleOrAction(hList,nAction,nChangeState);
                        }//Syslistview32 found under shell
                   if (!bListFound)
                   {//check next three levels
                       hList = 0;
                       while ((hLevel1 = FindWindowEx(hShell, hLevel1,
                           "DUIViewWndClassName", null)) > 0)
                       {
                           while ((hLevel2 = FindWindowEx(hLevel1, hLevel2,
                               "DirectUIHWND", null)) > 0)
                           {
                               while ((hLevel3 = FindWindowEx(hLevel2,
                                   hLevel3, "CtrlNotifySink", null)) > 0)
                               {
                                   while ((hList = FindWindowEx(hLevel3,
                                       hList, "SysListView32", null)) > 0)
                                   {
                                       fnStyleOrAction(hList, nAction,
                                           nChangeState);
                                   }//listview found on third level
                               }//check third level
                           }//check second level
                       }//check first level
                   }//check next three levels
               }//shell found
        }//winclass found
}//fnActionWinClass

The Real Challenge: Gathering Information

The actual programming may be rather pedestrian, but finding the information needed was an interesting exercise in software forensics.

  • Google helped me figure out names of various Explorer container objects: CabinetWClass, ExploreWClass, SHELLDLL_DefView, DUIViewWndClassName, DirectUIHWND, CtrlNotifySink and SysListView32 (a ListView subclass). Where Google couldn't help me, the Microsoft utility Spy++ could.
  • Much of the message definitions came from ancient Microsoft C header files, such as COMMCTRL.H and WINUSER.H. A .NET developer is normally discouraged from mucking about too much with low-level stuff (for sound architectural reasons) so these may well be missing from his IDE. However, if you google any one of them, you'll easily find them on the web. Example: COMMCTRL.H
  • Important insights, as well as core parts of the code (though somewhat modified), came from this page: Crossing the process boundry with .NET

Other parts of Windows could conceivably be customized in a similar, non-intrusive fashion. Any takers?

All source code for the project is contained in the zip file above. A ready-to-use executable can be found here: TrayProdder Home Page

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Sweden Sweden
A free-lance software developer/consultant, native of Stockholm, Sweden. See FAQ of home page for more information.

Comments and Discussions

 
GeneralInelegant implementation Pin
combatpiracy23-Dec-09 14:41
combatpiracy23-Dec-09 14:41 
GeneralTrayProdder Pin
Scott Cash Fields11-Dec-09 9:56
Scott Cash Fields11-Dec-09 9:56 
QuestionHow to Compile Pin
DDjinn11-Aug-09 4:10
DDjinn11-Aug-09 4:10 
AnswerRe: How to Compile Pin
Member 1126021224-Nov-14 7:12
Member 1126021224-Nov-14 7:12 
GeneralFabulous Tool! Worth it, Just for Full Row Select Pin
David A. Gray14-Jan-09 15:42
David A. Gray14-Jan-09 15:42 
GeneralRe: Fabulous Tool! Worth it, Just for Full Row Select Pin
The Everator14-Jan-09 23:02
The Everator14-Jan-09 23:02 
GeneralTagging of Closing Brackets Pin
David A. Gray14-Jan-09 15:40
David A. Gray14-Jan-09 15:40 
GeneralRe: Tagging of Closing Brackets Pin
The Everator14-Jan-09 23:18
The Everator14-Jan-09 23:18 
Generalcool. suggestions... Pin
dddoug8-Jan-09 12:54
dddoug8-Jan-09 12:54 
GeneralRe: cool. suggestions... Pin
The Everator14-Jan-09 23:27
The Everator14-Jan-09 23:27 
GeneralRe: cool. suggestions... Pin
dddoug15-Jan-09 4:29
dddoug15-Jan-09 4:29 
GeneralRe: cool. suggestions... Pin
The Everator15-Jan-09 6:45
The Everator15-Jan-09 6:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.