Using TShellItem

Date: 5 February 1997
Author: Christopher Kohlhoff

Overview

This article looks at how to use TShellItem. TShellItem allows your application to closely integrate with the Windows 95/NT shell, providing file operations such as copying, moving, deleting and creating shortcuts. If you build data structures using the file system in this way, you can take advantage of features such as the Recycling Bin. You can download the example project from here. Please note that this is Borland C++ 5.01/32-bit only. When building your project, make sure that OLE is selected in TargetExpert.

Steps

Creating Shell Items

When creating shell item objects from scratch, you will typically want to do it from a specified path, or for one of the "system" folders.

To create a shell item from a path you can use it like
  TShellItem item("c:\\temp");
which will create a shell item for the given directory, or like
  TShellItem item("c:\\autoexec.bat");
which creates an item for the given file.

To create a shell item for a special system folder, use something like:
  TShellItem item(TShellItem::Desktop);

Browsing for Folders

Browsing for a folder executes a system dialog that allows the user to select a shell folder. It optionally allows the selection of system folders such as the Control Panel. The shell item for which BrowseForFolder is called specifies the base directory where the user may browse. For example:

// create the shell item for where we start browsing
// (we will use the desktop). This MUST be a folder.
//
TShellItem browseRoot(TShellItem::Desktop);

// browse for the folder
//
TShellItem::TCreateStruct selItem;
if (browseRoot.BrowseForFolder(selItem, HWindow))
{
  TShellItem item(selItem);
  MessageBox(item.GetDisplayName(TShellItem::ForParsing));

Creating Shortcuts

There isn't really that much to say, except to show how to do it. This example creates a shortcut to the Program Files directory on the C drive:

// the object to make a shortcut to
const char* object = "c:\\Program Files";

// the place to save the shortcut
const char* path = "c:\\Shortcut to Programs.lnk";

// the description of the shortcut
const char* description = "Shortcut to Programs";

if (!SUCCEEDED(TShellItem::CreateShortCut(object,
                                          path,
                                          description)))
  MessageBox("Could not create shortcut.");

It is important that the path parameter specify the full path of the link file to be saved. The filename component should be what you want as the description.

Note: this example used the static member function version of CreateShortCut. A non-static version is also available (that is, it requires a TShellItem object) but I would not advise using it. It does not save the shortcut to disk, and if you want to save it yourself you have to mess around with the OLE IPersistFile interface. Not worth the trouble.

Copying, Moving, Deleting and Renaming

These are pretty straightforward. The only things to watch out for are that both Move and Rename result in the shell item being updated to the new location, and that Delete seems to result in the item becoming invalid, even if the user cancels the deletion. Note also that the new name passed to Rename should only include an extension if the extension is normally displayed for the file type (it isn't in the example below).

TShellItem item("c:\\temp\\readme.txt");

// copy the item
TShellItem copyDest("c:\\temp\\readme2.txt");
Item.Copy(copyDest, 0"Copying File", HWindow);

// move the item
TShellItem moveDest("c:\\temp\\readme3.txt");
Item.Move(moveDest, 0"Moving File", HWindow);

// rename the item
Item.Rename("stuff");

// delete the item
Item.Delete(0"Deleting File", HWindow);

Context Menus

You can display and use the shell context menus for file types just by adding the following code. Without adding any more, the user will be able to access file properties, formatting, and more.

// context menu identifiers
//
const int kContextBase = 28000;
const int kContextMax  = 28100;

DEFINE_RESPONSE_TABLE1(TShExWindow, TWindow)
  // ...
  EV_WM_CONTEXTMENU,
  // ...
END_RESPONSE_TABLE;


void TShExWindow::EvContextMenu(HWND childHwnd, int x, int y)
{
  TWindow::EvContextMenu(childHwnd, x, y);

  // create a popup menu for the item
  //
  TPopupMenu menu;

  // add the shell item's menu commands
  //
  ContextMenu = Item.GetContextMenu(*this);
  ContextMenu->QueryContextMenu(menu, 0, kContextBase,
                                kContextMax, CMF_NORMAL);

  // execute the popup menu
  //
  menu.TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
                      x, y, 0, *this);
}


TResult TShExWindow::EvCommand(uint id, THandle hWndCtl,
                               uint notifyCode)
{
  TResult result = TWindow::EvCommand(id, hWndCtl, notifyCode);

  if (id >= kContextBase && id <= kContextMax)
  {
    // initialise the command information
    //
    CMINVOKECOMMANDINFO info;
    info.cbSize = sizeof(info);
    info.fMask  = 0;
    info.hwnd   = *this;
    info.lpVerb = (LPCSTR) MAKELONG(id - kContextBase, 0);
    info.lpParameters = NULL;
    info.lpDirectory = NULL;
    info.nShow = SW_SHOW;

    // execute the selected command
    //
    ContextMenu->InvokeCommand(&info);
  }

  return result;
}