branch update for HEAD-2003050101
[reactos.git] / lib / kernel32 / process / proc.c
1 /* $Id$
2  *
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS system libraries
5  * FILE:            lib/kernel32/proc/proc.c
6  * PURPOSE:         Process functions
7  * PROGRAMMER:      Ariadne ( ariadne@xs4all.nl)
8  * UPDATE HISTORY:
9  *                  Created 01/11/98
10  */
11
12 /* INCLUDES ****************************************************************/
13
14 #include <k32.h>
15
16
17 #define NDEBUG
18 #include <kernel32/kernel32.h>
19
20
21 /* GLOBALS *******************************************************************/
22
23 WaitForInputIdleType  lpfnGlobalRegisterWaitForInputIdle;
24
25 LPSTARTUPINFO lpLocalStartupInfo = NULL;
26
27 VOID STDCALL
28 RegisterWaitForInputIdle(WaitForInputIdleType lpfnRegisterWaitForInputIdle);
29
30 WINBOOL STDCALL
31 GetProcessId (HANDLE hProcess, LPDWORD lpProcessId);
32
33
34 /* FUNCTIONS ****************************************************************/
35
36 BOOL STDCALL
37 GetProcessAffinityMask (HANDLE hProcess,
38                         LPDWORD lpProcessAffinityMask,
39                         LPDWORD lpSystemAffinityMask)
40 {
41   PROCESS_BASIC_INFORMATION ProcessInfo;
42   ULONG BytesWritten;
43   NTSTATUS Status;
44
45   Status = NtQueryInformationProcess (hProcess,
46                                       ProcessBasicInformation,
47                                       (PVOID)&ProcessInfo,
48                                       sizeof(PROCESS_BASIC_INFORMATION),
49                                       &BytesWritten);
50   if (!NT_SUCCESS(Status))
51     {
52       SetLastError (Status);
53       return FALSE;
54     }
55
56   *lpProcessAffinityMask = (DWORD)ProcessInfo.AffinityMask;
57
58   /* FIXME */
59   *lpSystemAffinityMask  = 0x00000001;
60
61   return TRUE;
62 }
63
64
65 BOOL STDCALL
66 SetProcessAffinityMask (HANDLE hProcess,
67                         DWORD dwProcessAffinityMask)
68 {
69   NTSTATUS Status;
70
71   Status = NtSetInformationProcess (hProcess,
72                                     ProcessAffinityMask,
73                                     (PVOID)&dwProcessAffinityMask,
74                                     sizeof(DWORD));
75   if (!NT_SUCCESS(Status))
76     {
77       SetLastError (Status);
78       return FALSE;
79     }
80
81   return TRUE;
82 }
83
84
85 WINBOOL STDCALL
86 GetProcessShutdownParameters (LPDWORD lpdwLevel,
87                               LPDWORD lpdwFlags)
88 {
89   CSRSS_API_REQUEST CsrRequest;
90   CSRSS_API_REPLY CsrReply;
91   NTSTATUS Status;
92
93   CsrRequest.Type = CSRSS_GET_SHUTDOWN_PARAMETERS;
94   Status = CsrClientCallServer(&CsrRequest,
95                                &CsrReply,
96                                sizeof(CSRSS_API_REQUEST),
97                                sizeof(CSRSS_API_REPLY));
98   if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrReply.Status))
99     {
100       SetLastError(Status);
101       return(FALSE);
102     }
103
104   *lpdwLevel = CsrReply.Data.GetShutdownParametersReply.Level;
105   *lpdwFlags = CsrReply.Data.GetShutdownParametersReply.Flags;
106
107   return(TRUE);
108 }
109
110
111 WINBOOL STDCALL
112 SetProcessShutdownParameters (DWORD dwLevel,
113                               DWORD dwFlags)
114 {
115   CSRSS_API_REQUEST CsrRequest;
116   CSRSS_API_REPLY CsrReply;
117   NTSTATUS Status;
118
119   CsrRequest.Data.SetShutdownParametersRequest.Level = dwLevel;
120   CsrRequest.Data.SetShutdownParametersRequest.Flags = dwFlags;
121
122   CsrRequest.Type = CSRSS_SET_SHUTDOWN_PARAMETERS;
123   Status = CsrClientCallServer(&CsrRequest,
124                                &CsrReply,
125                                sizeof(CSRSS_API_REQUEST),
126                                sizeof(CSRSS_API_REPLY));
127   if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrReply.Status))
128     {
129       SetLastError(Status);
130       return(FALSE);
131     }
132
133   return(TRUE);
134 }
135
136
137 WINBOOL STDCALL
138 GetProcessWorkingSetSize (HANDLE hProcess,
139                           LPDWORD lpMinimumWorkingSetSize,
140                           LPDWORD lpMaximumWorkingSetSize)
141 {
142   QUOTA_LIMITS QuotaLimits;
143   NTSTATUS Status;
144
145   Status = NtQueryInformationProcess(hProcess,
146                                      ProcessQuotaLimits,
147                                      &QuotaLimits,
148                                      sizeof(QUOTA_LIMITS),
149                                      NULL);
150   if (!NT_SUCCESS(Status))
151     {
152       SetLastErrorByStatus(Status);
153       return(FALSE);
154     }
155
156   *lpMinimumWorkingSetSize = (DWORD)QuotaLimits.MinimumWorkingSetSize;
157   *lpMaximumWorkingSetSize = (DWORD)QuotaLimits.MaximumWorkingSetSize;
158
159   return(TRUE);
160 }
161
162
163 WINBOOL STDCALL
164 SetProcessWorkingSetSize(HANDLE hProcess,
165                          DWORD dwMinimumWorkingSetSize,
166                          DWORD dwMaximumWorkingSetSize)
167 {
168   SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
169   return(FALSE);
170 }
171
172
173 WINBOOL STDCALL
174 GetProcessTimes(HANDLE hProcess,
175                 LPFILETIME lpCreationTime,
176                 LPFILETIME lpExitTime,
177                 LPFILETIME lpKernelTime,
178                 LPFILETIME lpUserTime)
179 {
180   KERNEL_USER_TIMES Kut;
181   NTSTATUS Status;
182
183   Status = NtQueryInformationProcess(hProcess,
184                                      ProcessTimes,
185                                      &Kut,
186                                      sizeof(Kut),
187                                      NULL);
188   if (!NT_SUCCESS(Status))
189     {
190       SetLastErrorByStatus(Status);
191       return(FALSE);
192     }
193
194   lpCreationTime->dwLowDateTime = Kut.CreateTime.u.LowPart;
195   lpCreationTime->dwHighDateTime = Kut.CreateTime.u.HighPart;
196
197   lpExitTime->dwLowDateTime = Kut.ExitTime.u.LowPart;
198   lpExitTime->dwHighDateTime = Kut.ExitTime.u.HighPart;
199
200   lpKernelTime->dwLowDateTime = Kut.KernelTime.u.LowPart;
201   lpKernelTime->dwHighDateTime = Kut.KernelTime.u.HighPart;
202
203   lpUserTime->dwLowDateTime = Kut.UserTime.u.LowPart;
204   lpUserTime->dwHighDateTime = Kut.UserTime.u.HighPart;
205
206   return(TRUE);
207 }
208
209
210 HANDLE STDCALL
211 GetCurrentProcess(VOID)
212 {
213   return((HANDLE)NtCurrentProcess());
214 }
215
216
217 HANDLE STDCALL
218 GetCurrentThread(VOID)
219 {
220   return((HANDLE)NtCurrentThread());
221 }
222
223
224 DWORD STDCALL
225 GetCurrentProcessId(VOID)
226 {
227   return((DWORD)GetTeb()->Cid.UniqueProcess);
228 }
229
230
231 WINBOOL STDCALL
232 GetExitCodeProcess(HANDLE hProcess,
233                    LPDWORD lpExitCode)
234 {
235   PROCESS_BASIC_INFORMATION ProcessBasic;
236   ULONG BytesWritten;
237   NTSTATUS Status;
238
239   Status = NtQueryInformationProcess(hProcess,
240                                      ProcessBasicInformation,
241                                      &ProcessBasic,
242                                      sizeof(PROCESS_BASIC_INFORMATION),
243                                      &BytesWritten);
244   if (!NT_SUCCESS(Status))
245     {
246       SetLastErrorByStatus(Status);
247       return(FALSE);
248      }
249
250   memcpy(lpExitCode, &ProcessBasic.ExitStatus, sizeof(DWORD));
251
252   return(TRUE);
253 }
254
255
256 WINBOOL STDCALL
257 GetProcessId(HANDLE hProcess,
258              LPDWORD lpProcessId)
259 {
260   PROCESS_BASIC_INFORMATION ProcessBasic;
261   ULONG BytesWritten;
262   NTSTATUS Status;
263
264   Status = NtQueryInformationProcess(hProcess,
265                                      ProcessBasicInformation,
266                                      &ProcessBasic,
267                                      sizeof(PROCESS_BASIC_INFORMATION),
268                                      &BytesWritten);
269   if (!NT_SUCCESS(Status))
270     {
271       SetLastErrorByStatus(Status);
272       return(FALSE);
273     }
274
275   memcpy(lpProcessId, &ProcessBasic.UniqueProcessId, sizeof(DWORD));
276
277   return(TRUE);
278 }
279
280
281 HANDLE STDCALL
282 OpenProcess(DWORD dwDesiredAccess,
283             WINBOOL bInheritHandle,
284             DWORD dwProcessId)
285 {
286    NTSTATUS errCode;
287    HANDLE ProcessHandle;
288    OBJECT_ATTRIBUTES ObjectAttributes;
289    CLIENT_ID ClientId ;
290    
291    ClientId.UniqueProcess = (HANDLE)dwProcessId;
292    ClientId.UniqueThread = INVALID_HANDLE_VALUE;
293    
294    ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
295    ObjectAttributes.RootDirectory = (HANDLE)NULL;
296    ObjectAttributes.SecurityDescriptor = NULL;
297    ObjectAttributes.SecurityQualityOfService = NULL;
298    ObjectAttributes.ObjectName = NULL;
299    
300    if (bInheritHandle == TRUE)
301      ObjectAttributes.Attributes = OBJ_INHERIT;
302    else
303      ObjectAttributes.Attributes = 0;
304    
305    errCode = NtOpenProcess(&ProcessHandle,
306                            dwDesiredAccess,
307                            &ObjectAttributes,
308                            &ClientId);
309    if (!NT_SUCCESS(errCode))
310      {
311         SetLastErrorByStatus (errCode);
312         return NULL;
313      }
314    return ProcessHandle;
315 }
316
317
318 UINT STDCALL
319 WinExec(LPCSTR lpCmdLine,
320         UINT uCmdShow)
321 {
322    STARTUPINFOA StartupInfo;
323    PROCESS_INFORMATION  ProcessInformation;
324    HINSTANCE hInst;
325    DWORD dosErr;
326
327    StartupInfo.cb = sizeof(STARTUPINFOA);
328    StartupInfo.wShowWindow = uCmdShow;
329    StartupInfo.dwFlags = 0;
330
331    hInst = (HINSTANCE)CreateProcessA(NULL,
332                                      (PVOID)lpCmdLine,
333                                      NULL,
334                                      NULL,
335                                      FALSE,
336                                      0,
337                                      NULL,
338                                      NULL,
339                                      &StartupInfo,
340                                      &ProcessInformation);
341    if ( hInst == NULL )
342      {
343         dosErr = GetLastError();
344         return dosErr;
345      }
346    if (NULL != lpfnGlobalRegisterWaitForInputIdle)
347    {
348      lpfnGlobalRegisterWaitForInputIdle (
349         ProcessInformation.hProcess,
350         10000
351         );
352    }
353    NtClose (ProcessInformation.hProcess);
354    NtClose (ProcessInformation.hThread);
355    return 0;    
356 }
357
358
359 VOID STDCALL
360 RegisterWaitForInputIdle (
361         WaitForInputIdleType    lpfnRegisterWaitForInputIdle
362         )
363 {
364         lpfnGlobalRegisterWaitForInputIdle = lpfnRegisterWaitForInputIdle;
365         return;
366 }
367
368
369 DWORD STDCALL
370 WaitForInputIdle (
371         HANDLE  hProcess,
372         DWORD   dwMilliseconds
373         )
374 {
375         return 0;
376 }
377
378
379 VOID STDCALL
380 Sleep(DWORD dwMilliseconds)
381 {
382   SleepEx(dwMilliseconds, FALSE);
383   return;
384 }
385
386
387 DWORD STDCALL
388 SleepEx(DWORD dwMilliseconds,
389         BOOL bAlertable)
390 {
391   TIME Interval;
392   NTSTATUS errCode;
393   
394   if (dwMilliseconds != INFINITE)
395     {
396       /*
397        * System time units are 100 nanoseconds (a nanosecond is a billionth of
398        * a second).
399        */
400       Interval.QuadPart = dwMilliseconds;
401       Interval.QuadPart = -(Interval.QuadPart * 10000);
402     }  
403   else
404     {
405       /* Approximately 292000 years hence */
406       Interval.QuadPart = -0x7FFFFFFFFFFFFFFF;
407     }
408
409   errCode = NtDelayExecution (bAlertable, &Interval);
410   if (!NT_SUCCESS(errCode))
411     {
412       SetLastErrorByStatus (errCode);
413       return -1;
414     }
415   return 0;
416 }
417
418
419 VOID STDCALL
420 GetStartupInfoW(LPSTARTUPINFOW lpStartupInfo)
421 {
422   PRTL_USER_PROCESS_PARAMETERS Params;
423
424   if (lpStartupInfo == NULL)
425     {
426       SetLastError(ERROR_INVALID_PARAMETER);
427       return;
428     }
429
430   Params = NtCurrentPeb()->ProcessParameters;
431
432   lpStartupInfo->cb = sizeof(STARTUPINFOW);
433   lpStartupInfo->lpDesktop = Params->DesktopInfo.Buffer;
434   lpStartupInfo->lpTitle = Params->WindowTitle.Buffer;
435   lpStartupInfo->dwX = Params->dwX;
436   lpStartupInfo->dwY = Params->dwY;
437   lpStartupInfo->dwXSize = Params->dwXSize;
438   lpStartupInfo->dwYSize = Params->dwYSize;
439   lpStartupInfo->dwXCountChars = Params->dwXCountChars;
440   lpStartupInfo->dwYCountChars = Params->dwYCountChars;
441   lpStartupInfo->dwFillAttribute = Params->dwFillAttribute;
442   lpStartupInfo->dwFlags = Params->dwFlags;
443   lpStartupInfo->wShowWindow = Params->wShowWindow;
444   lpStartupInfo->lpReserved = Params->ShellInfo.Buffer;
445   lpStartupInfo->cbReserved2 = Params->RuntimeInfo.Length;
446   lpStartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeInfo.Buffer;
447
448   lpStartupInfo->hStdInput = Params->hStdInput;
449   lpStartupInfo->hStdOutput = Params->hStdOutput;
450   lpStartupInfo->hStdError = Params->hStdError;
451 }
452
453
454 VOID STDCALL
455 GetStartupInfoA(LPSTARTUPINFOA lpStartupInfo)
456 {
457   PRTL_USER_PROCESS_PARAMETERS Params;
458   ANSI_STRING AnsiString;
459
460   if (lpStartupInfo == NULL)
461     {
462         SetLastError(ERROR_INVALID_PARAMETER);
463         return;
464     }
465
466   Params = NtCurrentPeb ()->ProcessParameters;
467
468   RtlAcquirePebLock ();
469
470   if (lpLocalStartupInfo == NULL)
471     {
472         /* create new local startup info (ansi) */
473         lpLocalStartupInfo = RtlAllocateHeap (RtlGetProcessHeap (),
474                                               0,
475                                               sizeof(STARTUPINFOA));
476
477         lpLocalStartupInfo->cb = sizeof(STARTUPINFOA);
478
479         /* copy window title string */
480         RtlUnicodeStringToAnsiString (&AnsiString,
481                                       &Params->WindowTitle,
482                                       TRUE);
483         lpLocalStartupInfo->lpTitle = AnsiString.Buffer;
484
485         /* copy desktop info string */
486         RtlUnicodeStringToAnsiString (&AnsiString,
487                                       &Params->DesktopInfo,
488                                       TRUE);
489         lpLocalStartupInfo->lpDesktop = AnsiString.Buffer;
490
491         /* copy shell info string */
492         RtlUnicodeStringToAnsiString (&AnsiString,
493                                       &Params->ShellInfo,
494                                       TRUE);
495         lpLocalStartupInfo->lpReserved = AnsiString.Buffer;
496
497         lpLocalStartupInfo->dwX = Params->dwX;
498         lpLocalStartupInfo->dwY = Params->dwY;
499         lpLocalStartupInfo->dwXSize = Params->dwXSize;
500         lpLocalStartupInfo->dwYSize = Params->dwYSize;
501         lpLocalStartupInfo->dwXCountChars = Params->dwXCountChars;
502         lpLocalStartupInfo->dwYCountChars = Params->dwYCountChars;
503         lpLocalStartupInfo->dwFillAttribute = Params->dwFillAttribute;
504         lpLocalStartupInfo->dwFlags = Params->dwFlags;
505         lpLocalStartupInfo->wShowWindow = Params->wShowWindow;
506         lpLocalStartupInfo->cbReserved2 = Params->RuntimeInfo.Length;
507         lpLocalStartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeInfo.Buffer;
508
509         lpLocalStartupInfo->hStdInput = Params->hStdInput;
510         lpLocalStartupInfo->hStdOutput = Params->hStdOutput;
511         lpLocalStartupInfo->hStdError = Params->hStdError;
512      }
513
514    RtlReleasePebLock ();
515
516    /* copy local startup info data to external startup info */
517    memcpy (lpStartupInfo,
518            lpLocalStartupInfo,
519            sizeof(STARTUPINFOA));
520 }
521
522
523 BOOL STDCALL
524 FlushInstructionCache (HANDLE   hProcess,
525                        LPCVOID  lpBaseAddress,
526                        DWORD    dwSize)
527 {
528   NTSTATUS Status;
529   
530   Status = NtFlushInstructionCache(hProcess,
531                                    (PVOID)lpBaseAddress,
532                                    dwSize);
533   if (!NT_SUCCESS(Status))
534     {
535       SetLastErrorByStatus(Status);
536       return FALSE;
537     }
538   return TRUE;
539 }
540
541
542 VOID STDCALL
543 ExitProcess(UINT uExitCode)
544 {
545   CSRSS_API_REQUEST CsrRequest;
546   CSRSS_API_REPLY CsrReply;
547   NTSTATUS Status;
548   
549   /* unload all dll's */
550   LdrShutdownProcess ();
551
552   /* notify csrss of process termination */
553   CsrRequest.Type = CSRSS_TERMINATE_PROCESS;
554   Status = CsrClientCallServer(&CsrRequest, 
555                                &CsrReply,
556                                sizeof(CSRSS_API_REQUEST),
557                                sizeof(CSRSS_API_REPLY));
558   if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrReply.Status))
559     {
560       DbgPrint("Failed to tell csrss about terminating process. "
561                "Expect trouble.\n");
562     }
563   
564   
565   NtTerminateProcess (NtCurrentProcess (),
566                       uExitCode);
567
568   /* should never get here */
569   assert(0);
570   while(1);
571 }
572
573
574 WINBOOL STDCALL
575 TerminateProcess (HANDLE        hProcess,
576                   UINT  uExitCode)
577 {
578   NTSTATUS Status;
579
580   Status = NtTerminateProcess (hProcess, uExitCode);
581   if (NT_SUCCESS(Status))
582     {
583       return TRUE;
584     }
585   SetLastErrorByStatus (Status);
586   return FALSE;
587 }
588
589
590 VOID STDCALL
591 FatalAppExitA (UINT     uAction,
592                LPCSTR   lpMessageText)
593 {
594   UNICODE_STRING MessageTextU;
595   ANSI_STRING MessageText;
596   
597   RtlInitAnsiString (&MessageText, (LPSTR) lpMessageText);
598
599   RtlAnsiStringToUnicodeString (&MessageTextU,
600                                 &MessageText,
601                                 TRUE);
602
603   FatalAppExitW (uAction, MessageTextU.Buffer);
604
605   RtlFreeUnicodeString (&MessageTextU);
606 }
607
608
609 VOID STDCALL
610 FatalAppExitW(UINT uAction,
611               LPCWSTR lpMessageText)
612 {
613   return;
614 }
615
616
617 VOID STDCALL
618 FatalExit (int ExitCode)
619 {
620   ExitProcess(ExitCode);
621 }
622
623
624 DWORD STDCALL
625 GetPriorityClass (HANDLE        hProcess)
626 {
627   HANDLE                hProcessTmp;
628   DWORD         CsrPriorityClass = 0; // This tells CSRSS we want to GET it!
629   NTSTATUS      Status;
630         
631   Status = 
632     NtDuplicateObject (GetCurrentProcess(),
633                        hProcess,
634                        GetCurrentProcess(),
635                        &hProcessTmp,
636                        (PROCESS_SET_INFORMATION | PROCESS_QUERY_INFORMATION),
637                        FALSE,
638                        0);
639   if (!NT_SUCCESS(Status))
640     {
641       SetLastErrorByStatus (Status);
642       return (0); /* ERROR */
643     }
644   /* Ask CSRSS to set it */
645   CsrSetPriorityClass (hProcessTmp, &CsrPriorityClass);
646   NtClose (hProcessTmp);
647   /* Translate CSR->W32 priorities */
648   switch (CsrPriorityClass)
649     {
650     case CSR_PRIORITY_CLASS_NORMAL:
651       return (NORMAL_PRIORITY_CLASS);   /* 32 */
652     case CSR_PRIORITY_CLASS_IDLE:
653       return (IDLE_PRIORITY_CLASS);     /* 64 */
654     case CSR_PRIORITY_CLASS_HIGH:
655       return (HIGH_PRIORITY_CLASS);     /* 128 */
656     case CSR_PRIORITY_CLASS_REALTIME:
657       return (REALTIME_PRIORITY_CLASS); /* 256 */
658     }
659   SetLastError (ERROR_ACCESS_DENIED);
660   return (0); /* ERROR */
661 }
662
663
664
665 WINBOOL STDCALL
666 SetPriorityClass (HANDLE        hProcess,
667                   DWORD dwPriorityClass)
668 {
669   HANDLE                hProcessTmp;
670   DWORD         CsrPriorityClass;
671   NTSTATUS      Status;
672   
673   switch (dwPriorityClass)
674     {
675     case NORMAL_PRIORITY_CLASS: /* 32 */
676       CsrPriorityClass = CSR_PRIORITY_CLASS_NORMAL;
677       break;
678     case IDLE_PRIORITY_CLASS:   /* 64 */
679       CsrPriorityClass = CSR_PRIORITY_CLASS_IDLE;
680       break;
681     case HIGH_PRIORITY_CLASS:   /* 128 */
682       CsrPriorityClass = CSR_PRIORITY_CLASS_HIGH;
683       break;
684     case REALTIME_PRIORITY_CLASS:       /* 256 */
685       CsrPriorityClass = CSR_PRIORITY_CLASS_REALTIME;
686       break;
687     default:
688       SetLastError (ERROR_INVALID_PARAMETER);
689       return (FALSE);
690     }
691   Status = 
692     NtDuplicateObject (GetCurrentProcess(),
693                        hProcess,
694                        GetCurrentProcess(),
695                        &hProcessTmp,
696                        (PROCESS_SET_INFORMATION | PROCESS_QUERY_INFORMATION),
697                        FALSE,
698                        0);
699   if (!NT_SUCCESS(Status))
700     {
701       SetLastErrorByStatus (Status);
702       return (FALSE); /* ERROR */
703     }
704   /* Ask CSRSS to set it */
705   Status = CsrSetPriorityClass (hProcessTmp, &CsrPriorityClass);
706   NtClose (hProcessTmp);
707   if (!NT_SUCCESS(Status))
708     {
709       SetLastErrorByStatus (Status);
710       return (FALSE);
711     }
712   return (TRUE);
713 }
714
715
716 DWORD STDCALL
717 GetProcessVersion (DWORD ProcessId)
718 {
719   DWORD                 Version = 0;
720   PIMAGE_NT_HEADERS     NtHeader = NULL;
721   PVOID                 BaseAddress = NULL;
722
723   /* Caller's */
724   if (0 == ProcessId || GetCurrentProcessId() == ProcessId)
725     {
726       BaseAddress = (PVOID) NtCurrentPeb()->ImageBaseAddress;
727       NtHeader = RtlImageNtHeader (BaseAddress);
728       if (NULL != NtHeader)
729         {
730           Version =
731             (NtHeader->OptionalHeader.MajorOperatingSystemVersion << 16) | 
732             (NtHeader->OptionalHeader.MinorOperatingSystemVersion);
733         }
734     }
735   else /* other process */
736     {
737       /* FIXME: open the other process */
738       SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
739     }
740   return (Version);
741 }
742
743 /* EOF */