Shell Features

   

The term Shell is used within Windows to describe an application that enables a user to group , start, and control other applications or files. Shell features include drag-and-drop file support, file association support for starting and finding other applications, and the capability to extract icons from other files. The Shell aspect of the Win32 API is a very powerful feature set and is contained within the Shell32.dll . The principal header file included within the source code of an application that provides the Shell features is the SHELLAPI.H . The basic features of the Shell API are described in Table 14.13.

Table 14.13. Basic Features of the Shell API

Shell Feature

Description

Drag-and-Drop

Enables a user to select one or more files in Windows Explorer (or even the old File Explorer provided with Win 3.x) that can be dragged and dropped into an open application that has previously used the DragAcceptFiles() Shell function. A WM_DROPFILES message is received by the open application, which is used to retrieve the filenames and display the position at which the files were dropped. This is accomplished using the DragQueryFile() and DragQueryPoint() Shell functions.

File Association Support for Starting and/or Finding Other Applications

Back in the Win 3.x days, Microsoft provided the Associate dialog box within File Explorer. This allowed a user to associate a filename extension with a particular application. Since Windows 95, a more detailed Associate dialog box is provided within Windows Explorer called the Open With dialog. The Registry provides an automated association of filename extensions and applications. Within Windows Explorer, a file that is double-clicked and has an association with a specific application will cause the application to load and open the selected file. The FindExecutable() and ShellExecute() routines contained within the Shell API make use of this file association. The FindExecutable() function is used to retrieve the name and handle to the application associated with a specified file. ShellExecute() and ShellExecuteEx() are used to open or print a specified file. The application required to open a file is launched based on the file association.

Extracting Icons

Applications, dynamic link libraries, and icon files are typically represented by one or more icons. The handle of an icon can be retrieved easily by using the ExtractIcon() Shell routine.

There are more than 120 Shell routines available in current versions of Windows, many of which are undocumented. Use Borland's impdef command-line tool or the DLL LIB Util application, which was described in the previous Tip, to view the full list of the available Shell library functions.

Shell functions are a big thing in Windows 98/2000/XP programming. Newer Shell functions are best known as SH x functions because many of them have the SH prefix. A few common ones and new ones supported in Windows 2000 and XP are described later in this section. Several newer Shell functions have replaced the older Win32 API routines because they are more versatile. Although the older API routines can still be used for simple transfers or manipulation, the new API routines provide more flexibility, yet sometimes are very hard to use. We will identify the newer ones from the older ones within our examples.

Using ShellExecute() to Open a Browser

With much of today's data available via the World Wide Web, it is often practical for an application to interface with the platform's default Web browser. The Win32 API Shell extensions make it possible to provide this type of interface.

The following code example in Listing 14.11 demonstrates how to use the ShellExecute() function to open a browser with a specified address (URL).

Listing 14.11 Opening the Browser with ShellExecute()
 void __fastcall TForm1::ButtonBrowserClick(TObject *Sender)  {    if (EditURL->Text.Length() > 0)    {      if (! ShellExecute(Handle, "open", EditURL->Text.c_str(),                          NULL, NULL, SW_SHOW))      {          char data[100];          sprintf(data,"Could not run browser with URL '%s'",EditURL->Text);          MessageBox(NULL,data,"Operation Error!",MB_OK);      }    }    else      MessageBeep(MB_OK);  }  

Let's look at the parameters used for ShellExecute() in this example. The first parameter identifies a handle to the parent. We can actually leave this NULL if we want. The second parameter used for ShellExecute() indicates a desire to "open" a file. Other choices include "print" a file, and "explore" a folder. The third parameter provides information about where that file or folder is located. In this example, our "file" is actually a Universal Resource Locator (URL) address commonly used for Web browsing. The Shell library deciphers URL descriptors and will use the registered browser that supports URLs.

Using ShellExecuteEx() to Spawn an Application

An easy way to launch an external application is by using the slightly more powerful ShellExecuteEx() function, which is illustrated by example in Figure 14.9. ShellExecuteEx() is considered easier to use than the CreateProcess() API routine that we discussed earlier. Unfortunately, it doesn't provide a way to retrieve the handle of the application being spawned like CreateProcess() does.

