2 * PROJECT: Recycle bin management
3 * LICENSE: GPL v2 - See COPYING in the top level directory
4 * FILE: lib/recyclebin/recyclebin_v5.c
5 * PURPOSE: Deals with recycle bins of Windows 2000/XP/2003
6 * PROGRAMMERS: Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org)
10 #include "recyclebin_v5.h"
17 DWORD RemovableAttributes
= FILE_ATTRIBUTE_READONLY
;
21 FileAttributes
= GetFileAttributesW(FullName
);
22 if (FileAttributes
== INVALID_FILE_ATTRIBUTES
)
24 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
28 if (FileAttributes
& RemovableAttributes
)
30 if (!SetFileAttributesW(FullName
, FileAttributes
& ~RemovableAttributes
))
33 if (FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
35 /* Recursive deletion */
36 /* FIXME: recursive deletion */
37 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
40 if (!RemoveDirectoryW(FullName
))
45 if (!DeleteFileW(FullName
))
57 IRecycleBin5 recycleBinImpl
;
61 DWORD EnumeratorCount
;
64 WCHAR Folder
[ANY_SIZE
]; /* [drive]:\[RECYCLE_BIN_DIRECTORY]\{SID} */
67 static HRESULT STDMETHODCALLTYPE
68 RecycleBin5_RecycleBin5_QueryInterface(
73 struct RecycleBin5
*s
= CONTAINING_RECORD(This
, struct RecycleBin5
, recycleBinImpl
);
78 if (IsEqualIID(riid
, &IID_IUnknown
))
79 *ppvObject
= &s
->recycleBinImpl
;
80 else if (IsEqualIID(riid
, &IID_IRecycleBin
))
81 *ppvObject
= &s
->recycleBinImpl
;
82 else if (IsEqualIID(riid
, &IID_IRecycleBin5
))
83 *ppvObject
= &s
->recycleBinImpl
;
90 IUnknown_AddRef(This
);
94 static ULONG STDMETHODCALLTYPE
95 RecycleBin5_RecycleBin5_AddRef(
98 struct RecycleBin5
*s
= CONTAINING_RECORD(This
, struct RecycleBin5
, recycleBinImpl
);
99 ULONG refCount
= InterlockedIncrement((PLONG
)&s
->ref
);
103 static ULONG STDMETHODCALLTYPE
104 RecycleBin5_RecycleBin5_Release(
107 struct RecycleBin5
*s
= CONTAINING_RECORD(This
, struct RecycleBin5
, recycleBinImpl
);
113 refCount
= InterlockedDecrement((PLONG
)&s
->ref
);
117 CloseHandle(s
->hInfo
);
118 CloseHandle(s
->hInfoMapped
);
125 static HRESULT STDMETHODCALLTYPE
126 RecycleBin5_RecycleBin5_DeleteFile(
127 IN IRecycleBin5
*This
,
128 IN LPCWSTR szFileName
)
130 struct RecycleBin5
*s
= CONTAINING_RECORD(This
, struct RecycleBin5
, recycleBinImpl
);
131 LPWSTR szFullName
= NULL
;
132 DWORD dwBufferLength
= 0;
135 WCHAR DeletedFileName
[MAX_PATH
];
137 HANDLE hFile
= INVALID_HANDLE_VALUE
;
138 PINFO2_HEADER pHeader
= NULL
;
139 PDELETED_FILE_RECORD pDeletedFile
;
140 ULARGE_INTEGER fileSize
;
142 SYSTEMTIME SystemTime
;
143 DWORD ClusterSize
, BytesPerSector
, SectorsPerCluster
;
146 if (s
->EnumeratorCount
!= 0)
149 /* Get full file name */
152 len
= GetFullPathNameW(szFileName
, dwBufferLength
, szFullName
, &lpFilePart
);
156 CoTaskMemFree(szFullName
);
157 return HRESULT_FROM_WIN32(GetLastError());
159 else if (len
< dwBufferLength
)
162 CoTaskMemFree(szFullName
);
163 dwBufferLength
= len
;
164 szFullName
= CoTaskMemAlloc(dwBufferLength
* sizeof(WCHAR
));
166 return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY
);
169 /* Check if file exists */
170 dwAttributes
= GetFileAttributesW(szFullName
);
171 if (dwAttributes
== INVALID_FILE_ATTRIBUTES
)
172 return HRESULT_FROM_WIN32(GetLastError());
174 if (dwBufferLength
< 2 || szFullName
[1] != ':')
176 /* Not a local file */
177 CoTaskMemFree(szFullName
);
178 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
181 hFile
= CreateFileW(szFullName
, 0, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
182 if (hFile
== INVALID_HANDLE_VALUE
)
184 hr
= HRESULT_FROM_WIN32(GetLastError());
188 /* Increase INFO2 file size */
189 CloseHandle(s
->hInfoMapped
);
190 SetFilePointer(s
->hInfo
, sizeof(DELETED_FILE_RECORD
), NULL
, FILE_END
);
191 SetEndOfFile(s
->hInfo
);
192 s
->hInfoMapped
= CreateFileMappingW(s
->hInfo
, NULL
, PAGE_READWRITE
| SEC_COMMIT
, 0, 0, NULL
);
195 hr
= HRESULT_FROM_WIN32(GetLastError());
199 /* Open INFO2 file */
200 pHeader
= MapViewOfFile(s
->hInfoMapped
, FILE_MAP_WRITE
, 0, 0, 0);
203 hr
= HRESULT_FROM_WIN32(GetLastError());
206 pDeletedFile
= ((PDELETED_FILE_RECORD
)(pHeader
+ 1)) + pHeader
->dwNumberOfEntries
;
210 if (!GetFileSizeEx(hFile
, (PLARGE_INTEGER
)&fileSize
))
212 hr
= HRESULT_FROM_WIN32(GetLastError());
216 fileSize
.u
.LowPart
= GetFileSize(hFile
, &fileSize
.u
.HighPart
);
217 if (fileSize
.u
.LowPart
== INVALID_FILE_SIZE
&& GetLastError() != NO_ERROR
)
219 hr
= HRESULT_FROM_WIN32(GetLastError());
223 /* Check if file size is > 4Gb */
224 if (fileSize
.u
.HighPart
!= 0)
226 /* FIXME: how to delete files >= 4Gb? */
230 pHeader
->dwTotalLogicalSize
+= fileSize
.u
.LowPart
;
232 /* Generate new name */
233 pHeader
->dwHighestRecordUniqueId
++;
234 Extension
= wcsrchr(szFullName
, '.');
235 ZeroMemory(pDeletedFile
, sizeof(DELETED_FILE_RECORD
));
236 pDeletedFile
->dwRecordUniqueId
= pHeader
->dwHighestRecordUniqueId
;
237 pDeletedFile
->dwDriveNumber
= tolower(szFullName
[0]) - 'a';
238 _snwprintf(DeletedFileName
, MAX_PATH
, L
"%s\\D%c%lu%s", s
->Folder
, pDeletedFile
->dwDriveNumber
+ 'a', pDeletedFile
->dwRecordUniqueId
, Extension
);
240 /* Get cluster size */
241 if (!GetDiskFreeSpaceW(s
->VolumePath
, &SectorsPerCluster
, &BytesPerSector
, NULL
, NULL
))
243 hr
= HRESULT_FROM_WIN32(GetLastError());
246 ClusterSize
= BytesPerSector
* SectorsPerCluster
;
248 /* Get current time */
249 GetSystemTime(&SystemTime
);
250 if (!SystemTimeToFileTime(&SystemTime
, &pDeletedFile
->DeletionTime
))
252 hr
= HRESULT_FROM_WIN32(GetLastError());
255 pDeletedFile
->dwPhysicalFileSize
= ROUND_UP(fileSize
.u
.LowPart
, ClusterSize
);
258 wcscpy(pDeletedFile
->FileNameW
, szFullName
);
259 if (WideCharToMultiByte(CP_ACP
, 0, pDeletedFile
->FileNameW
, -1, pDeletedFile
->FileNameA
, MAX_PATH
, NULL
, NULL
) == 0)
261 hr
= HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
262 SetLastError(ERROR_INVALID_NAME
);
265 pHeader
->dwNumberOfEntries
++;
268 if (MoveFileW(szFullName
, DeletedFileName
))
271 hr
= HRESULT_FROM_WIN32(GetLastError());
275 UnmapViewOfFile(pHeader
);
276 if (hFile
!= INVALID_HANDLE_VALUE
)
278 CoTaskMemFree(szFullName
);
282 static HRESULT STDMETHODCALLTYPE
283 RecycleBin5_RecycleBin5_EmptyRecycleBin(
284 IN IRecycleBin5
*This
)
286 IRecycleBinEnumList
*prbel
;
287 IRecycleBinFile
*prbf
;
292 hr
= IRecycleBin5_EnumObjects(This
, &prbel
);
295 hr
= IRecycleBinEnumList_Next(prbel
, 1, &prbf
, NULL
);
296 IRecycleBinEnumList_Release(prbel
);
299 hr
= IRecycleBinFile_Delete(prbf
);
300 IRecycleBinFile_Release(prbf
);
306 static HRESULT STDMETHODCALLTYPE
307 RecycleBin5_RecycleBin5_EnumObjects(
308 IN IRecycleBin5
*This
,
309 OUT IRecycleBinEnumList
**ppEnumList
)
311 struct RecycleBin5
*s
= CONTAINING_RECORD(This
, struct RecycleBin5
, recycleBinImpl
);
312 IRecycleBinEnumList
*prbel
;
316 hr
= RecycleBin5_Enumerator_Constructor(This
, s
->hInfo
, s
->hInfoMapped
, s
->Folder
, &pUnk
);
320 hr
= IUnknown_QueryInterface(pUnk
, &IID_IRecycleBinEnumList
, (void **)&prbel
);
323 s
->EnumeratorCount
++;
326 IUnknown_Release(pUnk
);
330 static HRESULT STDMETHODCALLTYPE
331 RecycleBin5_RecycleBin5_Delete(
332 IN IRecycleBin5
*This
,
333 IN LPCWSTR pDeletedFileName
,
334 IN DELETED_FILE_RECORD
*pDeletedFile
)
336 struct RecycleBin5
*s
= CONTAINING_RECORD(This
, struct RecycleBin5
, recycleBinImpl
);
337 ULARGE_INTEGER FileSize
;
338 PINFO2_HEADER pHeader
;
339 DELETED_FILE_RECORD
*pRecord
, *pLast
;
342 if (s
->EnumeratorCount
!= 0)
345 pHeader
= MapViewOfFile(s
->hInfoMapped
, FILE_MAP_WRITE
, 0, 0, 0);
347 return HRESULT_FROM_WIN32(GetLastError());
349 FileSize
.u
.LowPart
= GetFileSize(s
->hInfo
, &FileSize
.u
.HighPart
);
350 if (FileSize
.u
.LowPart
== 0)
352 UnmapViewOfFile(pHeader
);
353 return HRESULT_FROM_WIN32(GetLastError());
355 dwEntries
= (DWORD
)((FileSize
.QuadPart
- sizeof(INFO2_HEADER
)) / sizeof(DELETED_FILE_RECORD
));
357 pRecord
= (DELETED_FILE_RECORD
*)(pHeader
+ 1);
358 for (i
= 0; i
< dwEntries
; i
++)
360 if (pRecord
->dwRecordUniqueId
== pDeletedFile
->dwRecordUniqueId
)
363 if (!IntDeleteRecursive(pDeletedFileName
))
365 UnmapViewOfFile(pHeader
);
366 return HRESULT_FROM_WIN32(GetLastError());
369 /* Clear last entry in the file */
370 MoveMemory(pRecord
, pRecord
+ 1, (dwEntries
- i
- 1) * sizeof(DELETED_FILE_RECORD
));
371 pLast
= pRecord
+ (dwEntries
- i
- 1);
372 ZeroMemory(pLast
, sizeof(DELETED_FILE_RECORD
));
373 UnmapViewOfFile(pHeader
);
376 CloseHandle(s
->hInfoMapped
);
377 SetFilePointer(s
->hInfo
, -(LONG
)sizeof(DELETED_FILE_RECORD
), NULL
, FILE_END
);
378 SetEndOfFile(s
->hInfo
);
379 s
->hInfoMapped
= CreateFileMappingW(s
->hInfo
, NULL
, PAGE_READWRITE
| SEC_COMMIT
, 0, 0, NULL
);
381 return HRESULT_FROM_WIN32(GetLastError());
386 UnmapViewOfFile(pHeader
);
387 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
390 static HRESULT STDMETHODCALLTYPE
391 RecycleBin5_RecycleBin5_Restore(
392 IN IRecycleBin5
*This
,
393 IN LPCWSTR pDeletedFileName
,
394 IN DELETED_FILE_RECORD
*pDeletedFile
)
396 struct RecycleBin5
*s
= CONTAINING_RECORD(This
, struct RecycleBin5
, recycleBinImpl
);
397 ULARGE_INTEGER FileSize
;
398 PINFO2_HEADER pHeader
;
399 DELETED_FILE_RECORD
*pRecord
, *pLast
;
402 if (s
->EnumeratorCount
!= 0)
405 pHeader
= MapViewOfFile(s
->hInfoMapped
, FILE_MAP_WRITE
, 0, 0, 0);
407 return HRESULT_FROM_WIN32(GetLastError());
409 FileSize
.u
.LowPart
= GetFileSize(s
->hInfo
, &FileSize
.u
.HighPart
);
410 if (FileSize
.u
.LowPart
== 0)
412 UnmapViewOfFile(pHeader
);
413 return HRESULT_FROM_WIN32(GetLastError());
415 dwEntries
= (DWORD
)((FileSize
.QuadPart
- sizeof(INFO2_HEADER
)) / sizeof(DELETED_FILE_RECORD
));
417 pRecord
= (DELETED_FILE_RECORD
*)(pHeader
+ 1);
418 for (i
= 0; i
< dwEntries
; i
++)
420 if (pRecord
->dwRecordUniqueId
== pDeletedFile
->dwRecordUniqueId
)
423 if (!MoveFileW(pDeletedFileName
, pDeletedFile
->FileNameW
))
425 UnmapViewOfFile(pHeader
);
426 return HRESULT_FROM_WIN32(GetLastError());
429 /* Clear last entry in the file */
430 MoveMemory(pRecord
, pRecord
+ 1, (dwEntries
- i
- 1) * sizeof(DELETED_FILE_RECORD
));
431 pLast
= pRecord
+ (dwEntries
- i
- 1);
432 ZeroMemory(pLast
, sizeof(DELETED_FILE_RECORD
));
433 UnmapViewOfFile(pHeader
);
436 CloseHandle(s
->hInfoMapped
);
437 SetFilePointer(s
->hInfo
, -(LONG
)sizeof(DELETED_FILE_RECORD
), NULL
, FILE_END
);
438 SetEndOfFile(s
->hInfo
);
439 s
->hInfoMapped
= CreateFileMappingW(s
->hInfo
, NULL
, PAGE_READWRITE
| SEC_COMMIT
, 0, 0, NULL
);
441 return HRESULT_FROM_WIN32(GetLastError());
447 UnmapViewOfFile(pHeader
);
448 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
451 static HRESULT STDMETHODCALLTYPE
452 RecycleBin5_RecycleBin5_OnClosing(
453 IN IRecycleBin5
*This
,
454 IN IRecycleBinEnumList
*prb5el
)
456 struct RecycleBin5
*s
= CONTAINING_RECORD(This
, struct RecycleBin5
, recycleBinImpl
);
457 s
->EnumeratorCount
--;
461 CONST_VTBL
struct IRecycleBin5Vtbl RecycleBin5Vtbl
=
463 RecycleBin5_RecycleBin5_QueryInterface
,
464 RecycleBin5_RecycleBin5_AddRef
,
465 RecycleBin5_RecycleBin5_Release
,
466 RecycleBin5_RecycleBin5_DeleteFile
,
467 RecycleBin5_RecycleBin5_EmptyRecycleBin
,
468 RecycleBin5_RecycleBin5_EnumObjects
,
469 RecycleBin5_RecycleBin5_Delete
,
470 RecycleBin5_RecycleBin5_Restore
,
471 RecycleBin5_RecycleBin5_OnClosing
,
477 IN PSID OwnerSid OPTIONAL
)
479 LPWSTR BufferName
= NULL
;
480 LPWSTR Separator
; /* Pointer into BufferName buffer */
481 LPWSTR FileName
; /* Pointer into BufferName buffer */
482 LPCSTR DesktopIniContents
= "[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n";
483 INFO2_HEADER Info2Contents
[] = { { 5, 0, 0, 0x320, 0 } };
484 SIZE_T BytesToWrite
, BytesWritten
, Needed
;
485 HANDLE hFile
= INVALID_HANDLE_VALUE
;
488 Needed
= (wcslen(Folder
) + 1 + max(wcslen(RECYCLE_BIN_FILE_NAME
), wcslen(L
"desktop.ini")) + 1) * sizeof(WCHAR
);
489 BufferName
= HeapAlloc(GetProcessHeap(), 0, Needed
);
492 hr
= ERROR_NOT_ENOUGH_MEMORY
;
496 wcscpy(BufferName
, Folder
);
497 Separator
= wcsstr(&BufferName
[3], L
"\\");
499 *Separator
= UNICODE_NULL
;
500 if (!CreateDirectoryW(BufferName
, NULL
) && GetLastError() != ERROR_ALREADY_EXISTS
)
502 hr
= HRESULT_FROM_WIN32(GetLastError());
505 SetFileAttributesW(BufferName
, FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
);
509 if (!CreateDirectoryW(BufferName
, NULL
) && GetLastError() != ERROR_ALREADY_EXISTS
)
511 hr
= HRESULT_FROM_WIN32(GetLastError());
520 /* Add ACL to allow only user/SYSTEM to open it */
521 /* FIXME: rc = SetNamedSecurityInfo(
529 if (rc != ERROR_SUCCESS)
531 hr = HRESULT_FROM_WIN32(rc);
537 wcscat(BufferName
, L
"\\");
538 FileName
= &BufferName
[wcslen(BufferName
)];
540 /* Create desktop.ini */
541 wcscpy(FileName
, L
"desktop.ini");
542 hFile
= CreateFileW(BufferName
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
, NULL
);
543 if (hFile
== INVALID_HANDLE_VALUE
)
545 hr
= HRESULT_FROM_WIN32(GetLastError());
548 BytesToWrite
= strlen(DesktopIniContents
);
549 if (!WriteFile(hFile
, DesktopIniContents
, (DWORD
)BytesToWrite
, &BytesWritten
, NULL
))
551 hr
= HRESULT_FROM_WIN32(GetLastError());
554 if (BytesWritten
!= BytesToWrite
)
560 hFile
= INVALID_HANDLE_VALUE
;
562 /* Create empty INFO2 file */
563 wcscpy(FileName
, RECYCLE_BIN_FILE_NAME
);
564 hFile
= CreateFileW(BufferName
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_HIDDEN
, NULL
);
565 if (hFile
== INVALID_HANDLE_VALUE
)
567 hr
= HRESULT_FROM_WIN32(GetLastError());
570 BytesToWrite
= sizeof(Info2Contents
);
571 if (!WriteFile(hFile
, Info2Contents
, (DWORD
)BytesToWrite
, &BytesWritten
, NULL
))
573 hr
= HRESULT_FROM_WIN32(GetLastError());
576 if (BytesWritten
== BytesToWrite
)
582 HeapFree(GetProcessHeap(), 0, BufferName
);
583 if (hFile
!= INVALID_HANDLE_VALUE
)
588 HRESULT
RecycleBin5_Constructor(IN LPCWSTR VolumePath
, OUT IUnknown
**ppUnknown
)
590 struct RecycleBin5
*s
= NULL
;
591 DWORD FileSystemFlags
;
592 LPCWSTR RecycleBinDirectory
;
593 HANDLE tokenHandle
= INVALID_HANDLE_VALUE
;
594 PTOKEN_USER TokenUserInfo
= NULL
;
595 LPWSTR StringSid
= NULL
, p
;
596 SIZE_T Needed
, DirectoryLength
;
603 /* Get information about file system */
604 if (!GetVolumeInformationW(
614 hr
= HRESULT_FROM_WIN32(GetLastError());
617 if (!(FileSystemFlags
& FILE_PERSISTENT_ACLS
))
618 RecycleBinDirectory
= RECYCLE_BIN_DIRECTORY_WITHOUT_ACL
;
621 RecycleBinDirectory
= RECYCLE_BIN_DIRECTORY_WITH_ACL
;
624 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY
, &tokenHandle
))
626 hr
= HRESULT_FROM_WIN32(GetLastError());
629 if (GetTokenInformation(tokenHandle
, TokenUser
, NULL
, 0, &Needed
))
634 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
636 hr
= HRESULT_FROM_WIN32(GetLastError());
639 TokenUserInfo
= HeapAlloc(GetProcessHeap(), 0, Needed
);
645 if (!GetTokenInformation(tokenHandle
, TokenUser
, TokenUserInfo
, (DWORD
)Needed
, &Needed
))
647 hr
= HRESULT_FROM_WIN32(GetLastError());
650 if (!ConvertSidToStringSidW(TokenUserInfo
->User
.Sid
, &StringSid
))
652 hr
= HRESULT_FROM_WIN32(GetLastError());
657 DirectoryLength
= wcslen(VolumePath
) + wcslen(RecycleBinDirectory
) + 1;
659 DirectoryLength
+= wcslen(StringSid
) + 1;
660 DirectoryLength
+= 1 + wcslen(RECYCLE_BIN_FILE_NAME
);
661 DirectoryLength
+= wcslen(VolumePath
) + 1;
662 Needed
= (DirectoryLength
+ 1) * sizeof(WCHAR
);
664 s
= CoTaskMemAlloc(sizeof(struct RecycleBin5
) + Needed
);
670 ZeroMemory(s
, sizeof(struct RecycleBin5
));
671 s
->recycleBinImpl
.lpVtbl
= &RecycleBin5Vtbl
;
674 len
= swprintf(s
->Folder
, L
"%s%s\\%s", VolumePath
, RecycleBinDirectory
, StringSid
);
676 len
= swprintf(s
->Folder
, L
"%s%s", VolumePath
, RecycleBinDirectory
);
678 wcscpy(p
, L
"\\" RECYCLE_BIN_FILE_NAME
);
679 s
->hInfo
= CreateFileW(s
->Folder
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
680 if (s
->hInfo
== INVALID_HANDLE_VALUE
&& (GetLastError() == ERROR_PATH_NOT_FOUND
|| GetLastError() == ERROR_FILE_NOT_FOUND
))
683 hr
= RecycleBin5_Create(s
->Folder
, TokenUserInfo
? TokenUserInfo
->User
.Sid
: NULL
);
687 s
->hInfo
= CreateFileW(s
->Folder
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
689 if (s
->hInfo
== INVALID_HANDLE_VALUE
)
691 hr
= HRESULT_FROM_WIN32(GetLastError());
694 s
->hInfoMapped
= CreateFileMappingW(s
->hInfo
, NULL
, PAGE_READWRITE
| SEC_COMMIT
, 0, 0, NULL
);
697 hr
= HRESULT_FROM_WIN32(GetLastError());
701 s
->VolumePath
= p
+ 1;
702 wcscpy(s
->VolumePath
, VolumePath
);
704 *ppUnknown
= (IUnknown
*)&s
->recycleBinImpl
;
709 if (tokenHandle
!= INVALID_HANDLE_VALUE
)
710 CloseHandle(tokenHandle
);
711 HeapFree(GetProcessHeap(), 0, TokenUserInfo
);
713 LocalFree(StringSid
);
718 if (s
->hInfo
&& s
->hInfo
!= INVALID_HANDLE_VALUE
)
719 CloseHandle(s
->hInfo
);
721 CloseHandle(s
->hInfoMapped
);