CODEGATOR

.NET info worth sinking your teeth into!
Welcome to CODEGATOR Sign in | Join | Help
in Search

Martin Cook

Yet another C# developer with a blog.

A managed wrapper for the Windows Shell PIDL structure

My lunch hours are spoken for this week, and probably next as well. Since I can't write anything new Sad, I thought I would dig into my old code drawer and pull out something interesting. I've noticed lately that my article related to extracting file thumbnails using the Windows Shell has been popular, so I've decided to publish one of my old wrapper classes for the Windows Shell. In this case my wrapper for the PIDL structure. If you think PIDLing is something your dog does in the park then you probably don't mess around with the Windows Shell much, and this blog entry probably isn't for you. Check back soon and with any luck I'll be focused on other topics.

 

OK, for the rest of us Windows Shell spelunking weenies let's continue on! Big Smile

Here's the quick history for the code I'm about to present: I originally wrote this class in ANSI C++ in the mid-to-late 1990's. I ported the code to C# in 2002/2003. I have perodically gone back and tweaked the resulting .NET code over time. The version I am publishing here will work with .NET 1.x, 2.x or 3.x.

This class is a decent placeholder for a Windows shell PIDL. If you are writing software that must integrate with the Shell then this code may very well prove useful. I use this class pretty much anyplace I need to grab a PIDL.

Finally, a quick apology. This code uses .NET's PInvoke mechanism to interface with the Windows Shell. Because of that, there is a bunch of COM interface and structure definitions in the listing. It's really ugly but there isn't anything I can do about it. Whenever you start working with COM interfaces in .NET you have to put up with all the PInvoke ugliness. Sorry. I have isolated all the PInvoke stuff into a nested class called NativeMethods, hopefully that helps.

 

The class has several constructors and open methods for creating PIDL's. You can use an existing PIDL, a path to a shell object, or one of the enumerated entries in Environment.SpecialFolder to describe the object you want to get a PIDL for. Once you have an open PIDL you can find out all sorts of good-to-know stuff about it, including: it's display name, it's type name, it's path, etc. There are lots of properties - go check them out.

There is a method for grabbing the PIDL of an object's parent folder, which is VERY important when working inside the Windows Shell. There is also a method for getting a list of all child objects, which is another nice-to-have feature. Oh yea, and the PIDL object can also be cloned, compared and disposed of.

Here is the code listing for the class:

 

[ComVisible(false)]
public sealed class CGPidl : IDisposable, IComparable, ICloneable
{

	private static class NativeMethods
	{

		[StructLayout(LayoutKind.Sequential)]
		public struct RECT
		{
			public int left;
			public int top;
			public int right;
			public int bottom;

			public RECT(
				Rectangle r
				)
			{
				left = r.Left;
				top = r.Top;
				right = r.Right;
				bottom = r.Bottom;
			}

		} 
		
		[Flags]
		public enum CSIDL
		{
			CSIDL_FLAG_CREATE = 0x8000,
			CSIDL_ADMINTOOLS = 0x0030,
			CSIDL_ALTSTARTUP = 0x001d,
			CSIDL_APPDATA = 0x001a,
			CSIDL_BITBUCKET = 0x000a,
			CSIDL_CDBURN_AREA = 0x003b,
			CSIDL_COMMON_ADMINTOOLS = 0x002f,
			CSIDL_COMMON_ALTSTARTUP = 0x001e,
			CSIDL_COMMON_APPDATA = 0x0023,
			CSIDL_COMMON_DESKTOPDIRECTORY = 0x0019,
			CSIDL_COMMON_DOCUMENTS = 0x002e,
			CSIDL_COMMON_FAVORITES = 0x001f,
			CSIDL_COMMON_MUSIC = 0x0035,
			CSIDL_COMMON_PICTURES = 0x0036,
			CSIDL_COMMON_PROGRAMS = 0x0017,
			CSIDL_COMMON_STARTMENU = 0x0016,
			CSIDL_COMMON_STARTUP = 0x0018,
			CSIDL_COMMON_TEMPLATES = 0x002d,
			CSIDL_COMMON_VIDEO = 0x0037,
			CSIDL_CONTROLS = 0x0003,
			CSIDL_COOKIES = 0x0021,
			CSIDL_DESKTOP = 0x0000,
			CSIDL_DESKTOPDIRECTORY = 0x0010,
			CSIDL_DRIVES = 0x0011,
			CSIDL_FAVORITES = 0x0006,
			CSIDL_FONTS = 0x0014,
			CSIDL_HISTORY = 0x0022,
			CSIDL_INTERNET = 0x0001,
			CSIDL_INTERNET_CACHE = 0x0020,
			CSIDL_LOCAL_APPDATA = 0x001c,
			CSIDL_MYDOCUMENTS = 0x000c,
			CSIDL_MYMUSIC = 0x000d,
			CSIDL_MYPICTURES = 0x0027,
			CSIDL_MYVIDEO = 0x000e,
			CSIDL_NETHOOD = 0x0013,
			CSIDL_NETWORK = 0x0012,
			CSIDL_PERSONAL = 0x0005,
			CSIDL_PRINTERS = 0x0004,
			CSIDL_PRINTHOOD = 0x001b,
			CSIDL_PROFILE = 0x0028,
			CSIDL_PROFILES = 0x003e,
			CSIDL_PROGRAM_FILES = 0x0026,
			CSIDL_PROGRAM_FILES_COMMON = 0x002b,
			CSIDL_PROGRAMS = 0x0002,
			CSIDL_RECENT = 0x0008,
			CSIDL_SENDTO = 0x0009,
			CSIDL_STARTMENU = 0x000b,
			CSIDL_STARTUP = 0x0007,
			CSIDL_SYSTEM = 0x0025,
			CSIDL_TEMPLATES = 0x0015,
			CSIDL_WINDOWS = 0x0024
		} 

