|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionThis article introduces shell programming with C#. I'll be honest with you, this part has little fun in it, but it is essential that some aspects of the shell programming using C# will be clear before moving to the real stuff. If you want more details about shell programming I suggest you search msdn.microsoft.com using "Shell Programming" as the search term. While writing this article I realized it's to complicate to explain the shell and then explain how to do it in C#. you MUST read the following MSDN articles, they explain the basics of the shell, I promise that it will be worth it:
This article assumes that you read those article. So, what is shell programming? In a few words, shell programming means using the Windows platform and extending the Windows shell. I'll give some example for using and extending the shell. Ever used the "open file" dialog? Well, how do you think this dialog is the same in most of the applications? this dialog and more (open, save, font, color, printer) are dialogs that comes with windows and can be used with a set of API functions, called Shell API. I'll give another example for using the shell API. suppose you want to find the Windows directory or the My Documents directory or even the folder where you can put the files that are waiting to be written to CD (exists only on XP). In the past you used the environment variables but not all the information is there and its not the way Microsoft encourages. The correct way is using the Shell API to get this information. So far so good, using the Shell API is nice but extending the shell is a totally different story. Extending the shell means that you can put your own menu commands on the context menu when you right click a file in the explorer (WinZip does it, allowing you to select files and compress them from the explorer). Developing shell extensions is the way you integrate your application into the Windows platform. More examples for shell extensions includes: customize a file class's icon and shortcut menu; customize the appearance of a folder; integrate your application's cleanup procedures with the shell's disk cleanup manager; customize the way Webview displays the contents of a folder, creating custom Explorer bars - tool bands and desk bands; using the Active Desktop object; creating Control Panel applications; and many more... Well then, lets get to work. Main GoalsIn this article we will describe some of the basic functions and
interfaces that involves the shell and we will see how to use them with C#. Then
I'll introduce a library I have written called Interfaces we will review in the first section: Functions we will review in the first section: Interfaces we will review in the second section: Functions we will review in the second section: The second section of the article talks about the class I've written that wraps the 'Browse for Folder' dialog, its supports:
Note: the picture above shows an example of this window, with a custom filter for showing only txt and bmp files. Note 2: Main parts pf the article is using functions that exists only on win2k and XP, the Custom Filtering exists ONLY on XP. Section 1: Getting to Know the Basics of ShellInterface: IMallocMSDN Description: The C# definition:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00000002-0000-0000-C000-000000000046")]
public interface IMalloc
{
// Allocates a block of memory.
// Return value: a pointer to the allocated memory block.
[PreserveSig]
IntPtr Alloc(
UInt32 cb); // Size, in bytes, of the memory block to be allocated.
// Changes the size of a previously allocated memory block.
// Return value: Reallocated memory block
[PreserveSig]
IntPtr Realloc(
IntPtr pv, // Pointer to the memory block to be reallocated.
UInt32 cb); // Size of the memory block (in bytes) to be reallocated.
// Frees a previously allocated block of memory.
[PreserveSig]
void Free(
IntPtr pv); // Pointer to the memory block to be freed.
// This method returns the size (in bytes) of a memory block previously
// allocated with IMalloc::Alloc or IMalloc::Realloc.
// Return value: The size of the allocated memory block in bytes
[PreserveSig]
UInt32 GetSize(
IntPtr pv); // Pointer to the memory block for which the size
// is requested.
// This method determines whether this allocator was used to allocate
// the specified block of memory.
// Return value: 1 - allocated 0 - not allocated by this IMalloc instance.
[PreserveSig]
Int16 DidAlloc(
IntPtr pv); // Pointer to the memory block
// This method minimizes the heap as much as possible by releasing unused
// memory to the operating system,
// coalescing adjacent free blocks and committing free pages.
[PreserveSig]
void HeapMinimize();
}
Explaining the code: The A few words: the common use of this interface is when you get a PIDL from a function and you need to free its
memory. In this case you need to use Function: SHGetMallocMSDN Description: Retrieves a pointer to the Shell's
C# definition: // Retrieves a pointer to the Shell's IMalloc interface.
[DllImport("shell32.dll")]
public static extern Int32 SHGetMalloc(
out IntPtr hObject); // Address of a pointer that receives the Shell's
// IMalloc interface pointer.
Example of usage: // Get IMalloc interface
IntPtr ptrRet;
SHGetMalloc(out ptrRet);
System.Type mallocType = System.Type.GetType("IMalloc");
Object obj = Marshal.GetTypedObjectForIUnknown(ptrRet,mallocType);
IMalloc pMalloc = (IMalloc)obj;
// Get a PIDL
IntPtr pidlRoot;
SHGetFolderLocation(IntPtr.zero,CSIDL_WINDOWS,IntPtr.Zero,0,out pidlRoot);
// Use the IMalloc object to free PIDL
if (pidlRoot != IntPtr.Zero)
pMalloc.Free(pidlRoot);
// Free the IMalloc object
System.Runtime.InteropServices.Marshal.ReleaseComObject(pMalloc);
Explaining the code: Well the code starts by using the Function: SHGetFolderLocationMSDN Description: Retrieves the path of a folder as an ITEMIDLIST structure (PIDL). C# definition: // Retrieves the path of a folder as an PIDL.
[DllImport("shell32.dll")]
public static extern Int32 SHGetFolderLocation(
IntPtr hwndOwner, // Handle to the owner window.
Int32 nFolder, // A CSIDL value that identifies the folder to be
// located.
IntPtr hToken, // Token that can be used to represent a particular
// user.
UInt32 dwReserved, // Reserved.
out IntPtr ppidl); // Address of a pointer to an item identifier list
// structure
// specifying the folder's location relative to the
// root of the namespace
// (the desktop).
Example of usage: // Get a PIDL
IntPtr pidlRoot;
SHGetFolderLocation(IntPtr.zero,CSIDL_WINDOWS,IntPtr.Zero,0,out pidlRoot);
A few words: this function return the PIDL of the requested special folder.
the requested folder is specified in the nFolder parameter. In the library
Function: SHGetPathFromIDListMSDN Description: Converts an item identifier list to a file system path. C# definition: // Converts an item identifier list to a file system path.
[DllImport("shell32.dll")]
public static extern Int32 SHGetPathFromIDList(
IntPtr pidl, // Address of an item identifier list that
// specifies a file or directory location
// relative to the root of the namespace (the
// desktop).
StringBuilder pszPath); // Address of a buffer to receive the file system
// path.
Example of usage: IntPtr pidlRoot;
SHGetFolderLocation(IntPtr.zero,CSIDL_WINDOWS,IntPtr.Zero,0,out pidlRoot);
System.Text.StringBuilder path = new System.Text.StringBuilder(256);
SHGetPathFromIDList(pidlRoot,path);
Explaining the code: first we get the PIDL of the windows folder, then we create a buffer for the result and get the path of the PIDL. Note this function will work only on PIDL's that represents file or folders in the file system. Function: SHGetFolderPathMSDN Description: Takes the CSIDL of a folder and returns the pathname. C# definition: // Takes the CSIDL of a folder and returns the pathname.
[DllImport("shell32.dll")]
public static extern Int32 SHGetFolderPath(
IntPtr hwndOwner, // Handle to an owner window.
Int32 nFolder, // A CSIDL value that identifies the folder whose
// path is to be retrieved.
IntPtr hToken, // An access token that can be used to represent
// a particular user.
UInt32 dwFlags, // Flags to specify which path is to be returned.
// It is used for cases where
// the folder associated with a CSIDL may be moved
// or renamed by the user.
StringBuilder pszPath); // Pointer to a null-terminated string which will
// receive the path.
Example of usage: System.Text.StringBuilder path = new System.Text.StringBuilder(256);
SHGetFolderPath(IntPtr.Zero,CSIDL_WINDOWS,IntPtr.Zero,SHGFP_TYPE_CURRENT,path);
Explaining the code: well, this is quite simple, this function just do the work
quicker. but the two examples are identical.
Function: SHParseDisplayNameMSDN Description: Translates a Shell namespace object's display name into an item identifier list and returns the attributes of the object. This function is the preferred method to convert a string to a pointer to an item identifier list (PIDL). C# definition: // Translates a Shell namespace object's display name into an item
// identifier list and returns the attributes of the object. This function is
// the preferred method to convert a string to a pointer to an item identifier
// list (PIDL).
[DllImport("shell32.dll")]
public static extern Int32 SHParseDisplayName(
[MarshalAs(UnmanagedType.LPWStr)]
String pszName, // Pointer to a zero-terminated wide string that
// contains the display name
// to parse.
IntPtr pbc, // Optional bind context that controls the parsing
// operation. This parameter
// is normally set to NULL.
out IntPtr ppidl, // Address of a pointer to a variable of type
// ITEMIDLIST that receives the item
// identifier list for the object.
UInt32 sfgaoIn, // ULONG value that specifies the attributes to
// query.
out UInt32 psfgaoOut); // Pointer to a ULONG. On return, those attributes
// that are true for the
// object and were requested in sfgaoIn will be set.
Example of usage: ShellLib.IMalloc pMalloc;
pMalloc = ShellLib.ShellFunctions.GetMalloc();
IntPtr pidlRoot;
String sPath = @"c:\temp\divx";
uint iAttribute;
ShellLib.ShellApi.SHParseDisplayName(sPath,IntPtr.Zero,out pidlRoot,0,
out iAttribute);
if (pidlRoot != IntPtr.Zero)
pMalloc.Free(pidlRoot);
System.Runtime.InteropServices.Marshal.ReleaseComObject(pMalloc);
Explaining the code: Suppose you want a PIDL of the my documents
folder, we already seen how this is done, we got a function called Interface: IShellFolderMSDN Description: The C# definition: [ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214E6-0000-0000-C000-000000000046")]
public interface IShellFolder
{
// Translates a file object's or folder's display name into an item identifier list.
// Return value: error code, if any
[PreserveSig]
Int32 ParseDisplayName(
IntPtr hwnd, // Optional window handle
IntPtr pbc, // Optional bind context that controls the
// parsing operation. This parameter is
// normally set to NULL.
[MarshalAs(UnmanagedType.LPWStr)]
String pszDisplayName, // Null-terminated UNICODE string with the
// display name.
ref UInt32 pchEaten, // Pointer to a ULONG value that receives the
// number of characters of the
// display name that was parsed.
out IntPtr ppidl, // Pointer to an ITEMIDLIST pointer that receives
// the item identifier list for
// the object.
ref UInt32 pdwAttributes); // Optional parameter that can be used to
// query for file attributes.
// this can be values from the SFGAO enum
// Allows a client to determine the contents of a folder by creating an item
// identifier enumeration object and returning its IEnumIDList interface.
// Return value: error code, if any
[PreserveSig]
Int32 EnumObjects(
IntPtr hwnd, // If user input is required to perform the
// enumeration, this window handle
// should be used by the enumeration object as
// the parent window to take
// user input.
Int32 grfFlags, // Flags indicating which items to include in the
// enumeration. For a list
// of possible values, see the SHCONTF enum.
out IntPtr ppenumIDList); // Address that receives a pointer to the
// IEnumIDList interface of the
// enumeration object created by this method.
// Retrieves an IShellFolder object for a subfolder.
// Return value: error code, if any
[PreserveSig]
Int32 BindToObject(
IntPtr pidl, // Address of an ITEMIDLIST structure (PIDL)
// that identifies the subfolder.
IntPtr pbc, // Optional address of an IBindCtx interface on
// a bind context object to be
// used during this operation.
Guid riid, // Identifier of the interface to return.
out IntPtr ppv); // Address that receives the interface pointer.
// Requests a pointer to an object's storage interface.
// Return value: error code, if any
[PreserveSig]
Int32 BindToStorage(
IntPtr pidl, // Address of an ITEMIDLIST structure that
// identifies the subfolder relative
// to its parent folder.
IntPtr pbc, // Optional address of an IBindCtx interface on a
// bind context object to be
// used during this operation.
Guid riid, // Interface identifier (IID) of the requested
// storage interface.
out IntPtr ppv); // Address that receives the interface pointer specified by riid.
// Determines the relative order of two file objects or folders, given their
// item identifier lists. Return value: If this method is successful, the
// CODE field of the HRESULT contains one of the following values (the code
// can be retrived using the helper function GetHResultCode): Negative A
// negative return value indicates that the first item should precede
// the second (pidl1 < pidl2).
// Positive A positive return value indicates that the first item should
// follow the second (pidl1 > pidl2). Zero A return value of zero
// indicates that the two items are the same (pidl1 = pidl2).
[PreserveSig]
Int32 CompareIDs(
Int32 lParam, // Value that specifies how the comparison
// should be performed. The lower
// Sixteen bits of lParam define the sorting rule.
// The upper sixteen bits of
// lParam are used for flags that modify the
// sorting rule. values can be from
// the SHCIDS enum
IntPtr pidl1, // Pointer to the first item's ITEMIDLIST structure.
IntPtr pidl2); // Pointer to the second item's ITEMIDLIST structure.
// Requests an object that can be used to obtain information from or interact
// with a folder object.
// Return value: error code, if any
[PreserveSig]
Int32 CreateViewObject(
IntPtr hwndOwner, // Handle to the owner window.
Guid riid, // Identifier of the requested interface.
out IntPtr ppv); // Address of a pointer to the requested interface.
// Retrieves the attributes of one or more file objects or subfolders.
// Return value: error code, if any
[PreserveSig]
Int32 GetAttributesOf(
UInt32 cidl, // Number of file objects from which to retrieve
// attributes.
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)]
IntPtr[] apidl, // Address of an array of pointers to ITEMIDLIST
// structures, each of which
// uniquely identifies a file object relative to
// the parent folder.
ref UInt32 rgfInOut); // Address of a single ULONG value that, on entry,
// contains the attributes that
// the caller is requesting. On exit, this value
// contains the requested
// attributes that are common to all of the
// specified objects. this value can
// be from the SFGAO enum
// Retrieves an OLE interface that can be used to carry out actions on the
// specified file objects or folders.
// Return value: error code, if any
[PreserveSig]
Int32 GetUIObjectOf(
IntPtr hwndOwner, // Handle to the owner window that the client
// should specify if it displays
// a dialog box or message box.
UInt32 cidl, // Number of file objects or subfolders specified
// in the apidl parameter.
IntPtr[] apidl, // Address of an array of pointers to ITEMIDLIST
// structures, each of which
// uniquely identifies a file object or subfolder
// relative to the parent folder.
Guid riid, // Identifier of the COM interface object to return.
ref UInt32 rgfReserved, // Reserved.
out IntPtr ppv); // Pointer to the requested interface.
// Retrieves the display name for the specified file object or subfolder.
// Return value: error code, if any
[PreserveSig]
Int32 GetDisplayNameOf(
IntPtr pidl, // Address of an ITEMIDLIST structure (PIDL)
// that uniquely identifies the file
// object or subfolder relative to the parent folder.
UInt32 uFlags, // Flags used to request the type of display name
// to return. For a list of
// possible values, see the SHGNO enum.
out ShellApi.STRRET pName); // Address of a STRRET structure in which to
// return the display name.
// Sets the display name of a file object or subfolder, changing the item
// identifier in the process.
// Return value: error code, if any
[PreserveSig]
Int32 SetNameOf(
IntPtr hwnd, // Handle to the owner window of any dialog or
// message boxes that the client
// displays.
IntPtr pidl, // Pointer to an ITEMIDLIST structure that uniquely
// identifies the file object
// or subfolder relative to the parent folder.
[MarshalAs(UnmanagedType.LPWStr)]
String pszName, // Pointer to a null-terminated string that
// specifies the new display name.
UInt32 uFlags, // Flags indicating the type of name specified by
// the lpszName parameter. For a list of possible
// values, see the description of the SHGNO enum.
out IntPtr ppidlOut); // Address of a pointer to an ITEMIDLIST structure
// which receives the new ITEMIDLIST.
}
Example of usage: int retVal;
ShellLib.IMalloc pMalloc;
pMalloc = ShellLib.ShellFunctions.GetMalloc();
IntPtr pidlSystem;
retVal = ShellLib.ShellApi.SHGetFolderLocation(
IntPtr.Zero,
(int)ShellLib.ShellApi.CSIDL.CSIDL_SYSTEM,
IntPtr.Zero,
0,
out pidlSystem);
IntPtr ptrParent;
IntPtr pidlRelative = IntPtr.Zero;
retVal = ShellLib.ShellApi.SHBindToParent(
pidlSystem,
ShellLib.ShellGUIDs.IID_IShellFolder,
out ptrParent,
ref pidlRelative);
System.Type shellFolderType = ShellLib.ShellFunctions.GetShellFolderType();
Object obj = System.Runtime.InteropServices.Marshal.GetTypedObjectForIUnknown(
ptrParent,shellFolderType);
ShellLib.IShellFolder ishellParent = (ShellLib.IShellFolder)obj;
ShellLib.ShellApi.STRRET ptrString;
retVal = ishellParent.GetDisplayNameOf(pidlRelative,
(uint)ShellLib.ShellApi.SHGNO.SHGDN_NORMAL, out ptrString);
System.Text.StringBuilder strDisplay = new System.Text.StringBuilder(256);
retVal = ShellLib.ShellApi.StrRetToBuf(ref ptrString ,pidlSystem,strDisplay,
(uint)strDisplay.Capacity);
System.Runtime.InteropServices.Marshal.ReleaseComObject(ishellParent);
pMalloc.Free(pidlSystem);
System.Runtime.InteropServices.Marshal.ReleaseComObject(pMalloc);
Explaining the code: in this sample I've called Function: SHGetDesktopFolderMSDN Description: Retrieves the
C# definition: // Retrieves the IShellFolder interface for the desktop folder,
//which is the root of the Shell's namespace.
[DllImport("shell32.dll")]
public static extern Int32 SHGetDesktopFolder(
out IntPtr ppshf); // Address that receives an IShellFolder interface
// pointer for the
// desktop folder.
Example of usage: public static IShellFolder GetDesktopFolder() { IntPtr ptrRet; ShellApi.SHGetDesktopFolder(out ptrRet); System.Type shellFolderType = System.Type.GetType("ShellLib.IShellFolder"); Object obj = Marshal.GetTypedObjectForIUnknown(ptrRet,shellFolderType); IShellFolder ishellFolder = (IShellFolder)obj; return ishellFolder; } { ... ShellLib.IShellFolder pShellFolder; pShellFolder = ShellLib.ShellFunctions.GetDesktopFolder();3 IntPtr pidlRoot; ShellLib.ShellApi.SHGetFolderLocation( IntPtr.Zero, (short)ShellLib.ShellApi.CSIDL.CSIDL_SYSTEM, IntPtr.Zero, 0, out pidlRoot); ShellLib.ShellApi.STRRET ptrDisplayName; pShellFolder.GetDisplayNameOf( pidlRoot, (uint)ShellLib.ShellApi.SHGNO.SHGDN_NORMAL | (uint)ShellLib.ShellApi.SHGNO.SHGDN_FORPARSING, out ptrDisplayName); String sDisplay; ShellLib.ShellApi.StrRetToBSTR(ref ptrDisplayName,pidlRoot,out sDisplay); System.Runtime.InteropServices.Marshal.ReleaseComObject(pShellFolder); } Explaining the code: well, here I've made a function called Function: SHBindToParentMSDN Description: This function takes the fully-qualified pointer to an item identifier list (PIDL) of a namespace object, and returns a specified interface pointer on the parent object. C# definition: // This function takes the fully-qualified pointer to an item
// identifier list (PIDL) of a namespace object, and returns a specified
// interface pointer on the parent object.
[DllImport("shell32.dll")]
public static extern Int32 SHBindToParent(
IntPtr pidl, // The item's PIDL.
[MarshalAs(UnmanagedType.LPStruct)]
Guid riid, // The REFIID of one of the interfaces exposed by
// the item's parent object.
out IntPtr ppv, // A pointer to the interface specified by riid. You
// must release the object when
// you are finished.
ref IntPtr ppidlLast); // The item's PIDL relative to the parent folder. This
// PIDL can be used with many
// of the methods supported by the parent folder's
// interfaces. If you set ppidlLast
// to NULL, the PIDL will not be returned.
A few words: I will not give an example of using this function cause I
already gave one in the Function: StrRetToBSTRMSDN Description: Accepts a
C# definition: // Accepts a STRRET structure returned by
// ShellFolder::GetDisplayNameOf that contains or points to a string, and then
// returns that string as a BSTR.
[DllImport("shlwapi.dll")]
public static extern Int32 StrRetToBSTR(
ref STRRET pstr, // Pointer to a STRRET structure.
IntPtr pidl, // Pointer to an ITEMIDLIST uniquely identifying a file
// object or subfolder relative
// to the parent folder.
[MarshalAs(UnmanagedType.BStr)]
out String pbstr); // Pointer to a variable of type BSTR that contains the
// converted string.
Function: StrRetToBufMSDN Description: Takes a
C# definition: // Takes a STRRET structure returned by IShellFolder::GetDisplayNameOf,
// converts it to a string, and places the result in a buffer.
[DllImport("shlwapi.dll")]
public static extern Int32 StrRetToBuf(
ref STRRET pstr, // Pointer to the STRRET structure. When the function
// returns, this pointer will no
// longer be valid.
IntPtr pidl, // Pointer to the item's ITEMIDLIST structure.
StringBuilder pszBuf, // Buffer to hold the display name. It will be returned
// as a null-terminated
// string. If cchBuf is too small, the name will be
// truncated to fit.
UInt32 cchBuf); // Size of pszBuf, in characters. If cchBuf is too small,
// the string will be
// truncated to fit.
Section 2: ShellBrowseForFolderDialog - A Class that Wraps a DialogHow do we get the Browse for Folder dialog? the answer relies in a
Shell API function called: // Displays a dialog box that enables the user to select a Shell folder.
[DllImport("shell32.dll")]
public static extern IntPtr SHBrowseForFolder(
ref BROWSEINFO lbpi); // Pointer to a BROWSEINFO structure that contains
// information used to display
// the dialog box.
That's nice, the return value is a PIDL to the selected shell item (remember,
can be a file, folder or a virtual folder), later in the class we will see how
we return the display name of the selected PIDL. and the // Contains parameters for the SHBrowseForFolder function and
// receives information about the folder selected
// by the user.
[StructLayout(LayoutKind.Sequential)]
public struct BROWSEINFO
{
public IntPtr hwndOwner; // Handle to the owner window for the
// dialog box.
public IntPtr pidlRoot; // Pointer to an item identifier list
// (PIDL) specifying the location of
// the root folder from which to start
// browsing.
[MarshalAs(UnmanagedType.LPStr)] // Address of a buffer to receive the
// display name of the
public String pszDisplayName; // folder selected by the user.
[MarshalAs(UnmanagedType.LPStr)] // Address of a null-terminated string
// that is displayed
public String lpszTitle; // above the tree view control in the
// dialog box.
public UInt32 ulFlags; // Flags specifying the options for the
// dialog box.
[MarshalAs(UnmanagedType.FunctionPtr)] // Address of an application-defined
// function that the
public BrowseCallbackProc lpfn; // dialog box calls when an event occurs.
public Int32 lParam; // Application-defined value that the
// dialog box passes to
// the callback function
public Int32 iImage; // Variable to receive the image
// associated with the selected folder.
}
The So, all you need to do, is create a Setting main membersThe class /// <summary> Handle to the owner window for the dialog box. </summary>
public IntPtr hwndOwner;
/// <summary> Select the root type </summary>
public RootTypeOptions RootType;
/// <summary> valid only if RootType is RootTypeOptions.ByPath </summary>
public string RootPath;
/// <summary> valid only if RootType is RootTypeOptions.BySpecialFolder </summary>
public ShellApi.CSIDL RootSpecialFolder;
/// <summary>
/// Address of a null-terminated string that is displayed above the tree view
/// control in the dialog box.
/// </summary>
public string Title;
/// <summary> Token that can be used to represent a particular user. </summary>
public IntPtr UserToken;
Finally, the Setting Flag MembersRemember the flags member of the /// <summary>
Only return computers. If the user selects anything other than a computer,
/// the OK button is grayed. </summary>
public bool BrowseForComputer;
/// <summary>
Only return printers. If the user selects anything other than a printer, the
/// OK button is grayed.</summary>
public bool BrowseForPrinter;
/// <summary> The browse dialog box will display files as well as folders.
/// </summary>
public bool IncludeFiles;
/// <summary>
/// The browse dialog box can display URLs. The BIF_USENEWUI and
/// BIF_BROWSEINCLUDEFILES flags must also be set. If these three flags are not
/// set, the browser dialog box will reject URLs. Even when these flags are set,
/// the browse dialog box will only display URLs if the folder that contains the
/// selected item supports them. When the folder's IShellFolder::GetAttributesOf
/// method is called to request the selected item's attributes, the folder must
/// set the SFGAO_FOLDER attribute flag. Otherwise, the browse dialog box will
/// not display the URL. </summary>
public bool IncludeUrls;
/// <summary> Do not include network folders below the domain level in the
/// dialog box's tree view control. </summary>
public bool DontGoBelowDomain;
/// <summary> Include an edit control in the browse dialog box that allows the
/// user to type the name of an item. </summary>
public bool EditBox;
/// <summary> Use the new user interface. Setting this flag provides the user
/// with a larger dialog box that can be resized. The dialog box has several new
/// capabilities including: drag and drop capability within the dialog box,
/// reordering, shortcut menus, new folders, delete, and other shortcut menu
/// commands. </summary>
public bool NewDialogStyle;
/// <summary> Do not include the New Folder button in the browse dialog box.
/// </summary>
public bool NoNewFolderButton;
/// <summary> When the selected item is a shortcut, return the PIDL of the
/// shortcut itself rather than its target. </summary>
public bool NoTranslateTargets;
/// <summary> Only return file system ancestors. An ancestor is a subfolder that
/// is beneath the root folder in the namespace hierarchy. If the user selects an
/// ancestor of the root folder that is not part of the file system, the OK button
/// is grayed. </summary>
public bool ReturnOnlyFileSystemAncestors;
/// <summary> Only return file system directories. If the user selects folders
/// that are not part of the file system, the OK button is grayed. </summary>
public bool ReturnOnlyFileSystemDirs;
/// <summary> The browse dialog box can display shareable resources on remote
/// systems. It is intended for applications that want to expose remote shares on a
/// local system. The BIF_USENEWUI flag must also be set. </summary>
public bool Shareable;
/// <summary> Include a status area in the dialog box. The callback function can
/// set the status text by sending messages to the dialog box. </summary>
public bool StatusText;
/// <summary> When combined with BIF_NEWDIALOGSTYLE, adds a usage hint to the
/// dialog box in place of the edit box. BIF_EDITBOX overrides this flag. </summary>
public bool UsageHint;
/// <summary> Use the new user interface, including an edit box. This flag is
/// equivalent to BIF_EDITBOX | BIF_NEWDIALOGSTYLE. </summary>
public bool UseNewUI;
/// <summary> If the user types an invalid name into the edit box, the browse
/// dialog box will call the application's BrowseCallbackProc with the
/// BFFM_VALIDATEFAILED message. This flag is ignored if BIF_EDITBOX is not
/// specified. </summary>
public bool Validate;
Well, I'm not going to go over the flags one by one cause they are quite
simple and has a quick explanation near them. Also you can just test what
happens by setting the flag. Another important code about the flags is the
function that gives me the flags value when I need to call the private UInt32 GetFlagsValue()
{
UInt32 flags = 0;
if (BrowseForComputer) flags |= (uint)ShellApi.BIF.BIF_BROWSEFORCOMPUTER;
if (BrowseForPrinter) flags |= (uint)ShellApi.BIF.BIF_BROWSEFORPRINTER;
if (IncludeFiles) flags |= (uint)ShellApi.BIF.BIF_BROWSEINCLUDEFILES;
if (IncludeUrls) flags |= (uint)ShellApi.BIF.BIF_BROWSEINCLUDEURLS;
if (DontGoBelowDomain) flags |= (uint)ShellApi.BIF.BIF_DONTGOBELOWDOMAIN;
if (EditBox) flags |= (uint)ShellApi.BIF.BIF_EDITBOX;
if (NewDialogStyle) flags |= (uint)ShellApi.BIF.BIF_NEWDIALOGSTYLE;
if (NoNewFolderButton) flags |= (uint)ShellApi.BIF.BIF_NONEWFOLDERBUTTON;
if (NoTranslateTargets) flags |= (uint)ShellApi.BIF.BIF_NOTRANSLATETARGETS;
if (ReturnOnlyFileSystemAncestors) flags |= (uint)ShellApi.BIF.BIF_RETURNFSANCESTORS;
if (ReturnOnlyFileSystemDirs) flags |= (uint)ShellApi.BIF.BIF_RETURNONLYFSDIRS;
if (Shareable) flags |= (uint)ShellApi.BIF.BIF_SHAREABLE;
if (StatusText) flags |= (uint)ShellApi.BIF.BIF_STATUSTEXT;
if (UsageHint) flags |= (uint)ShellApi.BIF.BIF_UAHINT;
if (UseNewUI) flags |= (uint)ShellApi.BIF.BIF_USENEWUI;
if (Validate) flags |= (uint)ShellApi.BIF.BIF_VALIDATE;
return flags;
}
Update: As you have seen, the code in this section is quite ugly, and a little bird told me that it could be done in a nice simple way. So I've changed the code and the thanks goes to leppie for his great suggestion. So the changes are: the BIF enum has now the [Flags] attribute, and also I've changed its name to BrowseInfoFlags. And in the dialog class there is a member called
public BrowseInfoFlag DetailsFlags;
And so now, when you want to set some flags you do something like: DetailsFlags = BrowseInfoFlag.BIF_BROWSEINCLUDEFILES
| BrowseInfoFlag.BIF_EDITBOX
| BrowseInfoFlag.BIF_NEWDIALOGSTYLE
| BrowseInfoFlag.BIF_SHAREABLE
| BrowseInfoFlag.BIF_STATUSTEXT
| BrowseInfoFlag.BIF_USENEWUI
| BrowseInfoFlag.BIF_VALIDATE;
Showing the Dialog and Getting the ResultAfter we set the class properties we will now want to show the dialog, and
then getting the selected item has a normal path. in the Then we create a We are almost done. If we want to be good developers so we need to
free the PIDL's that return to us. To do that we need to Get the The following code is what I do, go thru it and see that it's
clear. the part when I set the function pointer of the 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);
}
Using messages and eventsWell, remember the line that set a delegate function to the public delegate Int32 BrowseCallbackProc(IntPtr hwnd, UInt32 uMsg, Int32 lParam, Int32 lpData);
This is the way the Shell gives you notification of events. What you need to do is declare a function of this type, Set the function pointer to your function, and when an event occurs the shell will notify your function with the correct message. There are 4 types of messages:
So, these are the notification you can receive. In my class what I've done is I don't let you specify that function cause this is ugly. what I do is defining 4 delegates properties, so if you want to get a notificaton you only need to create an event handler and set the class to use your event handler. This also allows me to translate the pointers to normal strings and passing your function normal values. The delegates and their arguments are declared as follows: public class InitializedEventArgs : EventArgs
{
public InitializedEventArgs(IntPtr hwnd)
{
this.hwnd = hwnd;
}
public readonly IntPtr hwnd;
}
public class IUnknownEventArgs : EventArgs
{
public IUnknownEventArgs(IntPtr hwnd, IntPtr iunknown)
{
this. | ||||||||||||||||||||