-
Sep 2nd, 2017, 03:45 PM
#1
Thread Starter
Hyperactive Member
[RESOLVED] How to get the location of the Recycle Bin for a given drive?
On my C drive the recycle bin path is: "C:\$Recycle.Bin" This is probably true on all Win7-10 systems. But on older systems (XP - FAT/NTFS) it appears as "C:\RECYCLED" or "C:\RECYCLER." I don't want to assume that it will always be "C:\$Recycle.Bin" in all future versions of Windows and just hard code that in my app.
I'm thinking that there has to be a way to get this information programmatically but I just can't find it anywhere. I would have expected SHGetFolderPath with CSIDL_BITBUCKET to retrieve this information but it doesn't. Any clever ideas?
Raymond Chen proposes a method here which a poster on StackOverflow used in this solution. The function cycles through the directories in the root directory looking at hidden and/or system directories. When it finds one, it checks the child subdirectories looking for one that has CLSID_Recycle Bin.
It's not in VB6 however and it seems like a lot of work to do. Is this really the only way to get the desired information? If so, anyone up for translating this to VB6 code?
Thanks in advance.
Last edited by AAraya; Sep 2nd, 2017 at 05:43 PM.
-
Sep 2nd, 2017, 04:47 PM
#2
Re: How to get the location of the Recycle Bin for a given drive?
I would think that a call to SHGetSpecialFolderLocation along with the constant CSIDL_BITBUCKET would get it done. However, truth be told, I've never used SHGetSpecialFolderLocation to get the recycle bin. Also, the MSDN says that this returns a virtual to the user's recycle bin. But that may be what you're after.
Good Luck,
Elroy
EDIT1: Here's a post I made a couple of years ago that shows usage of SHGetSpecialFolderLocation. Look at the ApiSpecialFolder function.
EDIT2: Not sure this'll ever get read but I just wanted to give Spoo a smile. This edit is also off-topic, so I didn't want to make another post. But yeah, it confuses me why the forum software doesn't underline hyperlinks, as that's a standard for html. But, I've just been manually doing it here for quite some time. (See post #3 for why I posted this edit.)
Last edited by Elroy; Sep 4th, 2017 at 12:49 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Sep 2nd, 2017, 05:14 PM
#3
Re: How to get the location of the Recycle Bin for a given drive?
Elroy
Another off-topic "highlighting" comment.
I generally find that embedding text in the URL wrapper does NOT provide "pop". The "bluish" forecolor is too close to the standard black.
However, your approach to further add a U wrapper is indeed an improvement.
Nice
Spoo
-
Sep 2nd, 2017, 05:46 PM
#4
Thread Starter
Hyperactive Member
Re: How to get the location of the Recycle Bin for a given drive?
I would think that a call to SHGetSpecialFolderLocation along with the constant CSIDL_BITBUCKET would get it done.
I'm after a path to a folder on the disk as in "C:\$Recycle.Bin" and that procedure doesn't do that for the Recycle Bin. However, the link you provided to the other post has an approach I've not tried yet which fafalone mentioned using IKnownFolderManager. I'll explore that path.
Thanks!
Last edited by AAraya; Sep 2nd, 2017 at 06:00 PM.
-
Sep 2nd, 2017, 06:43 PM
#5
Thread Starter
Hyperactive Member
Re: How to get the location of the Recycle Bin for a given drive?
Well I'm thinking that maybe the answer is in fafalone's IKnownFolderManager post but I just can't get it to work.
-
Sep 3rd, 2017, 12:43 PM
#6
Thread Starter
Hyperactive Member
Re: How to get the location of the Recycle Bin for a given drive?
Welp, it appears that none of the above approaches will return the Recycle Bin's path. Approaches tried so far which failed:
SHGetSpecialFolderLocation
SHGetKnownFolderPath
IKnownFolderManager (using the GetPath method of the KnownFolder object)
So I'm off to code the routine based on Raymond Chen's method. i.e. "A function which cycles through the directories in the root directory looking at hidden and/or system directories. When it finds one, it checks the child subdirectories looking for one that has CLSID_Recycle Bin."
I'll post a version of the code when its completed and tested.
-
Sep 3rd, 2017, 01:12 PM
#7
Re: How to get the location of the Recycle Bin for a given drive?
Out of curiosity, I tried using the SHFileOperation API to track 'renamed' files. Was hoping that a file deletion with optional FOF_ALLOWUNDO option would result in a 'move' vs straight 'delete' as far as Windows/API was concerned. If that worked, and it didn't, we could've deleted a test file, with the FOF_WANTMAPPINGHANDLE option, and tracked where it went. Was worth a try.
-
Sep 3rd, 2017, 01:15 PM
#8
Thread Starter
Hyperactive Member
Re: How to get the location of the Recycle Bin for a given drive?
Oh well, that didn't take long. The C code solution posted here is beyond me. I am utterly dependent on the good will of one of my more skilled VB6 brethren to convert the two routines for me to VB6.
Any takers?
-
Sep 3rd, 2017, 01:31 PM
#9
Thread Starter
Hyperactive Member
Re: How to get the location of the Recycle Bin for a given drive?
Originally Posted by LaVolpe
Out of curiosity, I tried using the SHFileOperation API to track 'renamed' files. Was hoping that a file deletion with optional FOF_ALLOWUNDO option would result in a 'move' vs straight 'delete' as far as Windows/API was concerned. If that worked, and it didn't, we could've deleted a test file, with the FOF_WANTMAPPINGHANDLE option, and tracked where it went. Was worth a try.
That's thinking outside of the box!
-
Sep 3rd, 2017, 01:49 PM
#10
Re: How to get the location of the Recycle Bin for a given drive?
Can we ask why you need the folder location(s)? The recycle bin can be accessed via a PIDL. The PIDL can be cast to an IShellFolder interface which can be queried for content/properties. Now this does require a TLB for IShellFolder (fafalone offers such a TLB as well as others may exist) or low-level calls via DispCallFunction API. This may be an option depending on your needs.
Non-VB example from Raymond Chen
Last edited by LaVolpe; Sep 3rd, 2017 at 01:58 PM.
Reason: DispCallFunction vs CallDispFunction
-
Sep 3rd, 2017, 03:39 PM
#11
Thread Starter
Hyperactive Member
Re: How to get the location of the Recycle Bin for a given drive?
Originally Posted by LaVolpe
Can we ask why you need the folder location(s)?
For several different reasons depending on which app of mine I'll use it in. But basically to exclude Recycle Bin contents from the results returned by FindFirst/FindNext.
-
Sep 4th, 2017, 03:55 AM
#12
Re: How to get the location of the Recycle Bin for a given drive?
UPDATE: I made a Code Bank project with this code-
[VB6, Vista] List the Recycle Bin location(s) on a drive
IKnownFolderManager won't let you get the different paths for different drives directly.
What you could do is enumerate the files that are in the Recycle Bin to get the paths:
Code:
Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal PV As Long) ' Frees memory allocated by the shell
Private Declare Function SysReAllocString Lib "oleaut32.dll" (ByVal pBSTR As Long, Optional ByVal pszStrPtr As Long) As Long
Private Function LPWSTRtoStr(lPtr As Long, Optional ByVal fFree As Boolean = True) As String
SysReAllocString VarPtr(LPWSTRtoStr), lPtr
If fFree Then
Call CoTaskMemFree(lPtr)
End If
End Function
Public Sub EnumRecycleBinPaths(sBinPaths() As String)
Dim kfm As New KnownFolderManager
Dim pk As IKnownFolder
Dim pItem As IShellItem
Dim penum1 As IEnumShellItems
Dim pChild As IShellItem
Dim lpPath As Long, sPath As String
Dim sParent As String
Dim pcl As Long
ReDim sBinPaths(0)
kfm.GetFolder FOLDERID_RecycleBinFolder, pk
If (pk Is Nothing) = False Then
pk.GetShellItem KF_FLAG_DEFAULT, IID_IShellItem, pItem
pItem.BindToHandler ByVal 0&, BHID_EnumItems, IID_IEnumShellItems, penum1
Do While penum1.Next(1&, pChild, pcl) = S_OK
pChild.GetDisplayName SIGDN_FILESYSPATH, lpPath
sPath = LPWSTRtoStr(lpPath)
sParent = Left$(sPath, 3)
sPath = Mid$(sPath, 4)
sParent = sParent & Left$(sPath, InStr(sPath, "\"))
arr_add_dedupe sBinPaths, sParent
Loop
End If
End Sub
Private Sub arr_add_dedupe(sAr() As String, sNew As String)
Dim i As Long
For i = 0 To UBound(sAr)
If sAr(i) = sNew Then Exit Sub
Next
If (UBound(sAr) = 0) And (sAr(0) = "") Then
sAr(0) = sNew
Else
ReDim Preserve sAr(UBound(sAr) + 1)
sAr(UBound(sAr)) = sNew
End If
End Sub
Public Function LPWSTRtoStr(lPtr As Long, Optional ByVal fFree As Boolean = True) As String
SysReAllocString VarPtr(LPWSTRtoStr), lPtr
If fFree Then
Call CoTaskMemFree(lPtr)
End If
End Function
That's assuming we're only looking at lettered drives (C:\, D:\, etc) and not network paths \\abc\def -- if you want network path support like that all you have to do is adjust the quick and dirty logic I used to get the top level parent folder.
So you run that function and see you've now got a list of active recycle bin directories (if there's no recycled files on a drive, the folder doesn't exist):
Code:
Dim sbinf() As String
EnumRecycleBinPaths sbinf
Dim rbctr As Long
For rbctr = 0 To UBound(sbinf)
Debug.Print "sbinf." & rbctr & "=" & sbinf(rbctr)
Next
sbinf.0=D:\$RECYCLE.BIN\
sbinf.1=C:\$Recycle.Bin\
I have more drives, but as mentioned, the bin folders only exist when there's something in them.
This won't be slow unless there's 10s of thousands of items right in the root... it only enumerates the first level, it doesn't walk through subfolders.
Note: This code uses oleexp.tlb as well as the mIID.bas addon module that's included in the oleexp download.
Side note: If you wanted more information like in Raymond Chen's article, with my method, you would instead cast it to IShellItem2, and from there you can access its property store. I've also been working on a routine to restore items from a the recycle bin, by combining this enumeration method with a request for a IContextMenu to invoke the restore verb on.
Hmm, I should have read the thread first, that stackoverflow example might be a bit quicker.
...
It's not working for me, trying to find by CLSID.
.GetClassID is returning {F3364BA0-65B9-11CE-A9BA-00AA004AE837} for the Recycle Bin, which is the GUID for normal folders (all normal folders have that same class id).
It's way past my bedtime, I'll check back in tomorrow but in the mean time if anyone wants to poke around with it here's what I was doing, it's the same principle as the example on StackOverflow, it just uses a different route to the IPersist interface:
UPDATE: WORKING
It turns out the "official" Recycle Bin directory is one level deeper, with the S-whatever folder; so all that had to be done was to go one level deeper, which I restricted to only hidden, system folders.
This should work on all Vista+ systems, but if it doesn't let me know.
Code:
Public Declare Function ILCreateFromPathW Lib "shell32" (ByVal pwszPath As Long) As Long
Public Declare Function SHCreateItemFromIDList Lib "shell32" (ByVal pidl As Long, riid As UUID, ppv As Any) As Long
Public Declare Function StringFromGUID2 Lib "ole32.dll" (ByRef rguid As Any, ByVal lpsz As String, ByVal cchMax As Long) As Long
Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal PV As Long) ' Frees memory allocated by the shell
Private Declare Function SysReAllocString Lib "oleaut32.dll" (ByVal pBSTR As Long, Optional ByVal pszStrPtr As Long) As Long
Private Function LPWSTRtoStr(lPtr As Long, Optional ByVal fFree As Boolean = True) As String
SysReAllocString VarPtr(LPWSTRtoStr), lPtr
If fFree Then
Call CoTaskMemFree(lPtr)
End If
End Function
Public Function FindRecycleBinsOnDrive(sDrive As String) As String()
Dim pItem As IShellItem
Dim penum1 As IEnumShellItems, penum2 As IEnumShellItems
Dim pChild As IShellItem
Dim pChild2 As IShellItem
Dim lpPath As Long, sPath As String
Dim sParent As String
Dim n As Long
Dim pcl As Long, pcl2 As Long
Dim pidl As Long
Dim gid As oleexp.UUID
Dim pPersist As oleexp.IPersist
Dim lAtr As SFGAO_Flags
Dim sOut() As String
ReDim sOut(0)
pidl = ILCreateFromPathW(StrPtr(sDrive))
Call SHCreateItemFromIDList(pidl, IID_IShellItem, pItem)
If (pItem Is Nothing) = False Then
pItem.BindToHandler ByVal 0&, BHID_EnumItems, IID_IEnumShellItems, penum1
Do While penum1.Next(1&, pChild, pcl) = S_OK
pChild.GetAttributes SFGAO_FOLDER Or SFGAO_HIDDEN Or SFGAO_SYSTEM, lAtr
If ((lAtr And SFGAO_FOLDER) = SFGAO_FOLDER) And ((lAtr And SFGAO_HIDDEN) = SFGAO_HIDDEN) And ((lAtr And SFGAO_SYSTEM) = SFGAO_SYSTEM) Then
pChild.BindToHandler ByVal 0&, BHID_EnumItems, IID_IEnumShellItems, penum2
Do While penum2.Next(1&, pChild2, pcl2) = S_OK
pChild2.BindToHandler ByVal 0&, BHID_SFObject, IID_IPersist, pPersist
If (pPersist Is Nothing) = False Then
pPersist.GetClassID gid
pChild2.GetDisplayName SIGDN_DESKTOPABSOLUTEPARSING, lpPath
If IsEqualGUID(gid, CLSID_RecycleBin) Then
pChild2.GetDisplayName SIGDN_FILESYSPATH, lpPath
ReDim Preserve sOut(n)
sOut(n) = LPWSTRtoStr(lpPath)
Debug.Print "BinOnDrive=" & sOut(n)
n = n + 1
End If
End If
Loop
End If
Loop
Else
Debug.Print "Failed to get drive object"
End If
Call CoTaskMemFree(pidl)
FindRecycleBinsOnDrive = sOut
End Function
Public Function CLSID_RecycleBin() As UUID
'{645ff040-5081-101b-9f08-00aa002f954e}
Static iid As UUID
If (iid.Data1 = 0) Then Call DEFINE_UUID(iid, &H645FF040, CInt(&H5081), CInt(&H101B), &H9F, &H8, &H0, &HAA, &H0, &H2F, &H95, &H4E)
CLSID_RecycleBin = iid
End Function
Public Sub PrintUUID(TempGUID As UUID)
Dim GuidStr As String
Dim GuidLen As Long
GuidStr = Space(80)
GuidLen = StringFromGUID2(TempGUID, GuidStr, 80)
If (GuidLen) Then
GuidStr = StrConv(Left$(GuidStr, (GuidLen - 1) * 2), vbFromUnicode)
Debug.Print GuidStr
End If
End Sub
sbinf.0=C:\$Recycle.Bin\S-1-5-21-4156059968-2326138946-3517228481-1000
sbinf.1=C:\RECYCLER\S-1-5-21-355670511-176562433-3046416621-1005
sbinf.0=D:\$RECYCLE.BIN\S-1-5-21-4090028877-2251132163-1144775278-1003
sbinf.1=D:\$RECYCLE.BIN\S-1-5-21-4156059968-2326138946-3517228481-1000
you call it like Dim sBins() As String: sBins = FindRecycleBinsOnDrive("C:\"); you can use an API like GetLogicalDriveStrings if you want to enumerate them all at once.
The top level $Recycle.Bin should only ever have the one child folder, so it shouldn't matter whether you use the full return or the parent, nor should it need to keep searching after finding the first one.
WEIRD
So I double checked that last part... I don't know why, or how, but I have two valid recycle bins on my C: drive, C:\$Recycle.Bin and C:\RECYCLER. Then two on my D: drive, with two S-x folders. So I had to turn the function into an array, FindRecycleBinsOnDrive
But we're all good now
In fact I'm going to go post this all in the codebank...
Last edited by fafalone; Sep 4th, 2017 at 11:33 AM.
Reason: Cleaned up code a bt
-
Sep 4th, 2017, 10:56 AM
#13
Thread Starter
Hyperactive Member
Re: How to get the location of the Recycle Bin for a given drive?
Thanks for your efforts on this fafalone. That's certainly one approach - to enumerate the contents of the Recycle Bin and parse the parents of each item in there to get a complete list of all active Recycle Bins. It could be slower than I want depending on the nature of the contents of the user's Recycle Bin (many, many files in the root of the RB).
I suppose one performance tweak would be to pass in the drive I was interested in - let's say "C:" for example and exit the enumeration as soon as a file in the Recycle Bin was found having that drive as part of their path. The worst case scenario with this approach would be if no files were in that drive's Recycle Bin - we'd have to go through all the contents to find that out.
I'd be interested in seeing if we could get the Stack Overflow example working - FindRecycleBinOnDrive(). Hopefully a good night's sleep and your excellent kick start of this will bring forth new insights today?!
Nevermind. I see that you were updating as I was replying. I'll check out your updated code!
Last edited by AAraya; Sep 4th, 2017 at 11:10 AM.
-
Sep 4th, 2017, 11:03 AM
#14
Re: How to get the location of the Recycle Bin for a given drive?
It is working. Just needed to go one level deeper to get the right class ID. I know I wrote a lot, but rest assured all bugs have been worked out and I even put both methods into a Code Bank project
-
Sep 4th, 2017, 11:14 AM
#15
Thread Starter
Hyperactive Member
Re: How to get the location of the Recycle Bin for a given drive?
Three things:
1. Thank you - you're a rock star!
2. The LPWSTRtoStr() procedure was missing from your latest code. I grabbed it from an earlier post of yours.
3. FindRecycleBinsOnDrive() is not working for me.
It returns an array of 7 elements (0-6) which are all empty!
Last edited by AAraya; Sep 4th, 2017 at 11:19 AM.
-
Sep 4th, 2017, 11:26 AM
#16
Re: How to get the location of the Recycle Bin for a given drive?
Could it have been from the missing LPWStrToStr? It hasn't changed from earlier projects but to double check
Code:
Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal PV As Long) ' Frees memory allocated by the shell
Private Declare Function SysReAllocString Lib "oleaut32.dll" (ByVal pBSTR As Long, Optional ByVal pszStrPtr As Long) As Long
Private Function LPWSTRtoStr(lPtr As Long, Optional ByVal fFree As Boolean = True) As String
SysReAllocString VarPtr(LPWSTRtoStr), lPtr
If fFree Then
Call CoTaskMemFree(lPtr)
End If
End Function
*with* those local API declares. SysReAllocString is defined in the TLB, but I add the local API declare because I've had problems with the TLB one.
Also... add a line to print out the first part of the folder class id...
Code:
pPersist.GetClassID gid
Debug.Print "gid=0x" & Hex$(gid.Data1)
That will tell us whether the problem is in enumerating things or in getting the string.
What version of Windows are you using anyway... I can fire up a VM to see if the problem might be arising out of that. It was all written and tested on Win 7 x64, with an IDE manifest.
Edit: And to limit things being confused, if you could run the sample project from the Code Bank and see if there's still a problem? With that all the versions and references are for sure right.
Last edited by fafalone; Sep 4th, 2017 at 11:29 AM.
-
Sep 4th, 2017, 11:47 AM
#17
Thread Starter
Hyperactive Member
Re: How to get the location of the Recycle Bin for a given drive?
Hi fafalone - I downloaded your code bank submission and all works properly! Not sure what the exact diffs are between what's posted above and what's in the files I downloaded but if everyone just makes sure to use your code bank code, all works great.
Thanks!
I'm going to tweak your current code for my needs. Currently your code returns 7 bins on my C:\ drive. I don't need the name/paths of each of the separate bins, I just want the common parent folder of all of them which is C:\$Recycle.Bin.
Again, much appreciation. I most definitely could not have done this without your assistance. I would have had to use the hard-coded approach. (YUCK!)
-
Sep 4th, 2017, 12:03 PM
#18
Thread Starter
Hyperactive Member
Re: How to get the location of the Recycle Bin for a given drive?
NVM - posted in Code Bank submission
-
Sep 4th, 2017, 01:57 PM
#19
Re: How to get the location of the Recycle Bin for a given drive?
Glad to help! It's fun for me. I'm right there with you about hard coded paths... why do it that way when there's a tiny bit of benefit from doing a far more complicated dive into shell interfaces, which is so much more fun??? (totally serious, as my demo projects show, my philosophy is that which can be done using shell interfaces, should be done using shell interfaces; as well as using API in VB to replace everything VB does natively)
I'm still interested to know the difference too; had you checked that LPWStrToStr was using the SysReAllocString defined in the project, as opposed to in the TLB, before you ran the CodeBank code? The main functions are just copy/paste from what's here, so that API sounds like the strongest candidate (I updated the code in this thread with that declare and the lpwstr function).
Just as interested in the other problem; the kfm.GetFolder FOLDERID_RecycleBinFolder, pk call now works for you in the new codebank project as well? I'm still not sure where that one could be coming from, unless you had some issue with references, like another tlb with the interface, or a stuck reference to a different oleexp version.
Just curious, if it's all working now it's not that important.
Last edited by fafalone; Sep 4th, 2017 at 02:08 PM.
-
Jan 10th, 2020, 01:50 AM
#20
Fanatic Member
Re: How to get the location of the Recycle Bin for a given drive?
Originally Posted by LaVolpe
... or low-level calls via DispCallFunction API. This may be an option depending on your needs.
Hi Lavolpe,
I am trying to use DispCallFunction in order to get the list of files located in the RecycleBin folder but I am finding it difficult specially when I reach the stage of implementing the IEnumIDList Interface.
Did you ever try or manage to make this work via low level calls instead of via a tlb ?
Regards.
-
Jan 10th, 2020, 04:10 AM
#21
Re: How to get the location of the Recycle Bin for a given drive?
Originally Posted by JAAFAR
I am trying to use DispCallFunction in order to get the list of files located in the RecycleBin folder but I am finding it difficult ...
Why torture yourself with DispCallFunc?
Code:
Public Sub PrintRecycleBinItemPaths()
Const ssfBITBUCKET = 10&
Dim oFolderItem As Object
For Each oFolderItem In CreateObject("Shell.Application").NameSpace(ssfBITBUCKET).Items
Debug.Print """"; oFolderItem.Path; """"
Next
End Sub
Originally Posted by JAAFAR
... instead of via a tlb ?
Why are VBA coders so afraid of TLBs?
-
Jan 10th, 2020, 11:12 AM
#22
Fanatic Member
Re: How to get the location of the Recycle Bin for a given drive?
Originally Posted by Victor Bravo VI
Why torture yourself with DispCallFunc?
Code:
Public Sub PrintRecycleBinItemPaths()
Const ssfBITBUCKET = 10&
Dim oFolderItem As Object
For Each oFolderItem In CreateObject("Shell.Application").NameSpace(ssfBITBUCKET).Items
Debug.Print """"; oFolderItem.Path; """"
Next
End Sub
Why are VB A coders so afraid of TLBs?
Hi Victor,
I know how to list the "names" & "paths" of files and folders located in the Recyclebin using the GetDetailsOf function of the Shell Namespace from Shell32.dll but as the recyclebin is a virtual folder, my understanding is that you cannot subsequently restaure or delete the files with that approach.
As for not wanting to use a tlb, this is because of portability reasons, wanting to keep everything self-contained within the vba host file without the need to register external tlb,dll files. In addition, *and this is the main reason for me* this gives me an opportunity to learn by example ... In fact, I have successfully used DispCallFunc in the past for enumerating & querying the ROT, for extracting a thumbnail image from a shell folder and to certain extent, for querying the IDataObject interface.
Thanks.
Last edited by JAAFAR; Jan 10th, 2020 at 11:18 AM.
-
Jan 12th, 2020, 10:16 PM
#23
Re: [RESOLVED] How to get the location of the Recycle Bin for a given drive?
You use ParseName to get a FolderItem object, then use InvokeVerb. "restore" or "delete".
Be advised there's no Unicode support.
Last edited by fafalone; Jan 12th, 2020 at 10:19 PM.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|