		[StructLayout(LayoutKind.Sequential)]
		public struct SHFILEINFO
		{
			public IntPtr hIcon;
			public Int32 iIcon;
			public UInt32 dwAttributes;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
			public string szDisplayName;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
			public string szTypeName;
		};

		public enum SHCONTF
		{
			SHCONTF_FOLDERS = 0x0020,
			SHCONTF_NONFOLDERS = 0x0040,
			SHCONTF_INCLUDEHIDDEN = 0x0080,
			SHCONTF_INIT_ON_FIRST_NEXT = 0x0100,
			SHCONTF_NETPRINTERSRCH = 0x0200,
			SHCONTF_SHAREABLE = 0x0400,
			SHCONTF_STORAGE = 0x0800
		} 
		
		[Flags]
		public enum SHGFI
		{
			SHGFI_ICON = 0x000000100,
			SHGFI_DISPLAYNAME = 0x000000200,
			SHGFI_TYPENAME = 0x000000400,
			SHGFI_ATTRIBUTES = 0x000000800,
			SHGFI_ICONLOCATION = 0x000001000,
			SHGFI_EXETYPE = 0x000002000,
			SHGFI_SYSICONINDEX = 0x000004000,
			SHGFI_LINKOVERLAY = 0x000008000,
			SHGFI_SELECTED = 0x000010000,
			SHGFI_ATTR_SPECIFIED = 0x000020000,
			SHGFI_LARGEICON = 0x000000000,
			SHGFI_SMALLICON = 0x000000001,
			SHGFI_OPENICON = 0x000000002,
			SHGFI_SHELLICONSIZE = 0x000000004,
			SHGFI_PIDL = 0x000000008,
			SHGFI_USEFILEATTRIBUTES = 0x000000010,
			SHGFI_ADDOVERLAYS = 0x000000020,
			SHGFI_OVERLAYINDEX = 0x000000040
		} 

		[Flags]
		public enum SHCIDS : uint
		{
			SHCIDS_ALLFIELDS = 0x80000000,
			SHCIDS_CANONICALONLY = 0x10000000,
			SHCIDS_BITMASK = 0xFFFF0000,
			SHCIDS_COLUMNMASK = 0x0000FFFF
		} 

		[Flags()]
		public enum SFGAO : uint
		{
			SFGAO_CANCOPY = 0x000000001,
			SFGAO_CANMOVE = 0x000000002,
			SFGAO_CANLINK = 0x000000004,
			SFGAO_STORAGE = 0x000000008,
			SFGAO_CANRENAME = 0x00000010,
			SFGAO_CANDELETE = 0x00000020,
			SFGAO_HASPROPSHEET = 0x00000040,
			SFGAO_DROPTARGET = 0x00000100,
			SFGAO_CAPABILITYMASK = 0x00000177,
			SFGAO_ENCRYPTED = 0x00002000,
			SFGAO_ISSLOW = 0x00004000,
			SFGAO_GHOSTED = 0x00008000,
			SFGAO_LINK = 0x00010000,
			SFGAO_SHARE = 0x00020000,
			SFGAO_READONLY = 0x00040000,
			SFGAO_HIDDEN = 0x00080000,
			SFGAO_DISPLAYATTRMASK = 0x000FC000,
			SFGAO_FILESYSANCESTOR = 0x10000000,
			SFGAO_FOLDER = 0x20000000,
			SFGAO_FILESYSTEM = 0x40000000,
			SFGAO_HASSUBFOLDER = 0x80000000,
			SFGAO_CONTENTSMASK = 0x80000000,
			SFGAO_VALIDATE = 0x01000000,
			SFGAO_REMOVABLE = 0x02000000,
			SFGAO_COMPRESSED = 0x04000000,
			SFGAO_BROWSABLE = 0x08000000,
			SFGAO_NONENUMERATED = 0x00100000,
			SFGAO_NEWCONTENT = 0x00200000,
			SFGAO_CANMONIKER = 0x00400000,
			SFGAO_HASSTORAGE = 0x00400000,
			SFGAO_STREAM = 0x00400000,
			SFGAO_STORAGEANCESTOR = 0x00800000,
			SFGAO_STORAGECAPMASK = 0x70C50008
		} 

