1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5//
6
7//
8// Implementation of CordbEnumerator, a templated COM enumeration pattern on Rs
9// types
10//*****************************************************************************
11
12#include "rspriv.h"
13
14// This CordbEnumerator is a templated enumerator from which COM enumerators for RS types can quickly be fashioned.
15// It uses a private array to store the items it enumerates over so by default it does not reference any
16// other RS type except the process it is associated with. The internal storage type does not need to match the
17// the item type exposed publically so that you can easily create an enumeration that holds RsSmartPtr<CordbThread>
18// but enumerates ICorDebugThread objects as an example. The enumerator has 4 templated parameters which must be
19// defined:
20// ElemType: this is the item type used for storage internal to the enumerator. For most Rs objects you will want
21// to use an RsSmartPtr<T> type to ensure the enumerator holds references to the objects it is
22// containing. The enumerator does not do any explicit Add/Release, it just copies items.
23// ElemPublicType: this is the item type exposed publically via the Next enumeration method. Typically this is
24// an ICorDebugX interface type but it can be anything.
25// EnumInterfaceType: this is the COM interface that the instantiated template will implement. It is expected that
26// this interface type follows the standard ICorDebug COM enumerator pattern, that the interface inherits
27// ICorDebugEnum and defines a strongly typed Next, enumerating over the ElemPublicType.
28// GetPublicType: this is a function which converts from ElemType -> ElemPublicType. It is used to produce the
29// elements that Next actually enumerates from the internal data the enumerator stores. Two conversion
30// functions are already defined here for convenience: QueryInterfaceConvert and IdentityConvert. If
31// neither of those suits your needs then you can define your own.
32//
33// Note: As of right now (10/13/08) most of the ICorDebug enumerators are not implemented using this base class,
34// however it might be good if we converged on this solution. There seems to be quite a bit of redundant and
35// one-off enumeration code that could be eliminated.
36//
37
38// A conversion function that converts from T to U by performing COM QueryInterface
39template<typename T, typename U>
40U * QueryInterfaceConvert(T obj)
41{
42 U* pPublic;
43 obj->QueryInterface(__uuidof(U), (void**) &pPublic);
44 return pPublic;
45}
46
47// A conversion identity function that just returns its argument
48template<typename T>
49T IdentityConvert(T obj)
50{
51 return obj;
52}
53
54// Constructor for an CordbEnumerator.
55// Arguments:
56// pProcess - the CordbProcess with which to associate this enumerator
57// items - the set of items which should be enumerated
58// countItems - the number of items in the array pointed to by items
59//
60// Note that the items are copied into an internal array, and no reference is kept to the users array.
61// Use RsSmartPtr types instead of Rs types directly to keep accurate ref counting for types which need it
62template< typename ElemType,
63 typename ElemPublicType,
64 typename EnumInterfaceType,
65 ElemPublicType (*GetPublicType)(ElemType)>
66CordbEnumerator<ElemType,
67 ElemPublicType,
68 EnumInterfaceType,
69 GetPublicType>::CordbEnumerator(CordbProcess *pProcess,
70 ElemType *items,
71 DWORD countItems) :
72CordbBase(pProcess, 0, enumCordbEnumerator),
73m_countItems(countItems),
74m_nextIndex(0)
75{
76 m_items = new ElemType[countItems];
77 for(UINT i = 0; i < countItems; i++)
78 {
79 m_items[i] = items[i];
80 }
81}
82
83// Constructor for an CordbEnumerator.
84// Arguments:
85// pProcess - the CordbProcess with which to associate this enumerator
86// items - the address of an array of items which should be enumerated
87// countItems - the number of items in the array pointed to by items
88//
89// Note that the items array is simply taken over, setting *items to NULL.
90// Use RsSmartPtr types instead of Rs types directly to keep accurate ref counting for types which need it
91template< typename ElemType,
92 typename ElemPublicType,
93 typename EnumInterfaceType,
94 ElemPublicType (*GetPublicType)(ElemType)>
95CordbEnumerator<ElemType,
96 ElemPublicType,
97 EnumInterfaceType,
98 GetPublicType>::CordbEnumerator(CordbProcess *pProcess,
99 ElemType **items,
100 DWORD countItems) :
101CordbBase(pProcess, 0, enumCordbEnumerator),
102m_countItems(countItems),
103m_nextIndex(0)
104{
105 _ASSERTE(items != NULL);
106 m_items = *items;
107 *items = NULL;
108}
109
110// Destructor
111template< typename ElemType,
112 typename ElemPublicType,
113 typename EnumInterfaceType,
114 ElemPublicType (*GetPublicType)(ElemType)>
115CordbEnumerator<ElemType,
116 ElemPublicType,
117 EnumInterfaceType,
118 GetPublicType>::~CordbEnumerator()
119{
120 // for now at least all of these enumerators should be in neuter lists and get neutered prior to destruction
121 _ASSERTE(IsNeutered());
122}
123
124// COM IUnknown::QueryInterface - provides ICorDebugEnum, IUnknown, and templated EnumInterfaceType
125//
126// Arguments:
127// riid - IID of the interface to query for
128// ppInterface - on output set to a pointer to the desired interface
129//
130// Return:
131// S_OK for the supported interfaces and E_NOINTERFACE otherwise
132template< typename ElemType,
133 typename ElemPublicType,
134 typename EnumInterfaceType,
135 ElemPublicType (*GetPublicType)(ElemType)>
136HRESULT CordbEnumerator<ElemType,
137 ElemPublicType,
138 EnumInterfaceType,
139 GetPublicType>::QueryInterface(REFIID riid, VOID** ppInterface)
140{
141 if(riid == __uuidof(ICorDebugEnum))
142 {
143 *ppInterface = static_cast<ICorDebugEnum*>(this);
144 AddRef();
145 return S_OK;
146 }
147 else if(riid == __uuidof(IUnknown))
148 {
149 *ppInterface = static_cast<IUnknown*>(static_cast<CordbBase*>(this));
150 AddRef();
151 return S_OK;
152 }
153 else if(riid == __uuidof(EnumInterfaceType))
154 {
155 *ppInterface = static_cast<EnumInterfaceType*>(this);
156 AddRef();
157 return S_OK;
158 }
159 else
160 {
161 return E_NOINTERFACE;
162 }
163}
164
165// COM IUnknown::AddRef()
166template< typename ElemType,
167 typename ElemPublicType,
168 typename EnumInterfaceType,
169 ElemPublicType (*GetPublicType)(ElemType)>
170ULONG CordbEnumerator<ElemType,
171 ElemPublicType,
172 EnumInterfaceType,
173 GetPublicType>::AddRef()
174{
175 return BaseAddRef();
176}
177
178// COM IUnknown::Release()
179template< typename ElemType,
180 typename ElemPublicType,
181 typename EnumInterfaceType,
182 ElemPublicType (*GetPublicType)(ElemType)>
183ULONG CordbEnumerator<ElemType,
184 ElemPublicType,
185 EnumInterfaceType,
186 GetPublicType>::Release()
187{
188 return BaseRelease();
189}
190
191// ICorDebugEnum::Clone
192// Makes a duplicate of the enumeration. The internal items are copied by value and there is no explicit reference
193// between the new and old enumerations.
194//
195// Arguments:
196// ppEnum - on output filled with a duplicate enumeration
197//
198// Return:
199// S_OK if the clone was created succesfully, otherwise some appropriate failing HRESULT
200template< typename ElemType,
201 typename ElemPublicType,
202 typename EnumInterfaceType,
203 ElemPublicType (*GetPublicType)(ElemType)>
204HRESULT CordbEnumerator<ElemType,
205 ElemPublicType,
206 EnumInterfaceType,
207 GetPublicType>::Clone(ICorDebugEnum **ppEnum)
208{
209 FAIL_IF_NEUTERED(this);
210 VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorDebugEnum **);
211 HRESULT hr = S_OK;
212 EX_TRY
213 {
214 CordbEnumerator<ElemType, ElemPublicType, EnumInterfaceType, GetPublicType>* clone =
215 new CordbEnumerator<ElemType, ElemPublicType, EnumInterfaceType,GetPublicType>(
216 GetProcess(), m_items, m_countItems);
217 clone->QueryInterface(__uuidof(ICorDebugEnum), (void**)ppEnum);
218 }
219 EX_CATCH_HRESULT(hr)
220 {
221 }
222 return hr;
223}
224
225// ICorDebugEnum::GetCount
226// Gets the number of items in the the list that is being enumerated
227//
228// Arguments:
229// pcelt - on return the number of items being enumerated
230//
231// Return:
232// S_OK or failing HRESULTS for other error conditions
233template< typename ElemType,
234 typename ElemPublicType,
235 typename EnumInterfaceType,
236 ElemPublicType (*GetPublicType)(ElemType)>
237HRESULT CordbEnumerator<ElemType,
238 ElemPublicType,
239 EnumInterfaceType,
240 GetPublicType>::GetCount(ULONG *pcelt)
241{
242 FAIL_IF_NEUTERED(this);
243 VALIDATE_POINTER_TO_OBJECT(pcelt, ULONG *);
244
245 *pcelt = m_countItems;
246 return S_OK;
247}
248
249// ICorDebugEnum::Reset
250// Restarts the enumeration at the beginning of the list
251//
252// Return:
253// S_OK or failing HRESULTS for other error conditions
254template< typename ElemType,
255 typename ElemPublicType,
256 typename EnumInterfaceType,
257 ElemPublicType (*GetPublicType)(ElemType)>
258HRESULT CordbEnumerator<ElemType,
259 ElemPublicType,
260 EnumInterfaceType,
261 GetPublicType>::Reset()
262{
263 FAIL_IF_NEUTERED(this);
264
265 m_nextIndex = 0;
266 return S_OK;
267}
268
269// ICorDebugEnum::Skip
270// Skips over celt items in the enumeration, if celt is greater than the number of remaining items then all
271// remaining items are skipped.
272//
273// Arguments:
274// celt - number of items to be skipped
275//
276// Return:
277// S_OK or failing HRESULTS for other error conditions
278template< typename ElemType,
279 typename ElemPublicType,
280 typename EnumInterfaceType,
281 ElemPublicType (*GetPublicType)(ElemType)>
282HRESULT CordbEnumerator<ElemType,
283 ElemPublicType,
284 EnumInterfaceType,
285 GetPublicType>::Skip(ULONG celt)
286{
287 FAIL_IF_NEUTERED(this);
288
289 m_nextIndex += celt;
290 if(m_nextIndex > m_countItems)
291 {
292 m_nextIndex = m_countItems;
293 }
294 return S_OK;
295}
296
297// EnumInterfaceType::Next
298// Attempts to enumerate the next celt items by copying them in the items array. If fewer than celt
299// items remain all remaining items are enumerated. In either case pceltFetched indicates the number
300// of items actually fetched.
301//
302// Arguments:
303// celt - the number of enumerated items requested
304// items - an array of size celt where the enumerated items will be copied
305// pceltFetched - on return, the actual number of items enumerated
306//
307// Return:
308// S_OK if all items could be enumerated, S_FALSE if not all the requested items were enumerated,
309// failing HRESULTS for other error conditions
310template< typename ElemType,
311 typename ElemPublicType,
312 typename EnumInterfaceType,
313 ElemPublicType (*GetPublicType)(ElemType)>
314HRESULT CordbEnumerator<ElemType,
315 ElemPublicType,
316 EnumInterfaceType,
317 GetPublicType>::Next(ULONG celt,
318 ElemPublicType items[],
319 ULONG *pceltFetched)
320{
321 FAIL_IF_NEUTERED(this);
322 VALIDATE_POINTER_TO_OBJECT_ARRAY(items, ElemInterfaceType *,
323 celt, true, true);
324 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pceltFetched, ULONG *);
325
326 if ((pceltFetched == NULL) && (celt != 1))
327 {
328 return E_INVALIDARG;
329 }
330
331 ULONG countFetched;
332 for(countFetched = 0; countFetched < celt && m_nextIndex < m_countItems; countFetched++, m_nextIndex++)
333 {
334 items[countFetched] = GetPublicType(m_items[m_nextIndex]);
335 }
336
337 if(pceltFetched != NULL)
338 {
339 *pceltFetched = countFetched;
340 }
341
342 return countFetched == celt ? S_OK : S_FALSE;
343}
344
345// Neuter
346// neuters the enumerator and deletes the contents (the contents are not explicitly neutered though)
347template< typename ElemType,
348 typename ElemPublicType,
349 typename EnumInterfaceType,
350 ElemPublicType (*GetPublicType)(ElemType)>
351VOID CordbEnumerator<ElemType,
352 ElemPublicType,
353 EnumInterfaceType,
354 GetPublicType>::Neuter()
355{
356 delete [] m_items;
357 m_items = NULL;
358 m_countItems = 0;
359 m_nextIndex = 0;
360 CordbBase::Neuter();
361}
362