Figure 14.9. Shell example for launching an application.

graphics/14fig09.gif

Listing 14.12 demonstrates how ShellExecuteEx() can be used to launch an application within C++Builder.

Listing 14.12 Launching an Application with ShellExecuteEx()
 void __fastcall TForm1::ButtonLaunchAppClick(TObject *Sender)  {    char temp[MAX_PATH];    char params[MAX_PATH];    sprintf(temp,"%s 
 void __fastcall TForm1::ButtonLaunchAppClick(TObject *Sender) { char temp[MAX_PATH]; char params[MAX_PATH]; sprintf(temp,"%s\0",EditFile->Text.c_str()); sprintf(params,"%s\0",EditParams->Text.c_str()); // Select the program and how it will be run. SHELLEXECUTEINFO execinfo ; memset (&execinfo, 0, sizeof (execinfo)) ; execinfo.cbSize = sizeof (execinfo) ; execinfo.lpVerb = "open" ; execinfo.lpFile = temp; execinfo.lpParameters = params; execinfo.fMask = SEE_MASK_NOCLOSEPROCESS ; execinfo.nShow = SW_SHOWDEFAULT ; // Run the program. if (! ShellExecuteEx (&execinfo)) { char data[100]; sprintf(data,"Could not run program '%s'",EditFile->Text.c_str()); MessageBox(NULL,data,"Operation Error!",MB_OK); } } 
",EditFile->Text.c_str()); sprintf(params,"%s
 void __fastcall TForm1::ButtonLaunchAppClick(TObject *Sender) { char temp[MAX_PATH]; char params[MAX_PATH]; sprintf(temp,"%s\0",EditFile->Text.c_str()); sprintf(params,"%s\0",EditParams->Text.c_str()); // Select the program and how it will be run. SHELLEXECUTEINFO execinfo ; memset (&execinfo, 0, sizeof (execinfo)) ; execinfo.cbSize = sizeof (execinfo) ; execinfo.lpVerb = "open" ; execinfo.lpFile = temp; execinfo.lpParameters = params; execinfo.fMask = SEE_MASK_NOCLOSEPROCESS ; execinfo.nShow = SW_SHOWDEFAULT ; // Run the program. if (! ShellExecuteEx (&execinfo)) { char data[100]; sprintf(data,"Could not run program '%s'",EditFile->Text.c_str()); MessageBox(NULL,data,"Operation Error!",MB_OK); } } 
",EditParams->Text.c_str()); // Select the program and how it will be run. SHELLEXECUTEINFO execinfo ; memset (&execinfo, 0, sizeof (execinfo)) ; execinfo.cbSize = sizeof (execinfo) ; execinfo.lpVerb = "open" ; execinfo.lpFile = temp; execinfo.lpParameters = params; execinfo.fMask = SEE_MASK_NOCLOSEPROCESS ; execinfo.nShow = SW_SHOWDEFAULT ; // Run the program. if (! ShellExecuteEx (&execinfo)) { char data[100]; sprintf(data,"Could not run program '%s'",EditFile->Text.c_str()); MessageBox(NULL,data,"Operation Error!",MB_OK); } }

In this example, the execinfo structure, defined by SHELLEXECUTEINFO , is filled and passed as a parameter to the ShellExecuteEx() function. The two key attributes of execinfo structure are the open flag and the file. Command-line arguments can also be passed as parameters. Based on these values, ShellExecuteEx() will open the application. In general, the ShellExecute() and ShellExecuteEx() functions use file association (examine the file extension) to determine how files are opened. If it is an exe file representing a standalone application, it will launch, but if it's a file such as a txt file, a registered application such as Notepad will launch and open the txt file.

Backing-Up Directories and Files

SHFileOperation() is a versatile file operation that supports multiple directory transfers. The SHFileOperation() is significantly more powerful than the system service MoveFile() API call because it can transfer files and directories over to a new volume. It can also transfer children files within a directory. The SHFileOperation() is widely used in Windows 95 and later and can be seen in Figure 14.10.

Figure 14.10. Shell example for backing-up directories and files.

graphics/14fig10.jpg

The example code, shown in Listing 14.13, demonstrates how to copy files and subdirectories all at once from one location to another.

Listing 14.13 Using the SHFileOperation() to Copy a Folder
 void __fastcall TForm1::ButtonCopyDirClick(TObject *Sender)  {      LabelMoveResults->Caption = "";      //Declare the SHFILEOPSTRUCT structure to fill in information for use      //of SHFileOperation function.      SHFILEOPSTRUCT op;      //Clear out any thing within the structure      ZeroMemory(&op, sizeof(op));      char source[MAX_PATH];      memset(source, 0, MAX_PATH);      char dest[MAX_PATH];      memset(dest, 0, MAX_PATH);      op.hwnd = Handle;     //This is a handle to the main window, used 0 for ours      op.wFunc= FO_COPY;    //Tell SHFileOperation to COPY files      sprintf(source,"%s 
 void __fastcall TForm1::ButtonCopyDirClick(TObject *Sender) { LabelMoveResults->Caption = ""; //Declare the SHFILEOPSTRUCT structure to fill in information for use //of SHFileOperation function. SHFILEOPSTRUCT op; //Clear out any thing within the structure ZeroMemory(&op, sizeof(op)); char source[MAX_PATH]; memset(source, 0, MAX_PATH); char dest[MAX_PATH]; memset(dest, 0, MAX_PATH); op.hwnd = Handle; //This is a handle to the main window, used 0 for ours op.wFunc= FO_COPY; //Tell SHFileOperation to COPY files sprintf(source,"%s\0",LabelSourceDir->Caption.c_str()); //Source directory op.pFrom = source; sprintf(dest,"%s\0",LabelTargetDir->Caption.c_str()); //Destination directory op.pTo = dest; op.fFlags= FOF_NOCONFIRMATION + FOF_ALLOWUNDO; TCursor Save_Cursor = Screen->Cursor; Screen->Cursor = crHourGlass; // Show hourglass cursor try { if (SHFileOperation(&op) == 0) // so far looks good { if (op.fAnyOperationsAborted) LabelMoveResults->Caption = "Copy process halted!"; else LabelMoveResults->Caption = "Copy operation successful!"; } else // had a problem LabelMoveResults->Caption = "Copy process unsuccessful ."; } __finally { Screen->Cursor = Save_Cursor; // always restore the cursor } DirectoryListBoxTarget->Update(); } 
",LabelSourceDir->Caption.c_str()); //Source directory op.pFrom = source; sprintf(dest,"%s
 void __fastcall TForm1::ButtonCopyDirClick(TObject *Sender) { LabelMoveResults->Caption = ""; //Declare the SHFILEOPSTRUCT structure to fill in information for use //of SHFileOperation function. SHFILEOPSTRUCT op; //Clear out any thing within the structure ZeroMemory(&op, sizeof(op)); char source[MAX_PATH]; memset(source, 0, MAX_PATH); char dest[MAX_PATH]; memset(dest, 0, MAX_PATH); op.hwnd = Handle; //This is a handle to the main window, used 0 for ours op.wFunc= FO_COPY; //Tell SHFileOperation to COPY files sprintf(source,"%s\0",LabelSourceDir->Caption.c_str()); //Source directory op.pFrom = source; sprintf(dest,"%s\0",LabelTargetDir->Caption.c_str()); //Destination directory op.pTo = dest; op.fFlags= FOF_NOCONFIRMATION + FOF_ALLOWUNDO; TCursor Save_Cursor = Screen->Cursor; Screen->Cursor = crHourGlass; // Show hourglass cursor try { if (SHFileOperation(&op) == 0) // so far looks good { if (op.fAnyOperationsAborted) LabelMoveResults->Caption = "Copy process halted!"; else LabelMoveResults->Caption = "Copy operation successful!"; } else // had a problem LabelMoveResults->Caption = "Copy process unsuccessful ."; } __finally { Screen->Cursor = Save_Cursor; // always restore the cursor } DirectoryListBoxTarget->Update(); } 
",LabelTargetDir->Caption.c_str()); //Destination directory op.pTo = dest; op.fFlags= FOF_NOCONFIRMATION + FOF_ALLOWUNDO; TCursor Save_Cursor = Screen->Cursor; Screen->Cursor = crHourGlass; // Show hourglass cursor try { if (SHFileOperation(&op) == 0) // so far looks good { if (op.fAnyOperationsAborted) LabelMoveResults->Caption = "Copy process halted!"; else LabelMoveResults->Caption = "Copy operation successful!"; } else // had a problem LabelMoveResults->Caption = "Copy process unsuccessful."; } __finally { Screen->Cursor = Save_Cursor; // always restore the cursor } DirectoryListBoxTarget->Update(); }

When this example is executed, all files and subdirectories within the directory identified by LabelSourceDir->Caption will be copied over to the directory identified by LabelTargetDir->Caption .

You'll notice that one of the first things done in the ButtonCopyDir() event handler is the declaration of a variable, op , based on the SHFILEOPSTRUCT structure and that its values are initialized . The variable op is used by the SHFileOperation() function to carry out the file move operation. The structure SHFILEOPSTRUCT has the following format:

 typedef struct _SHFILEOPSTRUCT { // shfos      HWND         hwnd;      UINT         wFunc;      LPCSTR       pFrom;      LPCSTR       pTo;      FILEOP_FLAGS fFlags;      BOOL         fAnyOperationsAborted;      LPVOID       hNameMappings;      LPCSTR       lpszProgressTitle;  } SHFILEOPSTRUCT, FAR *LPSHFILEOPSTRUCT; 

At first glance, there seems to be many items to fill out, however, not all fields are necessary. For instance, the only properties used in our example include wFunc , pFrom , pTo , and fFlags . Let's examine each of these properties.

wFunc indicates the operation to perform. The operation selections are shown in Table 14.14.

Table 14.14. Possible Operations

Member

Value

FO_COPY

Copies the files specified by pFrom to the location specified by pTo . You can copy files from one location to another location even across volumes .

FO_DELETE

Deletes the files specified by pFrom . The pTo is ignored because we are just deleting the files. See flags under this parameter for Recycle Bin operations.

FO_MOVE

Moves the files specified by pFrom to the location specified by pTo . This will physically move the source files to another location. It does support over-the-volume transfers.

FO_RENAME

Renames the files specified by pFrom .

In our example, we are copying one directory to another (making a backup), using FO_COPY . Use caution when using FO_MOVE or FO_DELETE .

pFrom represents a pointer to a buffer specifying one or more source filenames or a folder. Multiple names must be null-separated. The list of names must be double “null- terminated . If you have many files in a list, you could use a regular \0 , which is the separator for those files. At the end of the list, use double-null termination. In our example, we're identifying a complete directory and any subfolders .

pTo represents a pointer to a buffer that contains the name of the destination file or folder. The buffer can contain multiple destination filenames if the fFlags member specifies FOF_MULTIDESTFILES (see Table 14.14). Multiple names must be null-separated, and the list of names must be double “null-terminated.

In our example, we use the C function sprintf() to set up our null-terminated strings used for pFrom and pTo . This is where we transfer the value from our TEdit controls representing the folders and tack on an additional null terminator.

CAUTION

Not tacking an additional null terminator at the end of the string used for pFrom or pTo is a common miss . Forgetting to do so, will likely cause an error in the SHFileOperation() operation.


Next, fFlags identifies the flags that control the file operation. This member can be any combination of the values shown in Table 14.15.

Table 14.15. File Operation Flags

Flag

Value

FOF_ALLOWUNDO

Preserves undo information, if possible.

FOF_FILESONLY

Performs the operation only on files if a wildcard filename ( *.* ) is specified.

FOF_MULTIDESTFILES

Indicates that the pTo member specifies multiple destination files (one for each source file) rather than one directory where all source files are to be deposited.

FOF_NOCONFIRMATION

Responds with Yes to All for any dialog box that is displayed.

FOF_NOCONFIRMMKDIR

Does not confirm the creation of a new directory if the operation requires one to be created.

FOF_RENAMEONCOLLISION

Gives a new name to the file being operated on (such as Copy #1 of... ) in a move, copy, or rename operation, if a file of the target name already exists.

FOF_SILENT

Does not display a progress dialog box. Nothing will appear while this function is in operation.

FOF_SIMPLEPROGRESS

Displays a progress dialog box, but does not show the file-names.

FOF_WANTMAPPINGHANDLE

Fills in the hNameMappings member. The handle must be freed by using the SHFreeNameMappings() function.

In our example, we are using the FOF_NOCONFIRMATION and FOF_ALLOWUNDO to support our folder copy. To enable a list of files (rather than the contents and subdirectories of a folder), you would use the FOF_MULTIDESTFILES flag. An example is provided in the sample program contained on the CD-ROM.

These are the only properties we're interested in within our example because they relate to SHFILEOPSTRUCT . Be sure to check out the other structure properties, which are explained in the Win32 API Help file.

NOTE

You might have noticed the function ZeroMemory() used in the example. After declaring the SHFILEOPSTRUCT record variable op , we want to NULL out the structure information so that we can start with a blank canvas. The reason is that we don't want any dirty values at the memory location associated to op because there are a wide number of properties that are examined by the SHFileOperation() function and could affect its operation.


Sending Files to the Recycle Bin

One novel way of using the SHFileOperation() function is to delete files and place them in the Recycle Bin. The code example in Listing 14.14 illustrates how to perform a directory deletion.

Listing 14.14 Deleting All Files in a Directory
 void __fastcall TForm1::ButtonSendToRecycleBinClick(TObject *Sender)  {      // initialize      MemoFilesToMove->Clear();      ListBoxFilesToMove->Clear();      LabelMoveResults->Caption = "";      AnsiString DelDir;    //Our directory handle      int index = DirectoryListBoxSource->ItemIndex; //keep track of index      //Get the CURRENTLY selected item in the list...      DelDir = DirectoryListBoxSource->Directory;      AnsiString alert;      alert =  "Are you sure you wish to remove the folder \"" + DelDir + "\" "               "and all its contents?";      int response =          Application->MessageBox(alert.c_str(), "Remove Folder", MB_YESNO);      if (response == ID_NO)          return; // get out      DelDir.SetLength(DelDir.Length() + 1);             DelDir[DelDir.Length()] = ' 
 void __fastcall TForm1::ButtonSendToRecycleBinClick(TObject *Sender) { // initialize MemoFilesToMove->Clear(); ListBoxFilesToMove->Clear(); LabelMoveResults->Caption = ""; AnsiString DelDir; //Our directory handle int index = DirectoryListBoxSource->ItemIndex; //keep track of index //Get the CURRENTLY selected item in the list... DelDir = DirectoryListBoxSource->Directory; AnsiString alert; alert = "Are you sure you wish to remove the folder \"" + DelDir + "\" " "and all its contents?"; int response = Application->MessageBox(alert.c_str(), "Remove Folder", MB_YESNO); if (response == ID_NO) return; // get out DelDir.SetLength(DelDir.Length() + 1); DelDir[DelDir.Length()] = '\0'; DelDir.SetLength(DelDir.Length() + 1); DelDir[DelDir.Length()] = '\0'; // before we send the folder and it's contents to the recycle // bin, lets drop back one folder. This keeps from having a sharing // violation DirectoryListBoxSource->Directory = DirectoryListBoxSource->Items->Strings[index-1]; DirectoryListBoxSource->Update(); SHFILEOPSTRUCT op; //Declare structure variable //Clear out memory ZeroMemory(&op, sizeof(op)); //Set up structure for SHFIleOperation for source and destination... op.hwnd = 0; op.wFunc= FO_DELETE; //Delete files flag op.pFrom = DelDir.c_str(); op.fFlags=FOF_ALLOWUNDO; int copy_done = SHFileOperation(&op); if (copy_done == 0) { if (op.fAnyOperationsAborted) LabelMoveResults->Caption = "You have halted the removal of the folder." "Some files may have been moved to the Recycle Bin. " "Go to Recycle Bin to restore those files."; else LabelMoveResults->Caption = "Folder removal successful!"; } else LabelMoveResults->Caption = "Folder removal failed."; //Update DirectoryListbox DirectoryListBoxSource->Update(); } 
'; DelDir.SetLength(DelDir.Length() + 1); DelDir[DelDir.Length()] = '
 void __fastcall TForm1::ButtonSendToRecycleBinClick(TObject *Sender) { // initialize MemoFilesToMove->Clear(); ListBoxFilesToMove->Clear(); LabelMoveResults->Caption = ""; AnsiString DelDir; //Our directory handle int index = DirectoryListBoxSource->ItemIndex; //keep track of index //Get the CURRENTLY selected item in the list... DelDir = DirectoryListBoxSource->Directory; AnsiString alert; alert = "Are you sure you wish to remove the folder \"" + DelDir + "\" " "and all its contents?"; int response = Application->MessageBox(alert.c_str(), "Remove Folder", MB_YESNO); if (response == ID_NO) return; // get out DelDir.SetLength(DelDir.Length() + 1); DelDir[DelDir.Length()] = '\0'; DelDir.SetLength(DelDir.Length() + 1); DelDir[DelDir.Length()] = '\0'; // before we send the folder and it's contents to the recycle // bin, lets drop back one folder. This keeps from having a sharing // violation DirectoryListBoxSource->Directory = DirectoryListBoxSource->Items->Strings[index-1]; DirectoryListBoxSource->Update(); SHFILEOPSTRUCT op; //Declare structure variable //Clear out memory ZeroMemory(&op, sizeof(op)); //Set up structure for SHFIleOperation for source and destination... op.hwnd = 0; op.wFunc= FO_DELETE; //Delete files flag op.pFrom = DelDir.c_str(); op.fFlags=FOF_ALLOWUNDO; int copy_done = SHFileOperation(&op); if (copy_done == 0) { if (op.fAnyOperationsAborted) LabelMoveResults->Caption = "You have halted the removal of the folder." "Some files may have been moved to the Recycle Bin. " "Go to Recycle Bin to restore those files."; else LabelMoveResults->Caption = "Folder removal successful!"; } else LabelMoveResults->Caption = "Folder removal failed."; //Update DirectoryListbox DirectoryListBoxSource->Update(); } 
'; // before we send the folder and it's contents to the recycle // bin, lets drop back one folder. This keeps from having a sharing // violation DirectoryListBoxSource->Directory = DirectoryListBoxSource->Items->Strings[index-1]; DirectoryListBoxSource->Update(); SHFILEOPSTRUCT op; //Declare structure variable //Clear out memory ZeroMemory(&op, sizeof(op)); //Set up structure for SHFIleOperation for source and destination... op.hwnd = 0; op.wFunc= FO_DELETE; //Delete files flag op.pFrom = DelDir.c_str(); op.fFlags=FOF_ALLOWUNDO; int copy_done = SHFileOperation(&op); if (copy_done == 0) { if (op.fAnyOperationsAborted) LabelMoveResults->Caption = "You have halted the removal of the folder." "Some files may have been moved to the Recycle Bin. " "Go to Recycle Bin to restore those files."; else LabelMoveResults->Caption = "Folder removal successful!"; } else LabelMoveResults->Caption = "Folder removal failed."; //Update DirectoryListbox DirectoryListBoxSource->Update(); }

When this code runs, it will go through the directory selected by the user and delete all files and children files within that directory. All the files are transferred to the Recycle Bin. To set up SHFileOperation (), it's important to use the FOF_ALLOWUNDO flag for the fFlags attribute in combination with FO_DELETE flag for the wFunc attribute.

The last two examples provided thus far have demonstrated how powerful and flexible the SH x functions can be. You might find other SH x functions useful, such as SHEmptyRecycleBin() , SHQueryRecycleBin() , and SHBrowseForFolder() .

It would take a whole book to cover Shell programming. It's recommended that you refer to C++Builder's Win32 API Reference Guide for more information and experiment with some of the undocumented Shell features contained in the SHELL32.DLL , but, of course, use extreme caution. If not used properly, the Shell API can cause some serious problems such as deleted or misplaced files.


   
Top


C++ Builder Developers Guide
C++Builder 5 Developers Guide
ISBN: 0672319721
EAN: 2147483647
Year: 2002
Pages: 253

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net