		[Flags()]
		public enum SHGDN
		{
			SHGDN_NORMAL = 0,
			SHGDN_INFOLDER = 1,
			SHGDN_FORADDRESSBAR = 16384,
			SHGDN_FORPARSING = 32768
		} 

		[StructLayout(LayoutKind.Explicit)]
		public struct STRRET
		{
			[FieldOffset(0)]
			UInt32 uType;
			[FieldOffset(4)]
			IntPtr pOleStr;
			[FieldOffset(4)]
			IntPtr pStr;
			[FieldOffset(4)]
			UInt32 uOffset;
			[FieldOffset(4)]
			IntPtr cStr;
		} 

		public enum SVUIA_STATUS
		{
			SVUIA_DEACTIVATE = 0,
			SVUIA_ACTIVATE_NOFOCUS = 1,
			SVUIA_ACTIVATE_FOCUS = 2,
			SVUIA_INPLACEACTIVATE = 3
		} 

		[StructLayout(LayoutKind.Sequential)]
		public struct FOLDERSETTINGS
		{
			public FOLDERFLAGS ViewMode;
			public FOLDERVIEWMODE fFlags;
		} 

		[Flags]
		public enum FOLDERFLAGS
		{
			FWF_AUTOARRANGE = 0x1,
			FWF_ABBREVIATEDNAMES = 0x2,
			FWF_SNAPTOGRID = 0x4,
			FWF_OWNERDATA = 0x8,
			FWF_BESTFITWINDOW = 0x10,
			FWF_DESKTOP = 0x20,
			FWF_SINGLESEL = 0x40,
			FWF_NOSUBFOLDERS = 0x80,
			FWF_TRANSPARENT = 0x100,
			FWF_NOCLIENTEDGE = 0x200,
			FWF_NOSCROLL = 0x400,
			FWF_ALIGNLEFT = 0x800,
			FWF_NOICONS = 0x1000,
			FWF_SHOWSELALWAYS = 0x2000,
			FWF_NOVISIBLE = 0x4000,
			FWF_SINGLECLICKACTIVATE = 0x8000,
			FWF_NOWEBVIEW = 0x10000,
			FWF_HIDEFILENAMES = 0x20000,
			FWF_CHECKSELECT = 0x40000
		} 

		public enum FOLDERVIEWMODE
		{
			FVM_FIRST = 1,
			FVM_ICON = 1,
			FVM_SMALLICON = 2,
			FVM_LIST = 3,
			FVM_DETAILS = 4,
			FVM_THUMBNAIL = 5,
			FVM_TILE = 6,
			FVM_THUMBSTRIP = 7,
			FVM_LAST = 7
		} 

