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