6f4a2dd8197626bc641ab0b96853f6c630a16fe9
[reactos.git] / reactos / dll / win32 / kernel32 / misc / ldr.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT : ReactOS user mode libraries
5 * MODULE : kernel32.dll
6 * FILE : reactos/lib/kernel32/misc/ldr.c
7 * AUTHOR : Ariadne
8 */
9
10 #include <k32.h>
11
12 #define NDEBUG
13 #include <debug.h>
14
15 typedef struct tagLOADPARMS32 {
16 LPSTR lpEnvAddress;
17 LPSTR lpCmdLine;
18 LPSTR lpCmdShow;
19 DWORD dwReserved;
20 } LOADPARMS32;
21
22 extern BOOLEAN InWindows;
23 extern WaitForInputIdleType lpfnGlobalRegisterWaitForInputIdle;
24
25 /* FUNCTIONS ****************************************************************/
26
27 /**
28 * @name GetDllLoadPath
29 *
30 * Internal function to compute the load path to use for a given dll.
31 *
32 * @remarks Returned pointer must be freed by caller.
33 */
34
35 LPWSTR
36 GetDllLoadPath(LPCWSTR lpModule)
37 {
38 ULONG Pos = 0, Length = 0;
39 PWCHAR EnvironmentBufferW = NULL;
40 LPCWSTR lpModuleEnd = NULL;
41 UNICODE_STRING ModuleName;
42 DWORD LastError = GetLastError(); /* GetEnvironmentVariable changes LastError */
43
44 if ((lpModule != NULL) && (wcslen(lpModule) > 2) && (lpModule[1] == ':'))
45 {
46 lpModuleEnd = lpModule + wcslen(lpModule);
47 }
48 else
49 {
50 ModuleName = NtCurrentPeb()->ProcessParameters->ImagePathName;
51 lpModule = ModuleName.Buffer;
52 lpModuleEnd = lpModule + (ModuleName.Length / sizeof(WCHAR));
53 }
54
55 if (lpModule != NULL)
56 {
57 while (lpModuleEnd > lpModule && *lpModuleEnd != L'/' &&
58 *lpModuleEnd != L'\\' && *lpModuleEnd != L':')
59 {
60 --lpModuleEnd;
61 }
62 Length = (lpModuleEnd - lpModule) + 1;
63 }
64
65 Length += GetCurrentDirectoryW(0, NULL);
66 Length += GetDllDirectoryW(0, NULL);
67 Length += GetSystemDirectoryW(NULL, 0);
68 Length += GetWindowsDirectoryW(NULL, 0);
69 Length += GetEnvironmentVariableW(L"PATH", NULL, 0);
70
71 EnvironmentBufferW = RtlAllocateHeap(RtlGetProcessHeap(), 0,
72 Length * sizeof(WCHAR));
73 if (EnvironmentBufferW == NULL)
74 {
75 return NULL;
76 }
77
78 if (lpModule)
79 {
80 RtlCopyMemory(EnvironmentBufferW, lpModule,
81 (lpModuleEnd - lpModule) * sizeof(WCHAR));
82 Pos += lpModuleEnd - lpModule;
83 EnvironmentBufferW[Pos++] = L';';
84 }
85
86 Pos += GetCurrentDirectoryW(Length, EnvironmentBufferW + Pos);
87 EnvironmentBufferW[Pos++] = L';';
88 Pos += GetDllDirectoryW(Length - Pos, EnvironmentBufferW + Pos);
89 EnvironmentBufferW[Pos++] = L';';
90 Pos += GetSystemDirectoryW(EnvironmentBufferW + Pos, Length - Pos);
91 EnvironmentBufferW[Pos++] = L';';
92 Pos += GetWindowsDirectoryW(EnvironmentBufferW + Pos, Length - Pos);
93 EnvironmentBufferW[Pos++] = L';';
94 Pos += GetEnvironmentVariableW(L"PATH", EnvironmentBufferW + Pos, Length - Pos);
95 EnvironmentBufferW[Pos] = 0;
96
97 SetLastError(LastError);
98 return EnvironmentBufferW;
99 }
100
101 /*
102 * @implemented
103 */
104 BOOL
105 WINAPI
106 DisableThreadLibraryCalls (
107 HMODULE hLibModule
108 )
109 {
110 NTSTATUS Status;
111
112 Status = LdrDisableThreadCalloutsForDll ((PVOID)hLibModule);
113 if (!NT_SUCCESS (Status))
114 {
115 SetLastErrorByStatus (Status);
116 return FALSE;
117 }
118 return TRUE;
119 }
120
121
122 /*
123 * @implemented
124 */
125 HINSTANCE
126 WINAPI
127 LoadLibraryA (
128 LPCSTR lpLibFileName
129 )
130 {
131 return LoadLibraryExA (lpLibFileName, 0, 0);
132 }
133
134
135 /*
136 * @implemented
137 */
138 HINSTANCE
139 WINAPI
140 LoadLibraryExA (
141 LPCSTR lpLibFileName,
142 HANDLE hFile,
143 DWORD dwFlags
144 )
145 {
146 PWCHAR FileNameW;
147
148 if (!(FileNameW = FilenameA2W(lpLibFileName, FALSE)))
149 return FALSE;
150
151 return LoadLibraryExW(FileNameW, hFile, dwFlags);
152 }
153
154
155 /*
156 * @implemented
157 */
158 HINSTANCE
159 WINAPI
160 LoadLibraryW (
161 LPCWSTR lpLibFileName
162 )
163 {
164 return LoadLibraryExW (lpLibFileName, 0, 0);
165 }
166
167
168 static
169 NTSTATUS
170 LoadLibraryAsDatafile(PWSTR path, LPCWSTR name, HMODULE* hmod)
171 {
172 static const WCHAR dotDLL[] = {'.','d','l','l',0};
173
174 WCHAR filenameW[MAX_PATH];
175 HANDLE hFile = INVALID_HANDLE_VALUE;
176 HANDLE mapping;
177 HMODULE module;
178
179 *hmod = 0;
180
181 if (!SearchPathW( path, name, dotDLL, sizeof(filenameW) / sizeof(filenameW[0]),
182 filenameW, NULL ))
183 {
184 return NtCurrentTeb()->LastStatusValue;
185 }
186
187 hFile = CreateFileW( filenameW, GENERIC_READ, FILE_SHARE_READ,
188 NULL, OPEN_EXISTING, 0, 0 );
189
190 if (hFile == INVALID_HANDLE_VALUE) return NtCurrentTeb()->LastStatusValue;
191
192 mapping = CreateFileMappingW( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
193 CloseHandle( hFile );
194 if (!mapping) return NtCurrentTeb()->LastStatusValue;
195
196 module = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 );
197 CloseHandle( mapping );
198 if (!module) return NtCurrentTeb()->LastStatusValue;
199
200 /* make sure it's a valid PE file */
201 if (!RtlImageNtHeader(module))
202 {
203 UnmapViewOfFile( module );
204 return STATUS_INVALID_IMAGE_FORMAT;
205 }
206 *hmod = (HMODULE)((char *)module + 1); /* set low bit of handle to indicate datafile module */
207 return STATUS_SUCCESS;
208 }
209
210
211 /*
212 * @implemented
213 */
214 HINSTANCE
215 WINAPI
216 LoadLibraryExW (
217 LPCWSTR lpLibFileName,
218 HANDLE hFile,
219 DWORD dwFlags
220 )
221 {
222 UNICODE_STRING DllName;
223 HINSTANCE hInst;
224 NTSTATUS Status;
225 PWSTR SearchPath;
226 ULONG DllCharacteristics;
227 BOOL FreeString = FALSE;
228
229 (void)hFile;
230
231 if ( lpLibFileName == NULL )
232 return NULL;
233
234 /* Check for any flags LdrLoadDll might be interested in */
235 if (dwFlags & DONT_RESOLVE_DLL_REFERENCES)
236 {
237 /* Tell LDR to treat it as an EXE */
238 DllCharacteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
239 }
240
241 dwFlags &=
242 DONT_RESOLVE_DLL_REFERENCES |
243 LOAD_LIBRARY_AS_DATAFILE |
244 LOAD_WITH_ALTERED_SEARCH_PATH;
245
246 SearchPath = GetDllLoadPath(
247 dwFlags & LOAD_WITH_ALTERED_SEARCH_PATH ? lpLibFileName : NULL);
248
249 RtlInitUnicodeString(&DllName, (LPWSTR)lpLibFileName);
250
251 if (DllName.Buffer[DllName.Length/sizeof(WCHAR) - 1] == L' ')
252 {
253 RtlCreateUnicodeString(&DllName, (LPWSTR)lpLibFileName);
254 while (DllName.Length > sizeof(WCHAR) &&
255 DllName.Buffer[DllName.Length/sizeof(WCHAR) - 1] == L' ')
256 {
257 DllName.Length -= sizeof(WCHAR);
258 }
259 DllName.Buffer[DllName.Length/sizeof(WCHAR)] = UNICODE_NULL;
260 FreeString = TRUE;
261 }
262
263 if (dwFlags & LOAD_LIBRARY_AS_DATAFILE)
264 {
265 Status = LdrGetDllHandle(SearchPath, NULL, &DllName, (PVOID*)&hInst);
266 if (!NT_SUCCESS(Status))
267 {
268 /* The method in load_library_as_datafile allows searching for the
269 * 'native' libraries only
270 */
271 Status = LoadLibraryAsDatafile(SearchPath, DllName.Buffer, &hInst);
272 goto done;
273 }
274 }
275
276 /* HACK!!! FIXME */
277 if (InWindows)
278 {
279 /* Call the API Properly */
280 Status = LdrLoadDll(SearchPath,
281 &DllCharacteristics,
282 &DllName,
283 (PVOID*)&hInst);
284 }
285 else
286 {
287 /* Call the ROS API. NOTE: Don't fix this, I have a patch to merge later. */
288 Status = LdrLoadDll(SearchPath, &dwFlags, &DllName, (PVOID*)&hInst);
289 }
290
291 done:
292 RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
293 if (FreeString)
294 RtlFreeUnicodeString(&DllName);
295 if ( !NT_SUCCESS(Status))
296 {
297 SetLastErrorByStatus (Status);
298 return NULL;
299 }
300
301 return hInst;
302 }
303
304
305 /*
306 * @implemented
307 */
308 FARPROC
309 WINAPI
310 GetProcAddress( HMODULE hModule, LPCSTR lpProcName )
311 {
312 ANSI_STRING ProcedureName;
313 FARPROC fnExp = NULL;
314 NTSTATUS Status;
315
316 if (HIWORD(lpProcName) != 0)
317 {
318 RtlInitAnsiString (&ProcedureName,
319 (LPSTR)lpProcName);
320 Status = LdrGetProcedureAddress ((PVOID)hModule,
321 &ProcedureName,
322 0,
323 (PVOID*)&fnExp);
324 }
325 else
326 {
327 Status = LdrGetProcedureAddress ((PVOID)hModule,
328 NULL,
329 (ULONG)lpProcName,
330 (PVOID*)&fnExp);
331 }
332
333 if (!NT_SUCCESS(Status))
334 {
335 SetLastErrorByStatus(Status);
336 fnExp = NULL;
337 }
338
339 return fnExp;
340 }
341
342
343 /*
344 * @implemented
345 */
346 BOOL WINAPI FreeLibrary(HINSTANCE hLibModule)
347 {
348 NTSTATUS Status;
349
350 if (!hLibModule)
351 {
352 SetLastError(ERROR_INVALID_HANDLE);
353 return FALSE;
354 }
355
356 if ((ULONG_PTR)hLibModule & 1)
357 {
358 /* this is a LOAD_LIBRARY_AS_DATAFILE module */
359 char *ptr = (char *)hLibModule - 1;
360 UnmapViewOfFile(ptr);
361 return TRUE;
362 }
363
364 Status = LdrUnloadDll(hLibModule);
365 if (!NT_SUCCESS(Status))
366 {
367 SetLastErrorByStatus(Status);
368 return FALSE;
369 }
370
371 return TRUE;
372 }
373
374
375 /*
376 * @implemented
377 */
378 VOID
379 WINAPI
380 FreeLibraryAndExitThread (
381 HMODULE hLibModule,
382 DWORD dwExitCode
383 )
384 {
385 FreeLibrary(hLibModule);
386 ExitThread(dwExitCode);
387 }
388
389
390 /*
391 * @implemented
392 */
393 DWORD
394 WINAPI
395 GetModuleFileNameA (
396 HINSTANCE hModule,
397 LPSTR lpFilename,
398 DWORD nSize
399 )
400 {
401 ANSI_STRING FileName;
402 PLIST_ENTRY ModuleListHead;
403 PLIST_ENTRY Entry;
404 PLDR_DATA_TABLE_ENTRY Module;
405 PPEB Peb;
406 ULONG Length = 0;
407
408 Peb = NtCurrentPeb ();
409 RtlEnterCriticalSection (Peb->LoaderLock);
410
411 if (hModule == NULL)
412 hModule = Peb->ImageBaseAddress;
413
414 ModuleListHead = &Peb->Ldr->InLoadOrderModuleList;
415 Entry = ModuleListHead->Flink;
416
417 while (Entry != ModuleListHead)
418 {
419 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
420 if (Module->DllBase == (PVOID)hModule)
421 {
422 Length = min(nSize, Module->FullDllName.Length / sizeof(WCHAR));
423 FileName.Length = 0;
424 FileName.MaximumLength = (USHORT)Length * sizeof(WCHAR);
425 FileName.Buffer = lpFilename;
426
427 /* convert unicode string to ansi (or oem) */
428 if (bIsFileApiAnsi)
429 RtlUnicodeStringToAnsiString (&FileName,
430 &Module->FullDllName,
431 FALSE);
432 else
433 RtlUnicodeStringToOemString (&FileName,
434 &Module->FullDllName,
435 FALSE);
436
437 if (nSize < Length)
438 SetLastErrorByStatus (STATUS_BUFFER_TOO_SMALL);
439 else
440 lpFilename[Length] = '\0';
441
442 RtlLeaveCriticalSection (Peb->LoaderLock);
443 return Length;
444 }
445
446 Entry = Entry->Flink;
447 }
448
449 SetLastErrorByStatus (STATUS_DLL_NOT_FOUND);
450 RtlLeaveCriticalSection (Peb->LoaderLock);
451
452 return 0;
453 }
454
455
456 /*
457 * @implemented
458 */
459 DWORD
460 WINAPI
461 GetModuleFileNameW (
462 HINSTANCE hModule,
463 LPWSTR lpFilename,
464 DWORD nSize
465 )
466 {
467 UNICODE_STRING FileName;
468 PLIST_ENTRY ModuleListHead;
469 PLIST_ENTRY Entry;
470 PLDR_DATA_TABLE_ENTRY Module;
471 PPEB Peb;
472 ULONG Length = 0;
473
474 Peb = NtCurrentPeb ();
475 RtlEnterCriticalSection (Peb->LoaderLock);
476
477 if (hModule == NULL)
478 hModule = Peb->ImageBaseAddress;
479
480 ModuleListHead = &Peb->Ldr->InLoadOrderModuleList;
481 Entry = ModuleListHead->Flink;
482 while (Entry != ModuleListHead)
483 {
484 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
485
486 if (Module->DllBase == (PVOID)hModule)
487 {
488 Length = min(nSize, Module->FullDllName.Length / sizeof(WCHAR));
489 FileName.Length = 0;
490 FileName.MaximumLength = (USHORT) Length * sizeof(WCHAR);
491 FileName.Buffer = lpFilename;
492
493 RtlCopyUnicodeString (&FileName,
494 &Module->FullDllName);
495 if (nSize < Length)
496 SetLastErrorByStatus (STATUS_BUFFER_TOO_SMALL);
497 else
498 lpFilename[Length] = L'\0';
499
500 RtlLeaveCriticalSection (Peb->LoaderLock);
501
502 return Length;
503 }
504
505 Entry = Entry->Flink;
506 }
507
508 SetLastErrorByStatus (STATUS_DLL_NOT_FOUND);
509 RtlLeaveCriticalSection (Peb->LoaderLock);
510
511 return 0;
512 }
513
514
515 /*
516 * @implemented
517 */
518 HMODULE
519 WINAPI
520 GetModuleHandleA ( LPCSTR lpModuleName )
521 {
522 ANSI_STRING ModuleName;
523 NTSTATUS Status;
524 PTEB pTeb = NtCurrentTeb();
525
526 if (lpModuleName == NULL)
527 {
528 return ((HMODULE)pTeb->ProcessEnvironmentBlock->ImageBaseAddress);
529 }
530
531 RtlInitAnsiString(&ModuleName, lpModuleName);
532
533 Status = RtlAnsiStringToUnicodeString(&pTeb->StaticUnicodeString,
534 &ModuleName,
535 FALSE);
536
537 if (NT_SUCCESS(Status))
538 {
539 return GetModuleHandleW(pTeb->StaticUnicodeString.Buffer);
540 }
541
542 SetLastErrorByStatus(Status);
543 return FALSE;
544 }
545
546
547 /*
548 * @implemented
549 */
550 HMODULE
551 WINAPI
552 GetModuleHandleW (LPCWSTR lpModuleName)
553 {
554 UNICODE_STRING ModuleName;
555 PVOID BaseAddress;
556 NTSTATUS Status;
557
558 if (lpModuleName == NULL)
559 return ((HMODULE)NtCurrentPeb()->ImageBaseAddress);
560
561 RtlInitUnicodeString (&ModuleName,
562 (LPWSTR)lpModuleName);
563
564 Status = LdrGetDllHandle (0,
565 0,
566 &ModuleName,
567 &BaseAddress);
568 if (!NT_SUCCESS(Status))
569 {
570 SetLastErrorByStatus (Status);
571 return NULL;
572 }
573
574 return ((HMODULE)BaseAddress);
575 }
576
577
578 /*
579 * @implemented
580 */
581 BOOL
582 WINAPI
583 GetModuleHandleExW(IN DWORD dwFlags,
584 IN LPCWSTR lpModuleName OPTIONAL,
585 OUT HMODULE* phModule)
586 {
587 HMODULE hModule;
588 NTSTATUS Status;
589 BOOL Ret = FALSE;
590
591 if (phModule == NULL ||
592 ((dwFlags & (GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT)) ==
593 (GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT)))
594 {
595 SetLastError(ERROR_INVALID_PARAMETER);
596 return FALSE;
597 }
598
599 if (lpModuleName == NULL)
600 {
601 hModule = NtCurrentPeb()->ImageBaseAddress;
602 }
603 else
604 {
605 if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)
606 {
607 hModule = (HMODULE)RtlPcToFileHeader((PVOID)lpModuleName,
608 (PVOID*)&hModule);
609 if (hModule == NULL)
610 {
611 SetLastErrorByStatus(STATUS_DLL_NOT_FOUND);
612 }
613 }
614 else
615 {
616 hModule = GetModuleHandleW(lpModuleName);
617 }
618 }
619
620 if (hModule != NULL)
621 {
622 if (!(dwFlags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT))
623 {
624 Status = LdrAddRefDll((dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN) ? LDR_PIN_MODULE : 0,
625 hModule);
626
627 if (NT_SUCCESS(Status))
628 {
629 Ret = TRUE;
630 }
631 else
632 {
633 SetLastErrorByStatus(Status);
634 hModule = NULL;
635 }
636 }
637 else
638 Ret = TRUE;
639 }
640
641 *phModule = hModule;
642 return Ret;
643 }
644
645 /*
646 * @implemented
647 */
648 BOOL
649 WINAPI
650 GetModuleHandleExA(IN DWORD dwFlags,
651 IN LPCSTR lpModuleName OPTIONAL,
652 OUT HMODULE* phModule)
653 {
654 ANSI_STRING ModuleName;
655 LPCWSTR lpModuleNameW;
656 NTSTATUS Status;
657 BOOL Ret;
658
659 PTEB pTeb = NtCurrentTeb();
660
661 if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)
662 {
663 lpModuleNameW = (LPCWSTR)lpModuleName;
664 }
665 else
666 {
667 RtlInitAnsiString(&ModuleName, lpModuleName);
668
669 Status = RtlAnsiStringToUnicodeString(&pTeb->StaticUnicodeString,
670 &ModuleName,
671 FALSE);
672
673 if (!NT_SUCCESS(Status))
674 {
675 SetLastErrorByStatus(Status);
676 return FALSE;
677 }
678
679 lpModuleNameW = pTeb->StaticUnicodeString.Buffer;
680 }
681
682 Ret = GetModuleHandleExW(dwFlags,
683 lpModuleNameW,
684 phModule);
685
686 return Ret;
687 }
688
689
690 /*
691 * @implemented
692 */
693 DWORD
694 WINAPI
695 LoadModule (
696 LPCSTR lpModuleName,
697 LPVOID lpParameterBlock
698 )
699 {
700 STARTUPINFOA StartupInfo;
701 PROCESS_INFORMATION ProcessInformation;
702 LOADPARMS32 *LoadParams;
703 char FileName[MAX_PATH];
704 char *CommandLine, *t;
705 BYTE Length;
706
707 LoadParams = (LOADPARMS32*)lpParameterBlock;
708 if(!lpModuleName || !LoadParams || (((WORD*)LoadParams->lpCmdShow)[0] != 2))
709 {
710 /* windows doesn't check parameters, we do */
711 SetLastError(ERROR_INVALID_PARAMETER);
712 return 0;
713 }
714
715 if(!SearchPathA(NULL, lpModuleName, ".exe", MAX_PATH, FileName, NULL) &&
716 !SearchPathA(NULL, lpModuleName, NULL, MAX_PATH, FileName, NULL))
717 {
718 return ERROR_FILE_NOT_FOUND;
719 }
720
721 Length = (BYTE)LoadParams->lpCmdLine[0];
722 if(!(CommandLine = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY,
723 strlen(lpModuleName) + Length + 2)))
724 {
725 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
726 return 0;
727 }
728
729 /* Create command line string */
730 strcpy(CommandLine, lpModuleName);
731 t = CommandLine + strlen(CommandLine);
732 *(t++) = ' ';
733 memcpy(t, LoadParams->lpCmdLine + 1, Length);
734
735 /* Build StartupInfo */
736 RtlZeroMemory(&StartupInfo, sizeof(STARTUPINFOA));
737 StartupInfo.cb = sizeof(STARTUPINFOA);
738 StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
739 StartupInfo.wShowWindow = ((WORD*)LoadParams->lpCmdShow)[1];
740
741 if(!CreateProcessA(FileName, CommandLine, NULL, NULL, FALSE, 0, LoadParams->lpEnvAddress,
742 NULL, &StartupInfo, &ProcessInformation))
743 {
744 DWORD Error;
745
746 RtlFreeHeap(RtlGetProcessHeap(), 0, CommandLine);
747 /* return the right value */
748 Error = GetLastError();
749 switch(Error)
750 {
751 case ERROR_BAD_EXE_FORMAT:
752 {
753 return ERROR_BAD_FORMAT;
754 }
755 case ERROR_FILE_NOT_FOUND:
756 case ERROR_PATH_NOT_FOUND:
757 {
758 return Error;
759 }
760 }
761 return 0;
762 }
763
764 RtlFreeHeap(RtlGetProcessHeap(), 0, CommandLine);
765
766 /* Wait up to 15 seconds for the process to become idle */
767 if (NULL != lpfnGlobalRegisterWaitForInputIdle)
768 {
769 lpfnGlobalRegisterWaitForInputIdle(ProcessInformation.hProcess, 15000);
770 }
771
772 NtClose(ProcessInformation.hThread);
773 NtClose(ProcessInformation.hProcess);
774
775 return 33;
776 }
777
778 /* EOF */