		[ComImport]
		[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
		[Guid("000214E6-0000-0000-C000-000000000046")]
		public interface IShellFolder
		{
			[PreserveSig]
			Int32 ParseDisplayName(
				IntPtr hwnd, 
				IntPtr pbc, 
				[MarshalAs(UnmanagedType.LPWStr)] 
				string pszDisplayName, 
				ref UInt32 pchEaten, 
				out IntPtr ppidl, 
				ref UInt32 pdwAttributes
				);
			[PreserveSig]
			Int32 EnumObjects(
			IntPtr hwnd, 
			SHCONTF grfFlags, 
			out IEnumIDList ppenumIDList
			);
			[PreserveSig]
			Int32 BindToObject(
				IntPtr pidl, 
				IntPtr pbc, 
				ref Guid riid, 
				out IntPtr ppv
				);
			[PreserveSig]
			Int32 BindToStorage(
				IntPtr pidl, 
				IntPtr pbc, 
				ref Guid riid, 
				out IntPtr ppv
				);
			[PreserveSig]
			Int32 CompareIDs(
				SHCIDS lParam, 
				IntPtr pidl1, 
				IntPtr pidl2
				);
			[PreserveSig]
			Int32 CreateViewObject(
				IntPtr hwndOwner, 
				ref Guid riid, 
				out IShellView ppv
				);
			[PreserveSig]
			Int32 GetAttributesOf(
				UInt32 cidl, 
				[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] 
				IntPtr[] apidl, 
				ref SFGAO rgfInOut
				);
			[PreserveSig]
			Int32 GetUIObjectOf(
				IntPtr hwndOwner, 
				UInt32 cidl, 
				IntPtr[] apidl, 
				Guid riid, 
				ref UInt32 rgfReserved, 
				out IntPtr ppv
				);
			[PreserveSig]
			Int32 GetDisplayNameOf(
				IntPtr pidl, 
				SHGDN uFlags, 
				out STRRET pName
				);
			[PreserveSig]
			Int32 SetNameOf(
				IntPtr hwnd, 
				IntPtr pidl, 
				[MarshalAs(UnmanagedType.LPWStr)] 
				string pszName, 
				UInt32 uFlags, 
				out IntPtr ppidlOut
				);
		} 
		
		[ComImportAttribute()]
		[GuidAttribute("000214F2-0000-0000-C000-000000000046")]
		[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
		public interface IEnumIDList
		{
			[PreserveSig]
			int Next(
				int celt, 
				ref IntPtr rgelt, 
				out int pceltFetched
				);
			[PreserveSig]
			int Skip(int celt);
			[PreserveSig]
			int Reset();
			[PreserveSig]
			int Clone(ref IEnumIDList ppenum);
		} 

		[ComImport()]
		[Guid("000214E3-0000-0000-C000-000000000046")]
		[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
		public interface IShellView
		{
			[PreserveSig]
			int GetWindow(out IntPtr phwnd);
			[PreserveSig]
			int ContextSensitiveHelp(bool fEnterMode);
			[PreserveSig]
			int TranslateAcceleratorA(IntPtr pmsg);
			[PreserveSig]
			int EnableModeless(bool fEnable);
			[PreserveSig]
			int UIActivate(SVUIA_STATUS uState);
			[PreserveSig]
			int Refresh();
			[PreserveSig]
			int CreateViewWindow(
				IShellView psvPrevious, 
				ref FOLDERSETTINGS pfs, 
				IShellBrowser psb, 
				ref RECT prcView, 
				out IntPtr phWnd
				);
			[PreserveSig]
			int DestroyViewWindow();
			[PreserveSig]
			int GetCurrentInfo(
				ref FOLDERSETTINGS pfs
				);
			[PreserveSig]
			int AddPropertySheetPages(
				long dwReserved, 
				ref IntPtr pfnPtr, 
				int lparam
				);
			[PreserveSig]
			int SaveViewState();
			[PreserveSig]
			int SelectItem(
				IntPtr pidlItem,
				uint uFlags
				);
			[PreserveSig]
			int GetItemObject(
				uint uItem, 
				ref Guid riid, 
				ref IntPtr ppv
				);
		} 

		[ComImport()]
		[Guid("000214E2-0000-0000-C000-000000000046")]
		[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
		public interface IShellBrowser
		{
			[PreserveSig]
			int GetWindow(out IntPtr phwnd);
			[PreserveSig]
			int ContextSensitiveHelp(bool fEnterMode);
			[PreserveSig]
			int InsertMenusSB(
				IntPtr hmenuShared, 
				ref IntPtr lpMenuWidths
				);
			[PreserveSig]
			int SetMenuSB(
				IntPtr hmenuShared, 
				IntPtr holemenuRes, 
				IntPtr hwndActiveObject
				);
			[PreserveSig]
			int RemoveMenusSB(IntPtr hmenuShared);
			[PreserveSig]
			int SetStatusTextSB(string pszStatusText);
			[PreserveSig]
			int EnableModelessSB(bool fEnable);
			[PreserveSig]
			int TranslateAcceleratorSB(
				IntPtr pmsg, 
				short wID
				);
			[PreserveSig]
			int BrowseObject(
				IntPtr pidl, 
				uint wFlags
				);
			[PreserveSig]
			int GetViewStateStream(
				long grfMode, 
				ref UCOMIStream ppStrm
				);
			[PreserveSig]
			int GetControlWindow(uint id, ref IntPtr phwnd);
			[PreserveSig]
			int SendControlMsg(
				uint id, 
				uint uMsg, 
				short wParam, 
				long lParam,
				ref long pret
				);
			[PreserveSig]
			int QueryActiveShellView(
				ref IShellView ppshv
				);
			[PreserveSig]
			int OnViewWindowActive(
				IShellView pshv
				);
			[PreserveSig]
			int SetToolbarItems(
				IntPtr lpButtons, 
				uint nButtons, 
				uint uFlags
				);
		} 

		[DllImport("shell32.dll", EntryPoint = "#18")]
		public static extern IntPtr ILClone(
			IntPtr pidl
			);

		[DllImport("shell32.dll", EntryPoint = "#25")]
		public static extern IntPtr ILCombine(
			IntPtr pidlA,
			IntPtr pidlB
			);

		[DllImport("shell32.dll", EntryPoint = "#17")]
		public static extern bool ILRemoveLastID(
			IntPtr pidl
			);

		[DllImport("shell32.dll")]
		public static extern Int32 SHGetDesktopFolder(
			out IShellFolder ppshf
			);

		[DllImport("shell32.dll")]
		public static extern Int32 SHGetFolderLocation(
			IntPtr hwndOwner,
			CSIDL nFolder,
			IntPtr hToken,
			UInt32 dwReserved,
			out IntPtr ppidl
			);

		[DllImport("shell32.dll")]
		public static extern IntPtr SHGetFileInfo(
			string fileName, 
			uint dwFileAttributes, 
			ref SHFILEINFO psfi, 
			int cbSizeFileInfo, 
			SHGFI flags
			);

		[DllImport("shell32.dll")]
		public static extern IntPtr SHGetFileInfo(
			IntPtr pidl, 
			uint dwFileAttributes, 
			ref SHFILEINFO psfi, 
			int cbSizeFileInfo, 
			SHGFI flags
			);

		[DllImport("shell32.dll")]
		public static extern bool SHGetPathFromIDList(
			IntPtr pidl,
			StringBuilder pszPath
			);

		#endregion

	} 

	private static readonly 
		NativeMethods.IShellFolder c_desktopFolder;
	private static readonly IntPtr c_desktopPidl;
	private IntPtr m_pidl;
	private string m_displayName;
	private string m_typeName;
	private string m_physicalPath;
	private NativeMethods.SFGAO m_attributes;

	public IntPtr Pidl
	{
		get {return m_pidl;}
	} 
	
	public string DisplayName
	{
		get {return m_displayName;}
	} 
	
	public string TypeName
	{
		get {return m_typeName;}
	} 
	
	public string PhysicalPath
	{
		get {return m_physicalPath;}
	} 
	
	public bool CanRename
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_CANRENAME) != 0;
			}
	} 
	
