1 /*
2 * PROJECT: Recycle bin management
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Enumerates contents of all recycle bins
5 * COPYRIGHT: Copyright 2007 Hervé Poussineau (hpoussin@reactos.org)
6 * Copyright 2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
7 */
8
9 #include "recyclebin_private.h"
10
11 class RecycleBinGenericEnum : public IRecycleBinEnumList
12 {
13 public:
14 RecycleBinGenericEnum();
15 virtual ~RecycleBinGenericEnum();
16
17 /* IUnknown methods */
18 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override;
19 STDMETHODIMP_(ULONG) AddRef() override;
20 STDMETHODIMP_(ULONG) Release() override;
21
22 /* IRecycleBinEnumList methods */
23 STDMETHODIMP Next(DWORD celt, IRecycleBinFile **rgelt, DWORD *pceltFetched) override;
24 STDMETHODIMP Skip(DWORD celt) override;
25 STDMETHODIMP Reset() override;
26
27 protected:
28 LONG m_ref;
29 IRecycleBinEnumList *m_current;
30 DWORD m_dwLogicalDrives;
31 SIZE_T m_skip;
32 };
33
34 STDMETHODIMP
QueryInterface(REFIID riid,void ** ppvObject)35 RecycleBinGenericEnum::QueryInterface(REFIID riid, void **ppvObject)
36 {
37 TRACE("(%p, %s, %p)\n", this, debugstr_guid(&riid), ppvObject);
38
39 if (!ppvObject)
40 return E_POINTER;
41
42 if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IRecycleBinEnumList))
43 *ppvObject = static_cast<IRecycleBinEnumList *>(this);
44 else
45 {
46 *ppvObject = NULL;
47 return E_NOINTERFACE;
48 }
49
50 AddRef();
51 return S_OK;
52 }
53
STDMETHODIMP_(ULONG)54 STDMETHODIMP_(ULONG)
55 RecycleBinGenericEnum::AddRef()
56 {
57 ULONG refCount = InterlockedIncrement(&m_ref);
58 TRACE("(%p)\n", this);
59 return refCount;
60 }
61
~RecycleBinGenericEnum()62 RecycleBinGenericEnum::~RecycleBinGenericEnum()
63 {
64 TRACE("(%p)\n", this);
65
66 if (m_current)
67 m_current->Release();
68 }
69
STDMETHODIMP_(ULONG)70 STDMETHODIMP_(ULONG)
71 RecycleBinGenericEnum::Release()
72 {
73 TRACE("(%p)\n", this);
74
75 ULONG refCount = InterlockedDecrement(&m_ref);
76 if (refCount == 0)
77 delete this;
78 return refCount;
79 }
80
81 STDMETHODIMP
Next(DWORD celt,IRecycleBinFile ** rgelt,DWORD * pceltFetched)82 RecycleBinGenericEnum::Next(DWORD celt, IRecycleBinFile **rgelt, DWORD *pceltFetched)
83 {
84 TRACE("(%p, %u, %p, %p)\n", this, celt, rgelt, pceltFetched);
85
86 if (!rgelt)
87 return E_POINTER;
88 if (!pceltFetched && celt > 1)
89 return E_INVALIDARG;
90
91 HRESULT hr;
92 DWORD fetched = 0;
93 while (TRUE)
94 {
95 /* Get enumerator implementation */
96 if (!m_current && m_dwLogicalDrives)
97 {
98 for (DWORD i = 0; i < L'Z' - L'A' + 1; ++i)
99 {
100 if (m_dwLogicalDrives & (1 << i))
101 {
102 WCHAR szVolumeName[4];
103 szVolumeName[0] = (WCHAR)(L'A' + i);
104 szVolumeName[1] = L':';
105 szVolumeName[2] = L'\\';
106 szVolumeName[3] = UNICODE_NULL;
107 if (GetDriveTypeW(szVolumeName) != DRIVE_FIXED)
108 {
109 m_dwLogicalDrives &= ~(1 << i);
110 continue;
111 }
112
113 IRecycleBin *prb;
114 hr = GetDefaultRecycleBin(szVolumeName, &prb);
115 if (!SUCCEEDED(hr))
116 return hr;
117 hr = prb->EnumObjects(&m_current);
118 prb->Release();
119
120 if (!SUCCEEDED(hr))
121 return hr;
122
123 m_dwLogicalDrives &= ~(1 << i);
124 break;
125 }
126 }
127 }
128
129 if (!m_current)
130 {
131 /* Nothing more to enumerate */
132 if (pceltFetched)
133 *pceltFetched = fetched;
134 return S_FALSE;
135 }
136
137 /* Skip some elements */
138 while (m_skip > 0)
139 {
140 IRecycleBinFile *rbf;
141 hr = m_current->Next(1, &rbf, NULL);
142 if (hr == S_OK)
143 rbf->Release();
144 else if (hr == S_FALSE)
145 break;
146 else if (!SUCCEEDED(hr))
147 return hr;
148 }
149
150 if (m_skip > 0)
151 continue;
152
153 /* Fill area */
154 DWORD newFetched;
155 hr = m_current->Next(celt - fetched, &rgelt[fetched], &newFetched);
156 if (SUCCEEDED(hr))
157 fetched += newFetched;
158
159 if (hr == S_FALSE || newFetched == 0)
160 {
161 m_current->Release();
162 m_current = NULL;
163 }
164 else if (!SUCCEEDED(hr))
165 return hr;
166
167 if (fetched == celt)
168 {
169 if (pceltFetched)
170 *pceltFetched = fetched;
171 return S_OK;
172 }
173 }
174
175 /* Never go here */
176 UNREACHABLE;
177 }
178
Skip(DWORD celt)179 STDMETHODIMP RecycleBinGenericEnum::Skip(DWORD celt)
180 {
181 TRACE("(%p, %u)\n", this, celt);
182 m_skip += celt;
183 return S_OK;
184 }
185
Reset()186 STDMETHODIMP RecycleBinGenericEnum::Reset()
187 {
188 TRACE("(%p)\n", this);
189
190 if (m_current)
191 {
192 m_current->Release();
193 m_current = NULL;
194 }
195 m_skip = 0;
196 m_dwLogicalDrives = ::GetLogicalDrives();
197 return S_OK;
198 }
199
RecycleBinGenericEnum()200 RecycleBinGenericEnum::RecycleBinGenericEnum()
201 : m_ref(1)
202 , m_current(NULL)
203 , m_dwLogicalDrives(0)
204 , m_skip(0)
205 {
206 }
207
208 EXTERN_C
209 HRESULT
RecycleBinGenericEnum_Constructor(OUT IRecycleBinEnumList ** pprbel)210 RecycleBinGenericEnum_Constructor(
211 OUT IRecycleBinEnumList **pprbel)
212 {
213 RecycleBinGenericEnum *pThis = new RecycleBinGenericEnum();
214 if (!pThis)
215 return E_OUTOFMEMORY;
216
217 *pprbel = static_cast<IRecycleBinEnumList *>(pThis);
218 return (*pprbel)->Reset();
219 }
220