:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / lib / psapi / misc / win32.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/misc/win32.c
8  * PURPOSE:     Win32 interfaces for PSAPI
9  * PROGRAMMER:  KJK::Hyperion <noog@libero.it>
10  * UPDATE HISTORY:
11  *              10/06/2002: Created
12  */
13
14 #include <windows.h>
15 #include <psapi.h>
16 #include <stddef.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <ddk/ntddk.h>
20 #include <internal/psapi.h>
21
22 /* EmptyWorkingSet */
23 BOOL STDCALL EmptyWorkingSet(HANDLE hProcess)
24 {
25  NTSTATUS nErrCode;
26  QUOTA_LIMITS qlProcessQuota;
27
28  /* query the working set */
29  nErrCode = NtQueryInformationProcess
30  (
31   hProcess,
32   ProcessQuotaLimits,
33   &qlProcessQuota,
34   sizeof(qlProcessQuota),
35   NULL
36  );
37
38  /* failure */
39  if(!NT_SUCCESS(nErrCode))
40   goto fail;
41
42  /* empty the working set */
43  qlProcessQuota.MinimumWorkingSetSize = -1;
44  qlProcessQuota.MaximumWorkingSetSize = -1;
45
46  /* set the working set */
47  nErrCode = NtSetInformationProcess
48  (
49   hProcess,
50   ProcessQuotaLimits,
51   &qlProcessQuota,
52   sizeof(qlProcessQuota)
53  );
54
55  /* success */
56  if(NT_SUCCESS(nErrCode))
57   return (TRUE);
58
59 fail:
60  /* failure */
61  SetLastError(RtlNtStatusToDosError(nErrCode));
62  return (FALSE);
63 }
64
65 /* EnumDeviceDrivers */
66 /* callback context */
67 typedef struct _ENUM_DEVICE_DRIVERS_CONTEXT
68 {
69  LPVOID *lpImageBase;
70  DWORD nCount;
71 } ENUM_DEVICE_DRIVERS_CONTEXT, *PENUM_DEVICE_DRIVERS_CONTEXT;
72
73 /* callback routine */
74 NTSTATUS STDCALL EnumDeviceDriversCallback
75 (
76  IN ULONG ModuleCount,
77  IN PSYSTEM_MODULE_ENTRY CurrentModule,
78  IN OUT PVOID CallbackContext
79 )
80 {
81  register PENUM_DEVICE_DRIVERS_CONTEXT peddcContext =
82   (PENUM_DEVICE_DRIVERS_CONTEXT)CallbackContext;
83
84  /* no more buffer space */
85  if(peddcContext->nCount == 0)
86   return STATUS_INFO_LENGTH_MISMATCH;
87
88  /* return current module */
89  *(peddcContext->lpImageBase) = CurrentModule->BaseAddress;
90
91  /* go to next array slot */
92  (peddcContext->lpImageBase) ++;
93  (peddcContext->nCount) --;
94
95  return STATUS_SUCCESS;
96 }
97
98 /* exported interface */
99 BOOL STDCALL EnumDeviceDrivers
100 (
101  LPVOID *lpImageBase,
102  DWORD cb,
103  LPDWORD lpcbNeeded
104 )
105 {
106  register NTSTATUS nErrCode;
107  ENUM_DEVICE_DRIVERS_CONTEXT eddcContext = {lpImageBase, cb / sizeof(PVOID)};
108
109  cb /= sizeof(PVOID);
110
111  /* do nothing if the buffer is empty */
112  if(cb == 0 || lpImageBase == NULL)
113  {
114   *lpcbNeeded = 0;
115   return (TRUE);
116  }
117
118  /* enumerate the system modules */
119  nErrCode = PsaEnumerateSystemModules(&EnumDeviceDriversCallback, &eddcContext);
120
121  /* return the count of bytes returned */
122  *lpcbNeeded = (cb - eddcContext.nCount) * sizeof(PVOID);
123
124  /* success */
125  if(NT_SUCCESS(nErrCode) || nErrCode == STATUS_INFO_LENGTH_MISMATCH)
126   return (TRUE);
127  else
128  {
129   /* failure */
130   SetLastError(RtlNtStatusToDosError(nErrCode));
131   return (FALSE);
132  }
133 }
134
135 /* EnumProcesses */
136 /* callback context */
137 typedef struct _ENUM_PROCESSES_CONTEXT
138 {
139  DWORD *lpidProcess;
140  DWORD nCount;
141 } ENUM_PROCESSES_CONTEXT, *PENUM_PROCESSES_CONTEXT;
142
143 /* callback routine */
144 NTSTATUS STDCALL EnumProcessesCallback
145 (
146  IN PSYSTEM_PROCESS_INFORMATION CurrentProcess,
147  IN OUT PVOID CallbackContext
148 )
149 {
150  register PENUM_PROCESSES_CONTEXT pepcContext =
151   (PENUM_PROCESSES_CONTEXT)CallbackContext;
152
153  /* no more buffer space */
154  if(pepcContext->nCount == 0)
155   return STATUS_INFO_LENGTH_MISMATCH;
156
157  /* return current process */
158  *(pepcContext->lpidProcess) = CurrentProcess->ProcessId;
159
160  /* go to next array slot */
161  (pepcContext->lpidProcess) ++;
162  (pepcContext->nCount) --;
163
164  return STATUS_SUCCESS;
165 }
166
167 /* exported interface */
168 BOOL STDCALL EnumProcesses
169 (
170  DWORD *lpidProcess,
171  DWORD cb,
172  LPDWORD lpcbNeeded
173 )
174 {
175  register NTSTATUS nErrCode;
176  ENUM_PROCESSES_CONTEXT epcContext = {lpidProcess, cb / sizeof(DWORD)};
177
178  cb /= sizeof(DWORD);
179
180  /* do nothing if the buffer is empty */
181  if(cb == 0 || lpidProcess == NULL)
182  {
183   *lpcbNeeded = 0;
184   return (TRUE);
185  }
186
187  /* enumerate the process ids */
188  nErrCode = PsaEnumerateProcesses(&EnumProcessesCallback, &epcContext);
189
190  *lpcbNeeded = (cb - epcContext.nCount) * sizeof(DWORD);
191
192  /* success */
193  if(NT_SUCCESS(nErrCode) || nErrCode == STATUS_INFO_LENGTH_MISMATCH)
194   return (TRUE);
195  else
196  {
197   /* failure */
198   SetLastError(RtlNtStatusToDosError(nErrCode));
199   return (FALSE);
200  }
201 }
202
203 /* EnumProcessModules */
204 /* callback context */
205 typedef struct _ENUM_PROCESS_MODULES_CONTEXT
206 {
207  HMODULE *lphModule;
208  DWORD nCount;
209 } ENUM_PROCESS_MODULES_CONTEXT, *PENUM_PROCESS_MODULES_CONTEXT;
210
211 /* callback routine */
212 NTSTATUS STDCALL EnumProcessModulesCallback
213 (
214  IN HANDLE ProcessHandle,
215  IN PLDR_MODULE CurrentModule,
216  IN OUT PVOID CallbackContext
217 )
218 {
219  register PENUM_PROCESS_MODULES_CONTEXT pepmcContext =
220   (PENUM_PROCESS_MODULES_CONTEXT)CallbackContext;
221
222  /* no more buffer space */
223  if(pepmcContext->nCount == 0)
224   return STATUS_INFO_LENGTH_MISMATCH;
225
226  /* return current process */
227  *(pepmcContext->lphModule) = CurrentModule->BaseAddress;
228
229  /* go to next array slot */
230  (pepmcContext->lphModule) ++;
231  (pepmcContext->nCount) --;
232
233  return STATUS_SUCCESS;
234 }
235
236 /* exported interface */
237 BOOL STDCALL EnumProcessModules(
238   HANDLE hProcess,
239   HMODULE *lphModule,
240   DWORD cb,
241   LPDWORD lpcbNeeded
242 )
243 {
244  register NTSTATUS nErrCode;
245  ENUM_PROCESS_MODULES_CONTEXT epmcContext = {lphModule, cb / sizeof(HMODULE)};
246
247  cb /= sizeof(DWORD);
248
249  /* do nothing if the buffer is empty */
250  if(cb == 0 || lphModule == NULL)
251  {
252   *lpcbNeeded = 0;
253   return (TRUE);
254  }
255
256  /* enumerate the process modules */
257  nErrCode = PsaEnumerateProcessModules
258  (
259   hProcess,
260   &EnumProcessModulesCallback,
261   &epmcContext
262  );
263
264  *lpcbNeeded = (cb - epmcContext.nCount) * sizeof(DWORD);
265
266  /* success */
267  if(NT_SUCCESS(nErrCode) || nErrCode == STATUS_INFO_LENGTH_MISMATCH)
268   return (TRUE);
269  else
270  {
271   /* failure */
272   SetLastError(RtlNtStatusToDosError(nErrCode));
273   return (FALSE);
274  }
275 }
276
277 /* GetDeviceDriverBase/FileName */
278 /* common callback context */
279 typedef struct _GET_DEVICE_DRIVER_NAME_CONTEXT
280 {
281  LPVOID ImageBase;
282  struct
283  {
284   ULONG bFullName:sizeof(ULONG) * 8 / 2;
285   ULONG bUnicode:sizeof(ULONG) * 8 / 2;
286  };
287  DWORD nSize;
288  union
289  {
290   LPVOID lpName;
291   LPSTR lpAnsiName;
292   LPWSTR lpUnicodeName;
293  };
294 } GET_DEVICE_DRIVER_NAME_CONTEXT, *PGET_DEVICE_DRIVER_NAME_CONTEXT;
295
296 /* common callback routine */
297 NTSTATUS STDCALL GetDeviceDriverNameCallback
298 (
299  IN ULONG ModuleCount,
300  IN PSYSTEM_MODULE_ENTRY CurrentModule,
301  IN OUT PVOID CallbackContext
302 )
303 {
304  register PGET_DEVICE_DRIVER_NAME_CONTEXT pgddncContext =
305   (PGET_DEVICE_DRIVER_NAME_CONTEXT) CallbackContext;
306
307  /* module found */
308  if(pgddncContext->ImageBase == CurrentModule->BaseAddress)
309  {
310   register PCHAR pcModuleName;
311   register ULONG l;
312
313   /* get the full name or just the filename part */
314   if(pgddncContext->bFullName)
315    pcModuleName = &CurrentModule->Name[0];
316   else
317    pcModuleName = &CurrentModule->Name[CurrentModule->PathLength];
318
319   /* get the length of the name */
320   l = strlen(pcModuleName);
321
322   /* if user buffer smaller than the name */
323   if(pgddncContext->nSize <= l)
324    /* use the user buffer's length */
325    l = pgddncContext->nSize;
326   /* if user buffer larger than the name */
327   else
328   {
329    /* enough space for the null terminator */
330    l ++;
331    pgddncContext->nSize = l;
332   }
333
334   /* copy the string */
335   if(pgddncContext->bUnicode)
336   {
337    /* Unicode: convert and copy */
338    ANSI_STRING strAnsi = {l, l, pcModuleName};
339    UNICODE_STRING wstrUnicode =
340    {
341      0,
342      l * sizeof(WCHAR),
343      pgddncContext->lpUnicodeName
344    };
345    /* driver names should always be in language-neutral ASCII, so we don't
346       bother calling AreFileApisANSI() */
347    RtlAnsiStringToUnicodeString(&wstrUnicode, &strAnsi, FALSE);
348   }
349   else
350    /* ANSI/OEM: direct copy */
351    memcpy(pgddncContext->lpAnsiName, pcModuleName, l);
352
353   /* terminate the enumeration */
354   return STATUS_NO_MORE_FILES;
355  }
356  /* continue searching */
357  else
358   return STATUS_SUCCESS;
359 }
360
361 /* common internal implementation */
362 DWORD FASTCALL internalGetDeviceDriverName(
363   BOOLEAN bUnicode,
364   BOOLEAN bFullName,
365   LPVOID ImageBase,
366   LPVOID lpName,
367   DWORD nSize
368 )
369 {
370  register NTSTATUS nErrCode;
371  GET_DEVICE_DRIVER_NAME_CONTEXT gddncContext =
372  {
373   ImageBase,
374   { bFullName, bUnicode },
375   nSize,
376   { lpName }
377  };
378
379  /* empty buffer */
380  if(lpName == NULL || nSize == 0)
381   return 0;
382
383  /* invalid image base */
384  if(ImageBase == NULL)
385  {
386   SetLastError(ERROR_INVALID_HANDLE);
387   return 0;
388  }
389
390  /* start the enumeration */
391  nErrCode = PsaEnumerateSystemModules
392  (
393   &GetDeviceDriverNameCallback,
394   &gddncContext
395  );
396
397  if(nErrCode == STATUS_NO_MORE_FILES)
398   /* module was found, return string size */
399   return gddncContext.nSize;
400  else
401  {
402   if(NT_SUCCESS(nErrCode))
403    /* module was not found */
404    SetLastError(ERROR_INVALID_HANDLE);
405   else
406    /* an error occurred */
407    SetLastError(RtlNtStatusToDosError(nErrCode));
408
409   /* failure */
410   return 0;
411  }
412 }
413
414 /* exported interfaces */
415 /*
416  NOTES:
417   - nSize is not, as stated by Microsoft's documentation, the byte size, but the
418     count of characters in the buffer
419   - the return value is the count of characters copied into the buffer
420   - the functions don't attempt to null-terminate the string
421  */
422 DWORD STDCALL GetDeviceDriverBaseNameA(
423   LPVOID ImageBase,
424   LPSTR lpBaseName,
425   DWORD nSize
426 )
427 {
428  return internalGetDeviceDriverName(FALSE, FALSE, ImageBase, lpBaseName, nSize);
429 }
430
431 DWORD STDCALL GetDeviceDriverFileNameA(
432   LPVOID ImageBase,
433   LPSTR lpFilename,
434   DWORD nSize
435 )
436 {
437  return internalGetDeviceDriverName(FALSE, TRUE, ImageBase, lpFilename, nSize);
438 }
439
440 DWORD STDCALL GetDeviceDriverBaseNameW(
441   LPVOID ImageBase,
442   LPWSTR lpBaseName,
443   DWORD nSize
444 )
445 {
446  return internalGetDeviceDriverName(TRUE, FALSE, ImageBase, lpBaseName, nSize);
447 }
448
449 DWORD STDCALL GetDeviceDriverFileNameW(
450   LPVOID ImageBase,
451   LPWSTR lpFilename,
452   DWORD nSize
453 )
454 {
455  return internalGetDeviceDriverName(TRUE, TRUE, ImageBase, lpFilename, nSize);
456 }
457
458 /* GetMappedFileName */
459 /* common internal implementation */
460 DWORD FASTCALL internalGetMappedFileName(
461   BOOLEAN bUnicode,
462   HANDLE hProcess,    
463   LPVOID lpv,         
464   LPVOID lpName,   
465   DWORD nSize         
466 )
467 {
468  register NTSTATUS nErrCode;
469  register ULONG nBufSize;
470  PMEMORY_SECTION_NAME pmsnName;
471
472  /* empty buffer */
473  if(nSize == 0 || (LPSTR)lpName == NULL)
474   return 0;
475
476  if(nSize > (0xFFFF / sizeof(WCHAR)))
477   /* if the user buffer contains more characters than would fit in an
478      UNICODE_STRING, limit the buffer size. RATIONALE: we don't limit buffer
479      size elsewhere because here superfluous buffer size will mean a larger
480      temporary buffer */
481   nBufSize = 0xFFFF / sizeof(WCHAR);
482  else
483   nBufSize = nSize * sizeof(WCHAR);
484  
485  /* allocate the memory */
486  pmsnName = malloc(nBufSize + offsetof(MEMORY_SECTION_NAME, NameBuffer));
487  
488  if(pmsnName == NULL)
489  {
490   /* failure */
491   SetLastError(ERROR_OUTOFMEMORY);
492   return 0;
493  }
494
495  /* initialize the destination buffer */
496  pmsnName->SectionFileName.Length = 0;
497  pmsnName->SectionFileName.Length = nBufSize;
498
499 #if 0
500  __try
501  {
502 #endif
503   /* query the name */
504   nErrCode = NtQueryVirtualMemory
505   (
506    hProcess,
507    lpv,
508    MemorySectionName,
509    pmsnName,
510    nBufSize,
511    NULL
512   );
513   
514   if(!NT_SUCCESS(nErrCode))
515   {
516    /* failure */
517    SetLastError(RtlNtStatusToDosError(nErrCode));
518 #if 0
519 #else
520    /* free the buffer */
521    free(pmsnName);
522 #endif
523    return 0;
524   }
525   
526   /* copy the name */
527   if(bUnicode)
528   {
529    /* destination is an Unicode string: direct copy */
530    memcpy
531    (
532     (LPWSTR)lpName,
533     pmsnName->NameBuffer,
534     pmsnName->SectionFileName.Length
535    );
536    
537 #if 0
538 #else
539    /* free the buffer */
540    free(pmsnName);
541 #endif
542    
543    if(pmsnName->SectionFileName.Length < nSize)
544    {
545     /* null-terminate the string */
546     ((LPWSTR)lpName)[pmsnName->SectionFileName.Length] = 0;
547     return pmsnName->SectionFileName.Length + 1;
548    }
549    
550    return pmsnName->SectionFileName.Length;
551   }
552   else
553   {
554    ANSI_STRING strAnsi = {0, nSize, (LPSTR)lpName};
555
556    if(AreFileApisANSI())
557     /* destination is an ANSI string: convert and copy */
558     RtlUnicodeStringToAnsiString(&strAnsi, &pmsnName->SectionFileName, FALSE);
559    else
560     /* destination is an OEM string: convert and copy */
561     RtlUnicodeStringToOemString(&strAnsi, &pmsnName->SectionFileName, FALSE);
562
563 #if 0
564 #else
565    /* free the buffer */
566    free(pmsnName);
567 #endif
568
569    if(strAnsi.Length < nSize)
570    {
571     /* null-terminate the string */
572     ((LPSTR)lpName)[strAnsi.Length] = 0;
573     return strAnsi.Length + 1;
574    }
575
576    return strAnsi.Length;
577   }
578
579 #if 0
580  }
581  __finally
582  {
583   free(pmsnName);
584  }
585 #endif
586 }
587
588 /* exported interfaces */
589 DWORD STDCALL GetMappedFileNameA(
590   HANDLE hProcess,    
591   LPVOID lpv,         
592   LPSTR lpFilename,   
593   DWORD nSize         
594 )
595 {
596  return internalGetMappedFileName(FALSE, hProcess, lpv, lpFilename, nSize);
597 }
598
599 DWORD STDCALL GetMappedFileNameW(
600   HANDLE hProcess,    
601   LPVOID lpv,         
602   LPWSTR lpFilename,  
603   DWORD nSize         
604 )
605 {
606  return internalGetMappedFileName(TRUE, hProcess, lpv, lpFilename, nSize);
607 }
608
609 /* GetModuleInformation */
610 /* common callback context */
611 typedef struct _GET_MODULE_INFORMATION_FLAGS
612 {
613  ULONG bWantName:sizeof(ULONG) * 8 / 4;
614  ULONG bUnicode:sizeof(ULONG) * 8 / 4;
615  ULONG bFullName:sizeof(ULONG) * 8 / 4;
616 } GET_MODULE_INFORMATION_FLAGS, *PGET_MODULE_INFORMATION_FLAGS;
617
618 typedef struct _GET_MODULE_INFORMATION_CONTEXT
619 {
620  HMODULE hModule;
621  GET_MODULE_INFORMATION_FLAGS Flags;
622  DWORD nBufSize;
623  union
624  {
625   LPWSTR lpUnicodeName;
626   LPSTR lpAnsiName;
627   LPMODULEINFO lpmodinfo;
628   LPVOID lpBuffer;
629  };
630 } GET_MODULE_INFORMATION_CONTEXT, *PGET_MODULE_INFORMATION_CONTEXT;
631
632 /* common callback */
633 NTSTATUS STDCALL GetModuleInformationCallback
634 (
635  IN HANDLE ProcessHandle,
636  IN PLDR_MODULE CurrentModule,
637  IN OUT PVOID CallbackContext
638 )
639 {
640  register PGET_MODULE_INFORMATION_CONTEXT pgmicContext =
641   (PGET_MODULE_INFORMATION_CONTEXT)CallbackContext;
642
643  /* found the module we were looking for */
644  if(CurrentModule->BaseAddress == pgmicContext->hModule)
645  {
646   /* we want the module name */
647   if(pgmicContext->Flags.bWantName)
648   {
649    register NTSTATUS nErrCode;
650    register PUNICODE_STRING pwstrSource;
651    register ULONG l;
652    
653    if(pgmicContext->Flags.bFullName)
654     /* full name */
655     pwstrSource = &(CurrentModule->FullDllName);
656    else
657     /* base name only */
658     pwstrSource = &(CurrentModule->BaseDllName);
659    
660    /* paranoia */
661    pwstrSource->Length -= pwstrSource->Length % sizeof(WCHAR);
662    
663    /* l is the byte size of the user buffer */
664    l = pgmicContext->nBufSize * sizeof(WCHAR);
665    
666    /* if the user buffer has room for the string and a null terminator */
667    if(l >= (pwstrSource->Length + sizeof(WCHAR)))
668    {
669     /* limit the buffer size */
670     l = pwstrSource->Length;
671     
672     /* null-terminate the string */
673     if(pgmicContext->Flags.bUnicode)
674      pgmicContext->lpUnicodeName[l / sizeof(WCHAR)] = 0;
675     else
676      pgmicContext->lpAnsiName[l / sizeof(WCHAR)] = 0;
677    }
678
679    if(pgmicContext->Flags.bUnicode)
680    {
681     /* Unicode: direct copy */
682     /* NOTE: I've chosen not to check for ProcessHandle == NtCurrentProcess(),
683        this function is complicated enough as it is */
684     nErrCode = NtReadVirtualMemory
685     (
686      ProcessHandle,
687      pwstrSource->Buffer,
688      pgmicContext->lpUnicodeName,
689      l,
690      NULL
691     );
692
693     if(NT_SUCCESS(nErrCode))
694      pgmicContext->nBufSize = l / sizeof(WCHAR);
695     else
696     {
697      pgmicContext->nBufSize = 0;
698      return nErrCode;
699     }
700    }
701    else
702    {
703     /* ANSI/OEM: convert and copy */
704     register LPWSTR pwcUnicodeBuf;
705     ANSI_STRING strAnsi = {0, pgmicContext->nBufSize, pgmicContext->lpAnsiName};
706     UNICODE_STRING wstrUnicodeBuf;
707     
708     /* allocate the local buffer */
709     pwcUnicodeBuf = malloc(pwstrSource->Length);
710
711 #if 0
712     __try
713     {
714 #endif
715      if(pwcUnicodeBuf == NULL)
716       /* failure */
717 #if 0
718       return STATUS_NO_MEMORY;
719 #else
720      {
721       nErrCode = STATUS_NO_MEMORY;
722       goto exitWithStatus;
723      }
724 #endif
725  
726      /* copy the string in the local buffer */
727      nErrCode = NtReadVirtualMemory
728      (
729       ProcessHandle,
730       pwstrSource->Buffer,
731       pwcUnicodeBuf,
732       l,
733       NULL
734      );
735  
736      if(!NT_SUCCESS(nErrCode))
737       /* failure */
738 #if 0
739       return nErrCode;
740 #else
741       goto exitWithStatus;
742 #endif
743      
744      /* initialize Unicode string buffer */
745      wstrUnicodeBuf.Length = wstrUnicodeBuf.MaximumLength = l;
746      wstrUnicodeBuf.Buffer = pwcUnicodeBuf;
747      
748      /* convert and copy */
749      if(AreFileApisANSI())
750       RtlUnicodeStringToAnsiString(&strAnsi, &wstrUnicodeBuf, FALSE);
751      else
752       RtlUnicodeStringToOemString(&strAnsi, &wstrUnicodeBuf, FALSE);
753      
754      /* return the string size */
755      pgmicContext->nBufSize = strAnsi.Length;
756 #if 0
757     }
758     __finally
759     {
760      /* free the buffer */
761      free(pwcUnicodeBuf);
762     }
763 #else
764      /* success */
765      nErrCode = STATUS_NO_MORE_FILES;
766
767 exitWithStatus:
768      /* free the buffer */
769      free(pwcUnicodeBuf);
770      return nErrCode;
771 #endif
772    }
773    
774   }
775   /* we want other module information */
776   else
777   {
778    register ULONG nSize = pgmicContext->nBufSize;
779    
780    /* base address */
781    if(nSize >= sizeof(CurrentModule->BaseAddress))
782    {
783     pgmicContext->lpmodinfo->lpBaseOfDll = CurrentModule->BaseAddress;
784     nSize -= sizeof(CurrentModule->BaseAddress);
785    }
786    
787    /* image size */
788    if(nSize >= sizeof(CurrentModule->SizeOfImage))
789    {
790     pgmicContext->lpmodinfo->SizeOfImage = CurrentModule->SizeOfImage;
791     nSize -= sizeof(CurrentModule->SizeOfImage);
792    }
793    
794    /* entry point */
795    if(nSize >= sizeof(CurrentModule->EntryPoint))
796     /* ??? FIXME? is "EntryPoint" just the offset, or the real address? */
797     pgmicContext->lpmodinfo->EntryPoint = (PVOID)CurrentModule->EntryPoint;
798    
799    pgmicContext->nBufSize = TRUE;
800   }
801   
802   return STATUS_NO_MORE_FILES;
803  }
804
805  return STATUS_SUCCESS;
806 }
807
808 /* common internal implementation */
809 DWORD FASTCALL internalGetModuleInformation(
810   HANDLE hProcess,
811   HMODULE hModule,
812   GET_MODULE_INFORMATION_FLAGS Flags,
813   LPVOID lpBuffer,
814   DWORD nBufSize
815 )
816 {
817  register NTSTATUS nErrCode;
818  GET_MODULE_INFORMATION_CONTEXT gmicContext =
819  {
820   hModule,
821   Flags,
822   nBufSize,
823   {lpBuffer}
824  };
825
826
827  nErrCode = PsaEnumerateProcessModules
828  (
829   hProcess,
830   &GetModuleInformationCallback,
831   &gmicContext
832  );
833
834  if(nErrCode == STATUS_NO_MORE_FILES)
835   return gmicContext.nBufSize;
836  else
837  {
838   if(NT_SUCCESS(nErrCode))
839    SetLastError(ERROR_INVALID_HANDLE);
840   else
841    SetLastError(RtlNtStatusToDosError(nErrCode));
842
843   return 0;
844  }
845 }
846
847 /* exported interfaces */
848 DWORD STDCALL GetModuleBaseNameA(
849   HANDLE hProcess,    // handle to process
850   HMODULE hModule,    // handle to module
851   LPSTR lpBaseName,   // base name buffer
852   DWORD nSize         // maximum characters to retrieve
853 )
854 {
855  register GET_MODULE_INFORMATION_FLAGS Flags = {TRUE, FALSE, FALSE};
856  return internalGetModuleInformation
857  (
858   hProcess,
859   hModule,
860   Flags,
861   lpBaseName,
862   nSize
863  );
864 }
865
866 DWORD STDCALL GetModuleBaseNameW(
867   HANDLE hProcess,    // handle to process
868   HMODULE hModule,    // handle to module
869   LPWSTR lpBaseName,  // base name buffer
870   DWORD nSize         // maximum characters to retrieve
871 )
872 {
873  register GET_MODULE_INFORMATION_FLAGS Flags = {TRUE, TRUE, FALSE};
874  return internalGetModuleInformation
875  (
876   hProcess,
877   hModule,
878   Flags,
879   lpBaseName,
880   nSize
881  );
882 }
883
884 DWORD STDCALL GetModuleFileNameExA(
885   HANDLE hProcess,    // handle to process
886   HMODULE hModule,    // handle to module
887   LPSTR lpFilename,   // path buffer
888   DWORD nSize         // maximum characters to retrieve
889 )
890 {
891  register GET_MODULE_INFORMATION_FLAGS Flags = {TRUE, FALSE, TRUE};
892  return internalGetModuleInformation
893  (
894   hProcess,
895   hModule,
896   Flags,
897   lpFilename,
898   nSize
899  );
900 }
901
902 DWORD STDCALL GetModuleFileNameExW(
903   HANDLE hProcess,    // handle to process
904   HMODULE hModule,    // handle to module
905   LPWSTR lpFilename,  // path buffer
906   DWORD nSize         // maximum characters to retrieve
907 )
908 {
909  register GET_MODULE_INFORMATION_FLAGS Flags = {TRUE, TRUE, TRUE};
910  return internalGetModuleInformation
911  (
912   hProcess,
913   hModule,
914   Flags,
915   lpFilename,
916   nSize
917  );
918 }
919
920 BOOL STDCALL GetModuleInformation(
921   HANDLE hProcess,         // handle to process
922   HMODULE hModule,         // handle to module
923   LPMODULEINFO lpmodinfo,  // information buffer
924   DWORD cb                 // size of buffer
925 )
926 {
927  register GET_MODULE_INFORMATION_FLAGS Flags = {FALSE, FALSE, FALSE};
928  return (BOOL)internalGetModuleInformation
929  (
930   hProcess,
931   hModule,
932   Flags,
933   lpmodinfo,
934   cb
935  );
936 }
937 /* EOF */
938