	public bool CanMove
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_CANMOVE) != 0;
			}
	} 
	
	public bool CanDelete
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_CANDELETE) != 0;
			}
	} 
	
	public bool CanCopy
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_CANCOPY) != 0;
			}
	} 
	
	public bool IsReadOnly
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_READONLY) != 0;
			}
	} 
	
	public bool IsEncrypted
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_ENCRYPTED) != 0;
			}
	} 
	
	public bool IsLink
	{
		get {
			return (m_attributes &
				NativeMethods.SFGAO.SFGAO_LINK) != 0;
			}
	} 
	
	public bool IsHidden
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_HIDDEN) != 0;
			}
	} 
	
	public bool IsSlow
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_ISSLOW) != 0;
			}
	} 
	
	public bool IsGhosted
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_GHOSTED) != 0;
			}
	} 
	
	public bool IsCompressed
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_COMPRESSED) != 0;
			}
	} 
	
	public bool IsRemovable
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_REMOVABLE) != 0;
			}
	} 
	
	public bool IsShared
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_SHARE) != 0;
			}
	} 
	
	public bool IsFolder
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_FOLDER) != 0;
			}
	} 
	
	public bool IsFileSystem
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_FILESYSTEM) != 0;
			}
	} 
	
	public bool HasSubfolders
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_HASSUBFOLDER) != 0;
			}
	} 
	
	public bool IsBrowsable
	{
		get {
			return (m_attributes & 
				NativeMethods.SFGAO.SFGAO_BROWSABLE) != 0;
			}
	} 
	
	public bool IsDesktop
	{
		get 
		{
		
			// Ask the desktop to determine if they are equal.
			return c_desktopFolder.CompareIDs(
				NativeMethods.SHCIDS.SHCIDS_CANONICALONLY,
				m_pidl,
				c_desktopPidl
				) == 0;

		} // End get

	} 
	
	[SecurityPermission(
		SecurityAction.LinkDemand, 
		Flags=SecurityPermissionFlag.UnmanagedCode)]
	static CGPidl()
	{

		// Get a reference to the desktop folder.
		NativeMethods.SHGetDesktopFolder(
			out c_desktopFolder
			);

		IntPtr hToken = new IntPtr(-1);

		// Get the pidl for the desktop folder.
		int hr = NativeMethods.SHGetFolderLocation(
			IntPtr.Zero,
			NativeMethods.CSIDL.CSIDL_DESKTOP,
			hToken,
			0,
			out c_desktopPidl
			);

	} 
	
	public CGPidl()
	{
		
	} 
	
	[SecurityPermission(
		SecurityAction.LinkDemand, 
		Flags=SecurityPermissionFlag.UnmanagedCode)]
	public CGPidl(
		IntPtr pidl
		)
	{
		Open(pidl);			
	} 
	
	[SecurityPermission(
		SecurityAction.LinkDemand, 
		Flags=SecurityPermissionFlag.UnmanagedCode)]
	public CGPidl(
		IntPtr parentPidl,
		IntPtr pidl
		)
	{
		Open(parentPidl, pidl);
	} 
	
	[SecurityPermission(
		SecurityAction.LinkDemand, 
		Flags=SecurityPermissionFlag.UnmanagedCode)]
	public CGPidl(
		Environment.SpecialFolder specialFolder
		)
	{
		Open(specialFolder);
	} 
	
	[SecurityPermission(
		SecurityAction.LinkDemand, 
		Flags=SecurityPermissionFlag.UnmanagedCode)]
	public CGPidl(
		string fullPath
		)
	{
		Open(fullPath);
	} 
	
	public void Dispose()
	{
		Close();
	} 
	
	public override bool Equals(object obj)
	{
	
		// Recover a reference to the CGPidl object.
		CGPidl pidl = obj as CGPidl;

		// Sanity check the type first.
		if (pidl == null)
			return false;
		
		// Ask the desktop to determine if they are equal.
		return c_desktopFolder.CompareIDs(
			NativeMethods.SHCIDS.SHCIDS_CANONICALONLY,
			m_pidl,
			pidl.m_pidl
			) == 0;
        
	} 
	
	public override int GetHashCode()
	{
		return base.GetHashCode() ^ m_pidl.GetHashCode();
	} 
	
	public void Open(
		IntPtr pidl
		)
	{

		// Clone to pidl.
		m_pidl = NativeMethods.ILClone(pidl);

		// Initialize the object.
		InitializeObject();

	} 
	
	[SecurityPermission(
		SecurityAction.LinkDemand, 
		Flags=SecurityPermissionFlag.UnmanagedCode)]
	public void Open(
		IntPtr parentPidl,
		IntPtr pidl
		)
	{

		// Create a fully qualified pidl for the object.
		m_pidl = NativeMethods.ILCombine(
			parentPidl,
			pidl
			);
		
		// Initialize the object.
		InitializeObject();

	} 
	
	[SecurityPermission(
		SecurityAction.LinkDemand, 
		Flags=SecurityPermissionFlag.UnmanagedCode)]
	public void Open(
		Environment.SpecialFolder specialFolder
		)
	{
		
		IntPtr hToken = new IntPtr(-1);

		// Get the pidl for the special folder.
		int hr = NativeMethods.SHGetFolderLocation(
			IntPtr.Zero,
			SpecialFolderToCSIDL(specialFolder),
			hToken,
			0,
			out m_pidl
			);

		// Did we fail?
		if (hr != 0)
			Marshal.ThrowExceptionForHR(hr);

		// Initialize the object.
		InitializeObject();

	} 
	
	[SecurityPermission(
		SecurityAction.LinkDemand, 
		Flags=SecurityPermissionFlag.UnmanagedCode)]
	public void Open(
		string fullPath
		)
	{
		
		uint attr = 0;
		uint pchEaten = 0;

		// Attempt to get a pidl to the object.
		int hr = c_desktopFolder.ParseDisplayName(
			IntPtr.Zero,
			IntPtr.Zero,
			fullPath,
			ref pchEaten,
			out m_pidl,
			ref attr
			);

		// Did we fail?
		if (hr != 0)
			Marshal.ThrowExceptionForHR(hr);

		// Initialize the object.
		InitializeObject();

	} 
	
	public void Close()
	{

		// Should we cleanup the pidl?
		if (m_pidl != IntPtr.Zero)
			Marshal.FreeCoTaskMem(m_pidl);
    
		m_pidl = IntPtr.Zero;

	} 
	
	[SecurityPermission(
		SecurityAction.LinkDemand, 
		Flags=SecurityPermissionFlag.UnmanagedCode)]
	public CGPidl GetParentFolder()
	{

		// Sanity check the pidl before attempting to use it.
		if (m_pidl == IntPtr.Zero)
			return null;

		// The desktop folder doesn't have a parent.
		if (c_desktopFolder.CompareIDs(
			NativeMethods.SHCIDS.SHCIDS_ALLFIELDS,
			m_pidl, 
			c_desktopPidl
			) == 0)
			return null;

		// Start by copying our pidl since we are about to 
		// modify it to create a pidl for the parent.
		IntPtr parentPidl = NativeMethods.ILClone(m_pidl);

		// If we failed to remove the last item in the PIDL then
		//   there really isn't any way to recover.
		if (!NativeMethods.ILRemoveLastID(parentPidl))
		{
			
			// Free the cloned pidl.
			Marshal.FreeCoTaskMem(parentPidl);
			parentPidl = IntPtr.Zero;

			return null;

		} // End if we failed to locate the parent.

		// Otherwise, create a CGPidl and return it.
		return new CGPidl(parentPidl);
        
	} 
	
	[SecurityPermission(
		SecurityAction.LinkDemand, 
		Flags=SecurityPermissionFlag.UnmanagedCode)]
	public System.Collections.ArrayList GetAncestors()
	{

		ArrayList list = new ArrayList();

		// Sanity check the pidl before attempting to use it.
		if (m_pidl == IntPtr.Zero)
			return list;

		CGPidl pidl = (CGPidl)Clone();
        
		// Loop and find the ancestors.
		while (pidl != null)
		{

			list.Add(pidl);
			pidl = pidl.GetParentFolder();

		} // End while there are more ancestors.

		// Return the family tree.
		return list;

	} 
	
	[SecurityPermission(
		SecurityAction.LinkDemand, 
		Flags=SecurityPermissionFlag.UnmanagedCode)]
	private void InitializeObject()
	{

		NativeMethods.SHFILEINFO shfi = 
			new NativeMethods.SHFILEINFO();

		// Attempt to get the information for the shell object.
		NativeMethods.SHGetFileInfo(
			m_pidl,
			0,
			ref shfi,
			Marshal.SizeOf(shfi),
			NativeMethods.SHGFI.SHGFI_PIDL |
			NativeMethods.SHGFI.SHGFI_DISPLAYNAME |
			NativeMethods.SHGFI.SHGFI_ATTRIBUTES |
			NativeMethods.SHGFI.SHGFI_TYPENAME
			);

		// Save the information.
		m_displayName = shfi.szDisplayName;
		m_typeName = shfi.szTypeName;
		m_attributes = (NativeMethods.SFGAO)shfi.dwAttributes;

		StringBuilder sb = new StringBuilder(260);
						
		// Get the physical path to the shell object.
		NativeMethods.SHGetPathFromIDList(
			m_pidl,
			sb
			);

		// Save the path.
		m_physicalPath = sb.ToString();

	} 
	
	private static NativeMethods.CSIDL SpecialFolderToCSIDL(
		Environment.SpecialFolder sf
		)
	{

		switch (sf)
		{

			case Environment.SpecialFolder.ApplicationData :

				return NativeMethods.CSIDL.CSIDL_APPDATA;

			case Environment.SpecialFolder.CommonApplicationData :

				return NativeMethods.CSIDL.CSIDL_COMMON_APPDATA;

			case Environment.SpecialFolder.CommonProgramFiles :

				return NativeMethods.CSIDL.CSIDL_COMMON_PROGRAMS;

			case Environment.SpecialFolder.Cookies :

				return NativeMethods.CSIDL.CSIDL_COOKIES;

			case Environment.SpecialFolder.Desktop :

				return NativeMethods.CSIDL.CSIDL_DESKTOP;

			case Environment.SpecialFolder.DesktopDirectory :

				return NativeMethods.CSIDL.CSIDL_DESKTOPDIRECTORY;

			case Environment.SpecialFolder.Favorites :

				return NativeMethods.CSIDL.CSIDL_FAVORITES;

			case Environment.SpecialFolder.History :

				return NativeMethods.CSIDL.CSIDL_HISTORY;

			case Environment.SpecialFolder.InternetCache :

				return NativeMethods.CSIDL.CSIDL_INTERNET_CACHE;

			case Environment.SpecialFolder.LocalApplicationData :

				return NativeMethods.CSIDL.CSIDL_LOCAL_APPDATA;

			case Environment.SpecialFolder.MyComputer :

				return NativeMethods.CSIDL.CSIDL_DRIVES;

			case Environment.SpecialFolder.MyMusic :

				return NativeMethods.CSIDL.CSIDL_MYMUSIC;

			case Environment.SpecialFolder.MyPictures :

				return NativeMethods.CSIDL.CSIDL_MYPICTURES;

			case Environment.SpecialFolder.Personal :

				return NativeMethods.CSIDL.CSIDL_PERSONAL;

			case Environment.SpecialFolder.ProgramFiles :

				return NativeMethods.CSIDL.CSIDL_PROGRAM_FILES;

			case Environment.SpecialFolder.Programs :

				return NativeMethods.CSIDL.CSIDL_PROGRAMS;

			case Environment.SpecialFolder.Recent :

				return NativeMethods.CSIDL.CSIDL_RECENT;

			case Environment.SpecialFolder.SendTo :

				return NativeMethods.CSIDL.CSIDL_SENDTO;

			case Environment.SpecialFolder.StartMenu :

				return NativeMethods.CSIDL.CSIDL_STARTMENU;

			case Environment.SpecialFolder.Startup :

				return NativeMethods.CSIDL.CSIDL_STARTUP;

			case Environment.SpecialFolder.System :

				return NativeMethods.CSIDL.CSIDL_SYSTEM;

			case Environment.SpecialFolder.Templates :

				return NativeMethods.CSIDL.CSIDL_TEMPLATES;

		} // End switch

		return NativeMethods.CSIDL.CSIDL_DESKTOP;

	} 
	
	public int CompareTo(object obj)
	{
		
		// Recover a reference to the CGPidl object.
		CGPidl pidl = obj as CGPidl;

		// Sanity check the type first.
		if (pidl == null)
			return 0;
		   
		// Ask the shell to do the compare.
		return c_desktopFolder.CompareIDs(
			NativeMethods.SHCIDS.SHCIDS_CANONICALONLY,
			pidl.Pidl,
			m_pidl
			);

	} 
	
	public object Clone()
	{
		
		// Clone the object.
		CGPidl pidl = new CGPidl();
		pidl.m_pidl = NativeMethods.ILClone(m_pidl);
		pidl.m_displayName = m_displayName;
		pidl.m_typeName = m_typeName;
		pidl.m_attributes = m_attributes;
		pidl.m_physicalPath = m_physicalPath;
        
		// Return the clone.
		return pidl;

	} 
	
} 

