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