Question

Given a folder, how can I tell that it is a recycle bin? I've found an answer for C++ but not for C#.

My first idea was to check for FileAttributes.System (which would be an acceptable approximation in my case) but actually that flag is cleared on the recycle folder.

Crude solutions using hardcoded folder names are out of the question (we're in 2009 after all).

Was it helpful?

Solution

There's a little problem here. The Windows Recycle Bin is a virtual folder and does not actually exist. The files that you see are not actually in that folder, they are the representation of existing files on disk that have been renamed to a special name, which "removes" them from the visible file system, but not the physical one.

You can "proof" this for yourself by asking for the folder location using the win32 API. It will return E_FAIL for the Recycle Bin, but not for other folders (see SHGetKnownFolderPath on pinvoke.net (and on MSDN) for all constants you can use and the declarations needed for this code to run):

IntPtr ptrRecycleBinPath;
// try it with KnownFolder.QuickLaunch to see it working:
HRESULT hr = (HRESULT) SHGetKnownFolderPath(
     KnownFolder.RecycleBinFolder, 
     0, 
     IntPtr.Zero, 
     out ptrRecycleBinPath);

if (hr == HRESULT.E_FAIL)
{
    Console.WriteLine("No folder avaialable, virtual folder");
}
else if (hr == HRESULT.S_OK)
{
    string RecycleBinPath = Marshal.PtrToStringUni(ptrRecycleBinPath);
    Marshal.FreeCoTaskMem(ptrRecycleBinPath);
    Console.WriteLine("path: " + RecycleBinPath);
}

// for convenience, you can use the code above
// directly if you paste the follow declarations in your class:

// get a "known path"
[DllImport("shell32.dll")]
static extern long SHGetKnownFolderPath(
    [MarshalAs(UnmanagedType.LPStruct)] Guid rfid, 
    uint dwFlags, 
    IntPtr hToken, 
    out IntPtr pszPath);

// known folder GUID declarations
public static class KnownFolder
{
    // many more entries exist, left out for clarity here

    public static readonly Guid RecycleBinFolder = 
         new Guid("B7534046-3ECB-4C18-BE4E-64CD4CB7D6AC");

    public static readonly Guid QuickLaunch = 
         new Guid("52a4f021-7b75-48a9-9f6b-4b87a210bc8f");

    //....
}

// results of COM invocations:
enum HRESULT : uint
{
    S_FALSE = 0x0001,
    S_OK = 0x0000,
    E_FAIL = 0x80004005,
    E_INVALIDARG = 0x80070057,
    E_OUTOFMEMORY = 0x8007000E
}

The fake foldername "$Recycle.bin" is repeated for each drive. The hidden name is not stored in the registry and it is not accessible by the API as such. The earlier suggested KnownFolderHelper will not retrieve this information either (the same lib has a named method for getting the Recycle Bin, it also has a GetPath, it will turn up empty).

But all is not lost. This fake non-existing "file name" or "folder name" contains a hidden file that looks something like "S-1-5-21-2703390745-3900912742-210389625-1000" (yours will be different). It's one of two "reliable" ways to find out whether a certain filename is actually a virtual directory of the recycle bin (the other way being: delete a file through SHFileOperation, explained here, and check whether it appears in the folder you have):

string [] entries = Directory.GetFileSystemEntries(@"c:\$Recycle.bin", "?-?-?-??*");
if(entries.Length > 0)
   // we have a winner
else 
   // no, not the recycle bin

Note: I don't know what the hidden folders are on other win32 versions, you'l have to experiment a bit. They all have the system and hidden flag set and look like a mangled GUID.

The API docs are not very clear about it, but if you need confirmation, this page explains that there really is no path that can be retrieved (the older CSIDL related page is much less clear on it).

Update: alternative approaches with SHGetSpecialFolderPath, SHGetSpecialFolderLocation, ShellAPI.SHGetFolderLocation and SHGetPathFromIDList all fail with the same: either an empty result or an error. I tested all functions both for Recycle Bin and for AppData (to be sure I used the correct parameters).

Only the documentation on ShGetPathFromIDListEx said it explicitly, quote: "Except for UNC printer names, if the location specified by the pidl parameter is not part of the file system, this function fails.".

OTHER TIPS

Microsoft's Windows API Code Pack contains this functionality.

To get the folder of the Recycle Bin, use

Microsoft.WindowsAPICodePack.Shell.KnownFolderHelper.FromPath("::{645FF040-5081-101B-9F08-00AA002F954E}");

I've no idea what that string means, but it was included in the docs as the reference to the Recycle Bin.

Hope this helps :)

Most of the recycle bin related methods have been written in C++ as you mentioned. You could create a wrapper class in your application using the managed extensions to C++, then you will have to use DLLImport like this:

using System;
using System.Runtime.InteropServices;

class MainApp
{
[DllImport("user32.dll", EntryPoint="MessageBox")]
public static extern int MessageBox(int hWnd, String strMessage, String
strCaption, uint uiType);

public static void Main()
{
MessageBox( 0, "Hello, this is PInvoke in operation!", ".NET", 0 );
}
}

There are also articles out there that do this some other way with C#, most of them use PInvoke or rely on the folder having $Recycle in it's name. Following are a few links I've found for this subject

http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/05f1476f-a101-4766-847b-0bdf4f6ad397

http://www.codeproject.com/KB/shell/recyclebin.aspx

http://www.pinvoke.net/default.aspx/shell32.SHFileOperation

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top