I hope someone find this code useful.

 

Have fun! Smile

Comments

 

Alan said:

Martin, I just found this code and it is *exactly* what I'm looking for. Thanks so much for posting it here!

November 15, 2007 6:56 PM
 

Martin said:

I'm glad to hear it Alan. Thanks for reading my little blog.

November 21, 2007 8:48 AM
 

amir reza nl said:

Hi,

I have a quesion about these methods:

SHGetFileInfo and SHGetSpecialFolderLocation

I used these methods in my code.for example I get control panel's ipdl and pass it to get its attributs. but sometimes it works sometimes it doesn't work!!!

My win is win xp sp2

plz see my code at:

www.codeproject.com/.../View.aspx

can u tell me what's wrong?

March 6, 2008 9:56 AM
 

Martin said:

Hello Amir,

I looked at your question on codeproject. You are correct that your code is similar to mine, however I am calling a slightly different method to get the path from the PIDL. In my case I am using the SHGetPathFromIDList function. Also, my code doesn't convert from the InPtr to a ITEMIDLIST structure. I am able to deal with PIDL's as IntPtr objects instead of converting them to ITEMIDLIST structures.

You don't say which call is failing in your code, is it SHGetSpecialFolderLocation or SHGetFileInfo?

My suggestion would be to try omiting the conversion of the PIDL to a struct if possible. If not, double check the StructLayout statements - sometimes marshalling problems can be intermittent, like the problem you describe.

