1c6428cfcb30c8c3a3c903cbc9e0e85095c6ace9
[reactos.git] / reactos / lib / psapi / enum / module.c
1 /* $Id: module.c,v 1.2 2002/08/29 23:57:53 hyperion Exp $
2 */
3 /*
4 * COPYRIGHT: See COPYING in the top level directory
5 * LICENSE: See LGPL.txt in the top level directory
6 * PROJECT: ReactOS system libraries
7 * FILE: reactos/lib/psapi/enum/module.c
8 * PURPOSE: Enumerate system and process modules
9 * PROGRAMMER: KJK::Hyperion <noog@libero.it>
10 * UPDATE HISTORY:
11 * 10/06/2002: Created
12 * 29/08/2002: Generalized the interface to improve reusability,
13 * more efficient use of memory operations
14 */
15
16 #include <ddk/ntddk.h>
17 #include <debug.h>
18 #include <internal/psapi.h>
19 #include <ntdll/ldr.h>
20
21 NTSTATUS
22 STDCALL
23 PsaEnumerateSystemModules
24 (
25 IN PSYSMOD_ENUM_ROUTINE Callback,
26 IN OUT PVOID CallbackContext,
27 IN OUT PVOID AllocatorContext
28 )
29 {
30 ULONG nSize;
31 register NTSTATUS nErrCode = STATUS_SUCCESS;
32 register PULONG pnModuleCount = &nSize;
33 register PSYSTEM_MODULE_ENTRY psmeCurModule;
34 register ULONG nModuleCount;
35
36 /* initial probe */
37 nErrCode = NtQuerySystemInformation
38 (
39 SystemModuleInformation,
40 pnModuleCount,
41 sizeof(nSize),
42 NULL
43 );
44
45 if(nErrCode != STATUS_INFO_LENGTH_MISMATCH && !NT_SUCCESS(nErrCode))
46 {
47 /* failure */
48 DPRINT(FAILED_WITH_STATUS, "NtQuerySystemInformation", nErrCode);
49 return nErrCode;
50 }
51
52 /* RATIONALE: the loading of a system module is a rare occurrence. To minimize
53 memory operations that could be expensive, or fragment the pool/heap, we try
54 to determine the buffer size in advance, knowing that the number of elements
55 is unlikely to change */
56 nSize = sizeof(ULONG) + nSize * sizeof(SYSTEM_MODULE_ENTRY);
57 pnModuleCount = NULL;
58
59 do
60 {
61 register void * pTmp;
62
63 /* free the buffer, and reallocate it to the new size. RATIONALE: since we
64 ignore the buffer's content at this point, there's no point in a realloc(),
65 that could end up copying a large chunk of data we'd discard anyway */
66 PsaFree(AllocatorContext, pnModuleCount);
67 pTmp = PsaMalloc(AllocatorContext, nSize);
68
69 if(pTmp == NULL)
70 {
71 /* failure */
72 nErrCode = STATUS_NO_MEMORY;
73 goto esm_Finalize;
74 }
75
76 pnModuleCount = pTmp;
77
78 /* query the information */
79 nErrCode = NtQuerySystemInformation
80 (
81 SystemModuleInformation,
82 pnModuleCount,
83 nSize,
84 NULL
85 );
86
87 /* double the buffer for the next loop */
88 nSize += nSize;
89 }
90 /* repeat until the buffer is big enough */
91 while(nErrCode == STATUS_INFO_LENGTH_MISMATCH);
92
93 if(!NT_SUCCESS(nErrCode))
94 {
95 /* failure */
96 DPRINT(FAILED_WITH_STATUS, "NtQuerySystemInformation", nErrCode);
97 goto esm_Finalize;
98 }
99
100 /* the array of modules starts right after an ULONG storing their count */
101 psmeCurModule = (PSYSTEM_MODULE_ENTRY)(pnModuleCount + 1);
102
103 nModuleCount = *pnModuleCount;
104
105 /* repeat until all modules have been returned */
106 while(nModuleCount > 0)
107 {
108 /* return current module to the callback */
109 nErrCode = Callback(nModuleCount, psmeCurModule, CallbackContext);
110
111 if(!NT_SUCCESS(nErrCode))
112 /* failure */
113 goto esm_Finalize;
114
115 /* next module */
116 psmeCurModule ++;
117 nModuleCount --;
118 }
119
120 esm_Finalize:
121 /* free the buffer */
122 PsaFree(AllocatorContext, pnModuleCount);
123
124 return (nErrCode);
125 }
126
127 NTSTATUS
128 STDCALL
129 PsaEnumerateProcessModules
130 (
131 IN HANDLE ProcessHandle,
132 IN PPROCMOD_ENUM_ROUTINE Callback,
133 IN OUT PVOID CallbackContext
134 )
135 {
136 register NTSTATUS nErrCode;
137
138 /* current process - use direct memory copy */
139 if(ProcessHandle == NtCurrentProcess())
140 {
141 register PLIST_ENTRY pleListHead;
142 register PLIST_ENTRY pleCurEntry;
143
144 #if 0
145 /* FIXME: activate this when GCC supports SEH */
146 __try
147 {
148 #endif
149 pleListHead = &(NtCurrentPeb()->Ldr->InLoadOrderModuleList);
150 pleCurEntry = pleListHead->Flink;
151
152 while(pleCurEntry != pleListHead)
153 {
154 register PLDR_MODULE plmModule = CONTAINING_RECORD
155 (
156 pleCurEntry,
157 LDR_MODULE,
158 InLoadOrderModuleList
159 );
160
161 /* return the current module to the callback */
162 nErrCode = Callback(ProcessHandle, plmModule, CallbackContext);
163
164 if(!NT_SUCCESS(nErrCode))
165 /* failure */
166 goto epm_Failure;
167
168 pleCurEntry = plmModule->InLoadOrderModuleList.Flink;
169 }
170 #if 0
171 /* FIXME: activate this when GCC supports SEH */
172 }
173 __except(EXCEPTION_EXECUTE_HANDLER)
174 {
175 return GetExceptionCode();
176 }
177 #endif
178 }
179 /* another process */
180 else
181 {
182 PROCESS_BASIC_INFORMATION pbiInfo;
183 PPEB_LDR_DATA ppldLdrData;
184 LDR_MODULE lmModule;
185 PLIST_ENTRY pleListHead;
186 PLIST_ENTRY pleCurEntry;
187
188 /* query the process basic information (includes the PEB address) */
189 nErrCode = NtQueryInformationProcess
190 (
191 ProcessHandle,
192 ProcessBasicInformation,
193 &pbiInfo,
194 sizeof(pbiInfo),
195 NULL
196 );
197
198 if(!NT_SUCCESS(nErrCode))
199 {
200 /* failure */
201 DPRINT(FAILED_WITH_STATUS, "NtQueryInformationProcess", nErrCode);
202 goto epm_Failure;
203 }
204
205 /* get the address of the PE Loader data */
206 nErrCode = NtReadVirtualMemory
207 (
208 ProcessHandle,
209 &(pbiInfo.PebBaseAddress->Ldr),
210 &ppldLdrData,
211 sizeof(ppldLdrData),
212 NULL
213 );
214
215 if(!NT_SUCCESS(nErrCode))
216 {
217 /* failure */
218 DPRINT(FAILED_WITH_STATUS, "NtReadVirtualMemory", nErrCode);
219 goto epm_Failure;
220 }
221
222 /* head of the module list: the last element in the list will point to this */
223 pleListHead = &ppldLdrData->InLoadOrderModuleList;
224
225 /* get the address of the first element in the list */
226 nErrCode = NtReadVirtualMemory
227 (
228 ProcessHandle,
229 &(ppldLdrData->InLoadOrderModuleList.Flink),
230 &pleCurEntry,
231 sizeof(pleCurEntry),
232 NULL
233 );
234
235 while(pleCurEntry != pleListHead)
236 {
237 /* read the current module */
238 nErrCode = NtReadVirtualMemory
239 (
240 ProcessHandle,
241 CONTAINING_RECORD(pleCurEntry, LDR_MODULE, InLoadOrderModuleList),
242 &lmModule,
243 sizeof(lmModule),
244 NULL
245 );
246
247 if(!NT_SUCCESS(nErrCode))
248 {
249 /* failure */
250 DPRINT(FAILED_WITH_STATUS, "NtReadVirtualMemory", nErrCode);
251 goto epm_Failure;
252 }
253
254 /* return the current module to the callback */
255 nErrCode = Callback(ProcessHandle, &lmModule, CallbackContext);
256
257 if(!NT_SUCCESS(nErrCode))
258 /* failure */
259 goto epm_Failure;
260
261 /* address of the next module in the list */
262 pleCurEntry = lmModule.InLoadOrderModuleList.Flink;
263 }
264
265 }
266
267 /* success */
268 return (STATUS_SUCCESS);
269
270 epm_Failure:
271 /* failure */
272 return (nErrCode);
273 }
274
275 /* EOF */