Set last error to ERROR_PROC_NOT_FOUND in case of NULL module in GetProcAddress
[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 (!hModule)
314 {
315 SetLastError(ERROR_PROC_NOT_FOUND);
316 return NULL;
317 }
318
319 if (HIWORD(lpProcName) != 0)
320 {
321 RtlInitAnsiString (&ProcedureName,
322 (LPSTR)lpProcName);
323 Status = LdrGetProcedureAddress ((PVOID)hModule,
324 &ProcedureName,
325 0,
326 (PVOID*)&fnExp);
327 }
328 else
329 {
330 Status = LdrGetProcedureAddress ((PVOID)hModule,
331 NULL,
332 (ULONG)lpProcName,
333 (PVOID*)&fnExp);
334 }
335
336 if (!NT_SUCCESS(Status))
337 {
338 SetLastErrorByStatus(Status);
339 fnExp = NULL;
340 }
341
342 return fnExp;
343 }
344
345
346 /*
347 * @implemented
348 */
349 BOOL WINAPI FreeLibrary(HINSTANCE hLibModule)
350 {
351 NTSTATUS Status;
352
353 if (!hLibModule)
354 {
355 SetLastError(ERROR_INVALID_HANDLE);
356 return FALSE;
357 }
358
359 if ((ULONG_PTR)hLibModule & 1)
360 {
361 /* this is a LOAD_LIBRARY_AS_DATAFILE module */
362 char *ptr = (char *)hLibModule - 1;
363 return UnmapViewOfFile(ptr);
364 }
365
366 Status = LdrUnloadDll(hLibModule);
367 if (!NT_SUCCESS(Status))
368 {
369 SetLastErrorByStatus(Status);
370 return FALSE;
371 }
372
373 return TRUE;
374 }
375
376
377 /*
378 * @implemented
379 */
380 VOID
381 WINAPI
382 FreeLibraryAndExitThread (
383 HMODULE hLibModule,
384 DWORD dwExitCode
385 )
386 {
387 FreeLibrary(hLibModule);
388 ExitThread(dwExitCode);
389 }
390
391
392 /*
393 * @implemented
394 */
395 DWORD
396 WINAPI
397 GetModuleFileNameA (
398 HINSTANCE hModule,
399 LPSTR lpFilename,
400 DWORD nSize
401 )
402 {
403 ANSI_STRING FileName;
404 PLIST_ENTRY ModuleListHead;
405 PLIST_ENTRY Entry;
406 PLDR_DATA_TABLE_ENTRY Module;
407 PPEB Peb;
408 ULONG Length = 0;
409
410 Peb = NtCurrentPeb ();
411 RtlEnterCriticalSection (Peb->LoaderLock);
412
413 if (hModule == NULL)
414 hModule = Peb->ImageBaseAddress;
415
416 ModuleListHead = &Peb->Ldr->InLoadOrderModuleList;
417 Entry = ModuleListHead->Flink;
418
419 while (Entry != ModuleListHead)
420 {
421 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
422 if (Module->DllBase == (PVOID)hModule)
423 {
424 Length = min(nSize, Module->FullDllName.Length / sizeof(WCHAR));
425 FileName.Length = 0;
426 FileName.MaximumLength = (USHORT)Length * sizeof(WCHAR);
427 FileName.Buffer = lpFilename;
428
429 /* convert unicode string to ansi (or oem) */
430 if (bIsFileApiAnsi)
431 RtlUnicodeStringToAnsiString (&FileName,
432 &Module->FullDllName,
433 FALSE);
434 else
435 RtlUnicodeStringToOemString (&FileName,
436 &Module->FullDllName,
437 FALSE);
438
439 if (Length < nSize)
440 lpFilename[Length] = '\0';
441 else
442 SetLastErrorByStatus (STATUS_BUFFER_TOO_SMALL);
443
444 RtlLeaveCriticalSection (Peb->LoaderLock);
445 return Length;
446 }
447
448 Entry = Entry->Flink;
449 }
450
451 SetLastErrorByStatus (STATUS_DLL_NOT_FOUND);
452 RtlLeaveCriticalSection (Peb->LoaderLock);
453
454 return 0;
455 }
456
457
458 /*
459 * @implemented
460 */
461 DWORD
462 WINAPI
463 GetModuleFileNameW (
464 HINSTANCE hModule,
465 LPWSTR lpFilename,
466 DWORD nSize
467 )
468 {
469 UNICODE_STRING FileName;
470 PLIST_ENTRY ModuleListHead;
471 PLIST_ENTRY Entry;
472 PLDR_DATA_TABLE_ENTRY Module;
473 PPEB Peb;
474 ULONG Length = 0;
475
476 Peb = NtCurrentPeb ();
477 RtlEnterCriticalSection (Peb->LoaderLock);
478
479 if (hModule == NULL)
480 hModule = Peb->ImageBaseAddress;
481
482 ModuleListHead = &Peb->Ldr->InLoadOrderModuleList;
483 Entry = ModuleListHead->Flink;
484 while (Entry != ModuleListHead)
485 {
486 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
487
488 if (Module->DllBase == (PVOID)hModule)
489 {
490 Length = min(nSize, Module->FullDllName.Length / sizeof(WCHAR));
491 FileName.Length = 0;
492 FileName.MaximumLength = (USHORT) Length * sizeof(WCHAR);
493 FileName.Buffer = lpFilename;
494
495 RtlCopyUnicodeString (&FileName,
496 &Module->FullDllName);
497 if (Length < nSize)
498 lpFilename[Length] = L'\0';
499 else
500 SetLastErrorByStatus (STATUS_BUFFER_TOO_SMALL);
501
502 RtlLeaveCriticalSection (Peb->LoaderLock);
503
504 return Length;
505 }
506
507 Entry = Entry->Flink;
508 }
509
510 SetLastErrorByStatus (STATUS_DLL_NOT_FOUND);
511 RtlLeaveCriticalSection (Peb->LoaderLock);
512
513 return 0;
514 }
515
516
517 /*
518 * @implemented
519 */
520 HMODULE
521 WINAPI
522 GetModuleHandleA ( LPCSTR lpModuleName )
523 {
524 ANSI_STRING ModuleName;
525 NTSTATUS Status;
526 PTEB pTeb = NtCurrentTeb();
527
528 if (lpModuleName == NULL)
529 {
530 return ((HMODULE)pTeb->ProcessEnvironmentBlock->ImageBaseAddress);
531 }
532
533 RtlInitAnsiString(&ModuleName, lpModuleName);
534
535 Status = RtlAnsiStringToUnicodeString(&pTeb->StaticUnicodeString,
536 &ModuleName,
537 FALSE);
538
539 if (NT_SUCCESS(Status))
540 {
541 return GetModuleHandleW(pTeb->StaticUnicodeString.Buffer);
542 }
543
544 SetLastErrorByStatus(Status);
545 return FALSE;
546 }
547
548
549 /*
550 * @implemented
551 */
552 HMODULE
553 WINAPI
554 GetModuleHandleW (LPCWSTR lpModuleName)
555 {
556 UNICODE_STRING ModuleName;
557 PVOID BaseAddress;
558 NTSTATUS Status;
559
560 if (lpModuleName == NULL)
561 return ((HMODULE)NtCurrentPeb()->ImageBaseAddress);
562
563 RtlInitUnicodeString (&ModuleName,
564 (LPWSTR)lpModuleName);
565
566 Status = LdrGetDllHandle (0,
567 0,
568 &ModuleName,
569 &BaseAddress);
570 if (!NT_SUCCESS(Status))
571 {
572 SetLastErrorByStatus (Status);
573 return NULL;
574 }
575
576 return ((HMODULE)BaseAddress);
577 }
578
579
580 /*
581 * @implemented
582 */
583 BOOL
584 WINAPI
585 GetModuleHandleExW(IN DWORD dwFlags,
586 IN LPCWSTR lpModuleName OPTIONAL,
587 OUT HMODULE* phModule)
588 {
589 HMODULE hModule;
590 NTSTATUS Status;
591 BOOL Ret = FALSE;
592
593 if (phModule == NULL ||
594 ((dwFlags & (GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT)) ==
595 (GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT)))
596 {
597 SetLastError(ERROR_INVALID_PARAMETER);
598 return FALSE;
599 }
600
601 if (lpModuleName == NULL)
602 {
603 hModule = NtCurrentPeb()->ImageBaseAddress;
604 }
605 else
606 {
607 if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)
608 {
609 hModule = (HMODULE)RtlPcToFileHeader((PVOID)lpModuleName,
610 (PVOID*)&hModule);
611 if (hModule == NULL)
612 {
613 SetLastErrorByStatus(STATUS_DLL_NOT_FOUND);
614 }
615 }
616 else
617 {
618 hModule = GetModuleHandleW(lpModuleName);
619 }
620 }
621
622 if (hModule != NULL)
623 {
624 if (!(dwFlags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT))
625 {
626 Status = LdrAddRefDll((dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN) ? LDR_PIN_MODULE : 0,
627 hModule);
628
629 if (NT_SUCCESS(Status))
630 {
631 Ret = TRUE;
632 }
633 else
634 {
635 SetLastErrorByStatus(Status);
636 hModule = NULL;
637 }
638 }
639 else
640 Ret = TRUE;
641 }
642
643 *phModule = hModule;
644 return Ret;
645 }
646
647 /*
648 * @implemented
649 */
650 BOOL
651 WINAPI
652 GetModuleHandleExA(IN DWORD dwFlags,
653 IN LPCSTR lpModuleName OPTIONAL,
654 OUT HMODULE* phModule)
655 {
656 ANSI_STRING ModuleName;
657 LPCWSTR lpModuleNameW;
658 NTSTATUS Status;
659 BOOL Ret;
660
661 PTEB pTeb = NtCurrentTeb();
662
663 if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)
664 {
665 lpModuleNameW = (LPCWSTR)lpModuleName;
666 }
667 else
668 {
669 RtlInitAnsiString(&ModuleName, lpModuleName);
670
671 Status = RtlAnsiStringToUnicodeString(&pTeb->StaticUnicodeString,
672 &ModuleName,
673 FALSE);
674
675 if (!NT_SUCCESS(Status))
676 {
677 SetLastErrorByStatus(Status);
678 return FALSE;
679 }
680
681 lpModuleNameW = pTeb->StaticUnicodeString.Buffer;
682 }
683
684 Ret = GetModuleHandleExW(dwFlags,
685 lpModuleNameW,
686 phModule);
687
688 return Ret;
689 }
690
691
692 /*
693 * @implemented
694 */
695 DWORD
696 WINAPI
697 LoadModule (
698 LPCSTR lpModuleName,
699 LPVOID lpParameterBlock
700 )
701 {
702 STARTUPINFOA StartupInfo;
703 PROCESS_INFORMATION ProcessInformation;
704 LOADPARMS32 *LoadParams;
705 char FileName[MAX_PATH];
706 char *CommandLine, *t;
707 BYTE Length;
708
709 LoadParams = (LOADPARMS32*)lpParameterBlock;
710 if(!lpModuleName || !LoadParams || (((WORD*)LoadParams->lpCmdShow)[0] != 2))
711 {
712 /* windows doesn't check parameters, we do */
713 SetLastError(ERROR_INVALID_PARAMETER);
714 return 0;
715 }
716
717 if(!SearchPathA(NULL, lpModuleName, ".exe", MAX_PATH, FileName, NULL) &&
718 !SearchPathA(NULL, lpModuleName, NULL, MAX_PATH, FileName, NULL))
719 {
720 return ERROR_FILE_NOT_FOUND;
721 }
722
723 Length = (BYTE)LoadParams->lpCmdLine[0];
724 if(!(CommandLine = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY,
725 strlen(lpModuleName) + Length + 2)))
726 {
727 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
728 return 0;
729 }
730
731 /* Create command line string */
732 strcpy(CommandLine, lpModuleName);
733 t = CommandLine + strlen(CommandLine);
734 *(t++) = ' ';
735 memcpy(t, LoadParams->lpCmdLine + 1, Length);
736
737 /* Build StartupInfo */
738 RtlZeroMemory(&StartupInfo, sizeof(STARTUPINFOA));
739 StartupInfo.cb = sizeof(STARTUPINFOA);
740 StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
741 StartupInfo.wShowWindow = ((WORD*)LoadParams->lpCmdShow)[1];
742
743 if(!CreateProcessA(FileName, CommandLine, NULL, NULL, FALSE, 0, LoadParams->lpEnvAddress,
744 NULL, &StartupInfo, &ProcessInformation))
745 {
746 DWORD Error;
747
748 RtlFreeHeap(RtlGetProcessHeap(), 0, CommandLine);
749 /* return the right value */
750 Error = GetLastError();
751 switch(Error)
752 {
753 case ERROR_BAD_EXE_FORMAT:
754 {
755 return ERROR_BAD_FORMAT;
756 }
757 case ERROR_FILE_NOT_FOUND:
758 case ERROR_PATH_NOT_FOUND:
759 {
760 return Error;
761 }
762 }
763 return 0;
764 }
765
766 RtlFreeHeap(RtlGetProcessHeap(), 0, CommandLine);
767
768 /* Wait up to 15 seconds for the process to become idle */
769 if (NULL != lpfnGlobalRegisterWaitForInputIdle)
770 {
771 lpfnGlobalRegisterWaitForInputIdle(ProcessInformation.hProcess, 15000);
772 }
773
774 NtClose(ProcessInformation.hThread);
775 NtClose(ProcessInformation.hProcess);
776
777 return 33;
778 }
779
780 /* EOF */