I hope I have helped.

March 6, 2008 2:23 PM
 

Erik said:

Hi Martin,

I have found your code very helpful, but still  I am facing a problem. I would like to get Path information from PIDL, but did not succeed. The stiuation is: I would like to implement drag and drop support into my application from web (the remote site is opened in an explorer (filebrowser) control in internet explorer). I have tried to do that with SHGetPathFromIDList(abspidl, path), which returned "",  tried that with your class, which returned the same result. I have found some information about that this method only works for physical paths, and not virtual. If I take a look into the memorystream (I have written the contents into a file) object of e.Data.GetData("Shell IDList Array"), i can see something like this: ...binary data... "Shared files on www.anything.com" ... binary data .... www.anything.com/.../...binary data.... filename.txt...The length of the path (for example 45) ..full path-www.anything.com/.../filename.txt ..hex0 hex0...

Could you help, what functions should I use to get the path information for this virtual path?

Regards Erik

May 16, 2008 8:15 AM
 

Martin said:

Helo Erik,

As you say, SHGetPathFromIDList will only return a path for a physical object (has to be part of the file system). I don't know of any magic to get around that fact. I personally wouldn't pursue a solution based on the internals of a serialized pidl. The exact structure of a pidl is subject to change by MS at any moment. Also, I think you might get different pidls based on whether the physical object is a shortcurt or contains a hardlink. Finally, beware of differences in Vista. I'm getting quite a bit of feedback from CODEGATOR readers about broken COM interfaces & methods in the new OS. :o(

If I think of a solution to your problem I'll post it here for everyone to see. Good luck.

May 19, 2008 5:45 AM
 

Erik said:

Hi

If anyone having the same trouble, I have solved this issue -not so perfect- with the help of regular expressions with this pattern: pattern = @"(http|https|ftp|file)://[a-zA-Z0-9.\-/ ]*\.[a-zA-Z]{3,4}\x00";

May 20, 2008 4:53 AM
 

Martin said:

Thanks for sharing your solution Erik! :o)

May 22, 2008 5:28 AM

Leave a Comment

(required) 
(optional)
(required) 
Submit

About Martin

I work as a software engineer specializing in designing and building object-oriented business solutions for Windows platforms using C#. I have been programming professionally for roughly 20 years.

This Blog

Syndication

Terms of Service | Privacy Statement