Shell Programming Using C#
Shell Programming Using C#
What is Shell ?
A shell is a command line interpretor, it takes commands and executes them.
What is Shell Programing ?
Shell Programming is creating a file containing a set of commands to be executed (run) by the shell in sequence as it reads the file along with our own menu commands.
Example :
- Opening a Dialog Box to select a file is a kind of shell concept which passes the commands to the windows and extends using Shell.
Shell Programming is to extend the shell concepts and to include our own commands in the Context Menu.
To Learn More About Shell and Shell Concepts Please Read the following MSDN articles
In this Article i would like to give you an extract of the Shell Programming Concepts that i learnt. Shell Programming concepts that i am giving here doesnot have basic things like what are variables in Shell, what are the statements in Shell. It contains examples which clearly depicts you the use of Shell Programming and the underlying concepts.
Hope you looked at the MSDN articles for Shell Concepts…
It would have given you an basic idea about Shell and the Programming Concepts.
All the below sections uses the separate GUID’s for them as they have com components implemented they use GUID’s and they play major role in displaying appropriate result.
First Section : Showing a Folder Browser Dialog Customized using Shell.
Second Section : Creating Shell Extended Application for File Operatons.
Third Section : To Create an Application Desktop Tool Bar.
Fourth Section : Creating an AutoCompleteSection.
First Section : Showing a Folder Browser Dialog Customized using Shell
Let me start the discussion here.
Here i gonna discuss about the BrowseFolderDialog Box.
In C#.Net we have a control FolderBrowserDialog which when used it will show an Folder Browser Dialog which will be let u select the files or folders by browsing through the folders in the dialog.
While using the Shell we can add our own commands to the Dialog, by our simple C# code we cannot add the commands to the Dialog.
So we are using Shell to extend its usage to add our own commands, display texts, Text Changes, enabling the Buttons and Texts to appear or disappear and also to filter the files to be displayed in the dialog.
The Following are the functions that enables the Browser Dialog to be added in them.
FilterUsingExtension
The Function allows the client to specify which item has to be enumerated and the method is called for each item in the folder. This Returns Ok to have the item enumerated. Returns false to prevent the item from enumerated. This enables the filtering of the Extension files which can be derived from the enumerated items.
InterfaceFolderFilter
This Functions sends the parameter that are enumerated and they send the values to the Window handle to Filter the Folders.
InterfaceFolderFilterSite
It enables the Caller Function to pass the Enumerated Pointer and Iunknown parameters.
InterfaceMalloc
This allows the process of Memory allocation with the Interface and the Deallocation of memory according to the size and bytes of memeory occupied. This enables the use of creating the access towards the Memory and the Parameters passed
InterfaceShellFolder
This function translates the folder’s display name into an item identifier list, which then passed to show folders in the dialog box.
ShellApifunc
This function has the parameters for the ShellBrowseForFolderDialog and sends the parameters for the Folders selected in them.
ShellBrowseForFolderDialog
The function gets the parameters and the SendingMessage function calls the window and they return when the process has completed.
-
Shell Programming influences on the Following:
-
Showing ShellBrowseFolderDialog with customizations.
-
Getting Folder Location according to ShellGetFolderLocation
-
Getting Folder Path according to ShellGetFolderPath
-
Setting Flags and Members.
-
Custom filter of files.
-
Displaying Dialog and Getting Results.
Now here you are going to be presented with the awaited code for creating ShellBrowser Dialog which creates Shell Browse For Folder Dialog.Using this interdace we extend the Shell Properties with the Browse For Folder dialog and add our own commands and texts.
public class ShellBrowseForFolderDialogDemo{
// The SendMessage function sends the specified message to a window or windows. It calls the window procedure for
// the specified window and does not return until the window procedure has processed the message.
[DllImport("User32.dll")]
public static extern Int32 SendingMessage(
IntPtr Windowhndle // handle to destination window
UInt32 Message, // message
UInt32 MessageParameter, // first message parameter
Int32 MessageParameter2 // second message parameter
);
[DllImport("User32.dll")]
public static extern Int32 SendingMessage(
IntPtr Windowhndle, // handle to destination window
UInt32 Message, // message
UInt32 MessageParameter, // first message parameter
[MarshalAs(UnmanagedType.LPWStr)]
String lParam // second message parameter
);
public enum RootTypeOptions
{
BySpecialFolder,
ByPath
}
[Flags]
public enum BrowseInfoFlag // BIF
{
BIF_RETURNONLYFSDIRS = 0x0001, // For finding a folder to start document searching
BIF_DONTGOBELOWDOMAIN = 0x0002, // For starting the Find Computer
BIF_STATUSTEXT = 0x0004, // Top of the dialog has 2 lines of text for BROWSEINFO.lpszTitle and
// one line if this flag is set. Passing the message
// BFFM_SETSTATUSTEXTA to the hwnd can set the rest of the text.
// This is not used with BIF_USENEWUI and BROWSEINFO.lpszTitle gets
// all three lines of text.
BIF_RETURNFSANCESTORS = 0x0008,
BIF_EDITBOX = 0x0010, // Add an editbox to the dialog
BIF_VALIDATE = 0x0020, // insist on valid result (or CANCEL)
BIF_NEWDIALOGSTYLE = 0x0040, // Use the new dialog layout with the ability to resize
// Caller needs to call OleInitialize() before using this API
BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX),
BIF_BROWSEINCLUDEURLS = 0x0080, // Allow URLs to be displayed or entered. (Requires BIF_USENEWUI)
BIF_UAHINT = 0x0100, // Add a UA hint to the dialog, in place of the edit box. May not be
// combined with BIF_EDITBOX
BIF_NONEWFOLDERBUTTON = 0x0200, // Do not add the "New Folder" button to the dialog. Only applicable
// with BIF_NEWDIALOGSTYLE.
BIF_NOTRANSLATETARGETS = 0x0400, // don't traverse target as shortcut
BIF_BROWSEFORCOMPUTER = 0x1000, // Browsing for Computers.
BIF_BROWSEFORPRINTER = 0x2000, // Browsing for Printers
BIF_BROWSEINCLUDEFILES = 0x4000, // Browsing for Everything
BIF_SHAREABLE = 0x8000 // sharable resources displayed (remote shares, requires BIF_USENEWUI)
}
public enum BrowseForFolderMessages // BFFM
{
// message from browser
BFFM_INITIALIZED = 1,
BFFM_SELCHANGED = 2,
BFFM_VALIDATEFAILEDA = 3, // lParam:szPath ret:1(cont),0(EndDialog)
BFFM_VALIDATEFAILEDW = 4, // lParam:wzPath ret:1(cont),0(EndDialog)
BFFM_IUNKNOWN = 5, // provides IUnknown to client. lParam: IUnknown*
// messages to browser
// 0x400 = WM_USER
BFFM_SETSTATUSTEXTA = (0x0400 + 100),
BFFM_ENABLEOK = (0x0400 + 101),
BFFM_SETSELECTIONA = (0x0400 + 102),
BFFM_SETSELECTIONW = (0x0400 + 103),
BFFM_SETSTATUSTEXTW = (0x0400 + 104),
BFFM_SETOKTEXT = (0x0400 + 105), // Unicode only
BFFM_SETEXPANDED = (0x0400 + 106) // Unicode only
}
public class InitializedEventArgs : EventArgs
{
public InitializedEventArgs(IntPtr hwnd)
{
this.hwnd = hwnd;
}
public readonly IntPtr hwnd;
}
public class InterfaceUnknownEventArgs : EventArgs
{
public IUnknownEventArgs(IntPtr hwnd, IntPtr iunknown)
{
this.hwnd = hwnd;
this.iunknown = iunknown;
}
public readonly IntPtr hwnd;
public readonly IntPtr iunknown;
}
public class SelectionChangedEventArgs : EventArgs
{
public SelectionChangedEventArgs(IntPtr hwnd, IntPtr pidl)
{
this.hwnd = hwnd;
this.pidl = pidl;
}
public readonly IntPtr hwnd;
public readonly IntPtr pidl;
}
public class ValidateFailedEventArgs : EventArgs
{
public ValidateFailedEventArgs(IntPtr hwnd, string invalidSel)
{
this.hwnd = hwnd;
this.invalidSel = invalidSel;
}
public readonly IntPtr hwnd;
public readonly string invalidSel;
}
public delegate void InitializedHandler(ShellBrowseForFolderDialog sender, InitializedEventArgs args);
public delegate void InterfaceUnknownHandler(ShellBrowseForFolderDialog sender, IUnknownEventArgs args);
public delegate void SelectionChangedHandler(ShellBrowseForFolderDialog sender, SelChangedEventArgs args);
public delegate int ValidateFailedHandler(ShellBrowseForFolderDialog sender, ValidateFailedEventArgs args);
public event InitializedHandler OnInitialized;
public event InterfaceUnknownHandler OnIUnknown;
public event SelChangedHandler OnSelChanged;
public event ValidateFailedHandler OnValidateFailed;
public void EnableOkText(IntPtr hwnd, bool Enabled)
{
SendingMessage(hwnd, (uint)BrowseForFolderMessages.BFFM_ENABLEOK, 0, Enabled ? 1 : 0);
}
public void SetExpandedProcess(IntPtr hwnd, string path)
{
SendingMessage(hwnd, (uint)BrowseForFolderMessages.BFFM_SETEXPANDED, 1, path);
}
public void SetOkTextChanged(IntPtr hwnd, string text)
{
SendingMessage(hwnd, (uint)BrowseForFolderMessages.BFFM_SETOKTEXT, 0, text);
}
public void SetSelectionChanged(IntPtr hwnd, string path)
{
SendingMessage(hwnd, (uint)BrowseForFolderMessages.BFFM_SETSELECTIONW, 1, path);
}
public void SetStatusTextProcess(IntPtr hwnd, string text)
{
SendingMessage(hwnd, (uint)BrowseForFolderMessages.BFFM_SETSTATUSTEXTW, 1, text);
}
private Int32 myBrowseCallbackProc(IntPtr hwnd, UInt32 uMsg, Int32 lParam, Int32 lpData)
{
switch ((BrowseForFolderMessages)uMsg)
{
case BrowseForFolderMessages.BFFM_INITIALIZED:
System.Diagnostics.Debug.WriteLine("BFFM_INITIALIZED");
if (OnInitialized != null)
{
InitializedEventArgs args = new InitializedEventArgs(hwnd);
OnInitialized(this,args);
}
break;
case BrowseForFolderMessages.BFFM_IUNKNOWN:
System.Diagnostics.Debug.WriteLine("BFFM_IUNKNOWN");
if (OnInterfaceUnknown != null)
{
IUnknownEventArgs args = new IUnknownEventArgs(hwnd,(IntPtr)lParam);
OnInterfaceUnknown(this,args);
}
break;
case BrowseForFolderMessages.BFFM_SELCHANGED:
System.Diagnostics.Debug.WriteLine("BFFM_SELCHANGED");
if (OnSelChanged != null)
{
SelChangedEventArgs args = new SelChangedEventArgs(hwnd,(IntPtr)lParam);
OnSelChanged(this,args);
}
break;
case BrowseForFolderMessages.BFFM_VALIDATEFAILEDA:
System.Diagnostics.Debug.WriteLine("BFFM_VALIDATEFAILEDA");
if (OnValidateFailed != null)
{
string failedSel = Marshal.PtrToStringAnsi((IntPtr)lParam);
ValidateFailedEventArgs args = new ValidateFailedEventArgs(hwnd,failedSel);
return OnValidateFailed(this,args);
}
break;
case BrowseForFolderMessages.BFFM_VALIDATEFAILEDW:
System.Diagnostics.Debug.WriteLine("BFFM_VALIDATEFAILEDW");
if (OnValidateFailed != null)
{
string failedSel = Marshal.PtrToStringUni((IntPtr)lParam);
ValidateFailedEventArgs args = new ValidateFailedEventArgs(hwnd,failedSel);
return OnValidateFailed(this,args);
}
break;
}
return 0;
}
public ShellBrowseForFolderDialogDemo()
{
hwndOwner = IntPtr.Zero;
RootType = RootTypeOptions.BySpecialFolder;
RootSpecialFolder = ShellApi.CSIDL.CSIDL_DESKTOP;
RootPath = "";
m_DisplayName = "";
Title = "";
UserToken = IntPtr.Zero;
m_FullName = "";
// Default flags values
DetailsFlags = BrowseInfoFlag.BIF_BROWSEINCLUDEFILES
| BrowseInfoFlag.BIF_EDITBOX
| BrowseInfoFlag.BIF_NEWDIALOGSTYLE
| BrowseInfoFlag.BIF_SHAREABLE
| BrowseInfoFlag.BIF_STATUSTEXT
| BrowseInfoFlag.BIF_USENEWUI
| BrowseInfoFlag.BIF_VALIDATE;
}
public void ShowDialog()
{
m_FullName = "";
m_DisplayName = "";
// Get shell's memory allocator, it is needed to free some memory later
IMalloc pMalloc;
pMalloc = ShellFunctions.GetMalloc();
IntPtr pidlRoot;
if (RootType == RootTypeOptions.BySpecialFolder)
{
ShellApi.SHGetFolderLocation(hwndOwner,(int)RootSpecialFolder,UserToken,0,out pidlRoot);
}
else // m_RootType = RootTypeOptions.ByPath
{
uint iAttribute;
ShellApi.SHParseDisplayName(RootPath,IntPtr.Zero,out pidlRoot,0,out iAttribute);
}
ShellApi.BROWSEINFO bi = new ShellApi.BROWSEINFO();
bi.hwndOwner = hwndOwner;
bi.pidlRoot = pidlRoot;
bi.pszDisplayName = new String(' ',256);
bi.lpszTitle = Title;
bi.ulFlags = (uint)DetailsFlags;
bi.lParam = 0;
bi.lpfn = new ShellApi.BrowseCallbackProc(this.myBrowseCallbackProc);
// Show dialog
IntPtr pidlSelected;
pidlSelected = ShellLib.ShellApi.SHBrowseForFolder(ref bi);
// Save the display name
m_DisplayName = bi.pszDisplayName.ToString();
IShellFolder isf = ShellFunctions.GetDesktopFolder();
ShellApi.STRRET ptrDisplayName;
isf.GetDisplayNameOf(pidlSelected,(uint)ShellApi.SHGNO.SHGDN_NORMAL | (uint)ShellApi.SHGNO.SHGDN_FORPARSING,out ptrDisplayName);
String sDisplay;
ShellLib.ShellApi.StrRetToBSTR(ref ptrDisplayName,pidlRoot,out sDisplay);
m_FullName = sDisplay;
if (pidlRoot != IntPtr.Zero)
pMalloc.Free(pidlRoot);
if (pidlSelected != IntPtr.Zero)
pMalloc.Free(pidlSelected);
Marshal.ReleaseComObject(isf);
Marshal.ReleaseComObject(pMalloc);
}
/// Handle to the owner window for the dialog box.
public IntPtr hwndOwner;
/// Select the root type
public RootTypeOptions RootType;
/// valid only if RootType is RootTypeOptions.ByPath
public string RootPath;
/// valid only if RootType is RootTypeOptions.BySpecialFolder
public ShellApi.CSIDL RootSpecialFolder;
/// Address of a buffer to receive the display name of the folder selected by the user.
public string DisplayName
{
get
{
return m_DisplayName;
}
}
private string m_DisplayName;
///
/// Address of a null-terminated string that is displayed above the tree view control in the dialog box.
///
public string Title;
/// Token that can be used to represent a particular user.
public IntPtr UserToken;
/// Return the result of the dialog
public string FullName
{
get
{
return m_FullName;
}
}
private string m_FullName;
public BrowseInfoFlag DetailsFlags;
}
The Interface Creates a FolderBrowserDialog which has the constraints like showing the filtered files and then adding our comments and commands with the dialog. The Ok button is also be changed to the text you desire.
The Interface Creates a FolderBrowserDialog which has the constraints like showing the filtered files and then adding our comments and commands with the dialog. The Ok button is also be changed to the text you desire.
The BrowseFolder enables the user to browse through the folders and select the filtered extension files.
Screen Showing Folder Browser Dialog
Fig 1.0

The Form1 will be displayed while executing the EXE. The form1 has a button called GetDialog which when clicked shows BrowseForFolderDialog.
“ Test for Shell Using ShellLibrary DLL” is the Text given by explicitly to the Shell folder Dialog, which is displayed at runtime.
The folder Text box shows the Selected Folder. In this example the Desktop folder is selected.
The “Text Changed” button is the “OK” button in which the text has been changed as “Text Changed”.
fig 1.1.

The above image only displays the txt files that is present in the desktop folder. And also note “Make New Folder” button also been shown.
We can make this happen using the shell concept extended to the ShellBrowseForFolder Dialog.
When we click on Make new folder the New Folder also been created, the new folder created is displayed in the figure fig 1.2
fig 1.2

Thus Shell extended its contribution in adding the Text in the Shell Browser Folder Dialog, and also to change the text of OK button, Filtering the files to display, and also enables the “Make New Folder” button to display or not and also creates a folder.
Conclusion for the First Section
In this Frist Section we learnt about how can we use the Shell Concepts in creating the ShowBrowseForFolderDialog with the customizations.Figures 1.0 , 1.1 , 1.2 shows you the concepts using Shell to show the BrowseForFolder Dialog.
