[WIN32K]
[reactos.git] / reactos / win32ss / user / ntuser / main.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Driver entry and initialization of win32k
5 * FILE: subsystems/win32/win32k/main/main.c
6 * PROGRAMER:
7 */
8
9 #include <win32k.h>
10 #include <napi.h>
11
12 #define NDEBUG
13 #include <debug.h>
14
15 HANDLE hModuleWin;
16
17 PGDI_HANDLE_TABLE NTAPI GDIOBJ_iAllocHandleTable(OUT PSECTION_OBJECT *SectionObject);
18 BOOL NTAPI GDI_CleanupForProcess (struct _EPROCESS *Process);
19 NTSTATUS NTAPI UserDestroyThreadInfo(struct _ETHREAD *Thread);
20
21 HANDLE GlobalUserHeap = NULL;
22 PSECTION_OBJECT GlobalUserHeapSection = NULL;
23
24 PSERVERINFO gpsi = NULL; // Global User Server Information.
25
26 SHORT gusLanguageID;
27 PPROCESSINFO ppiScrnSaver;
28
29 extern ULONG_PTR Win32kSSDT[];
30 extern UCHAR Win32kSSPT[];
31 extern ULONG Win32kNumberOfSysCalls;
32
33 #if DBG
34 void
35 NTAPI
36 DbgPreServiceHook(ULONG ulSyscallId, PULONG_PTR pulArguments)
37 {
38 GdiDbgPreServiceHook(ulSyscallId, pulArguments);
39 UserDbgPreServiceHook(ulSyscallId, pulArguments);
40 }
41
42 ULONG_PTR
43 NTAPI
44 DbgPostServiceHook(ULONG ulSyscallId, ULONG_PTR ulResult)
45 {
46 ulResult = GdiDbgPostServiceHook(ulSyscallId, ulResult);
47 ulResult = UserDbgPostServiceHook(ulSyscallId, ulResult);
48 return ulResult;
49 }
50 #endif
51
52 NTSTATUS
53 APIENTRY
54 Win32kProcessCallback(struct _EPROCESS *Process,
55 BOOLEAN Create)
56 {
57 PPROCESSINFO ppiCurrent;
58 DECLARE_RETURN(NTSTATUS);
59
60 ASSERT(Process->Peb);
61
62 UserEnterExclusive();
63
64 if (Create)
65 {
66 SIZE_T ViewSize = 0;
67 LARGE_INTEGER Offset;
68 PVOID UserBase = NULL;
69 PRTL_USER_PROCESS_PARAMETERS pParams = Process->Peb->ProcessParameters;
70 NTSTATUS Status;
71
72 ASSERT(PsGetProcessWin32Process(Process) == NULL);
73
74 ppiCurrent = ExAllocatePoolWithTag(NonPagedPool,
75 sizeof(PROCESSINFO),
76 USERTAG_PROCESSINFO);
77
78 if (ppiCurrent == NULL)
79 {
80 ERR_CH(UserProcess, "Failed to allocate ppi for PID:%d\n", Process->UniqueProcessId);
81 RETURN( STATUS_NO_MEMORY);
82 }
83
84 RtlZeroMemory(ppiCurrent, sizeof(PROCESSINFO));
85
86 PsSetProcessWin32Process(Process, ppiCurrent);
87
88 #if DBG
89 DbgInitDebugChannels();
90 #endif
91
92 TRACE_CH(UserProcess,"Allocated ppi 0x%x for PID:%d\n", ppiCurrent, Process->UniqueProcessId);
93
94 /* map the global heap into the process */
95 Offset.QuadPart = 0;
96 Status = MmMapViewOfSection(GlobalUserHeapSection,
97 PsGetCurrentProcess(),
98 &UserBase,
99 0,
100 0,
101 &Offset,
102 &ViewSize,
103 ViewUnmap,
104 SEC_NO_CHANGE,
105 PAGE_EXECUTE_READ); /* would prefer PAGE_READONLY, but thanks to RTL heaps... */
106 if (!NT_SUCCESS(Status))
107 {
108 TRACE_CH(UserProcess,"Failed to map the global heap! 0x%x\n", Status);
109 RETURN(Status);
110 }
111 ppiCurrent->HeapMappings.Next = NULL;
112 ppiCurrent->HeapMappings.KernelMapping = (PVOID)GlobalUserHeap;
113 ppiCurrent->HeapMappings.UserMapping = UserBase;
114 ppiCurrent->HeapMappings.Count = 1;
115
116 InitializeListHead(&ppiCurrent->MenuListHead);
117
118 InitializeListHead(&ppiCurrent->GDIBrushAttrFreeList);
119 InitializeListHead(&ppiCurrent->GDIDcAttrFreeList);
120
121 InitializeListHead(&ppiCurrent->PrivateFontListHead);
122 ExInitializeFastMutex(&ppiCurrent->PrivateFontListLock);
123
124 InitializeListHead(&ppiCurrent->DriverObjListHead);
125 ExInitializeFastMutex(&ppiCurrent->DriverObjListLock);
126
127 ppiCurrent->KeyboardLayout = W32kGetDefaultKeyLayout();
128 EngCreateEvent((PEVENT *)&ppiCurrent->InputIdleEvent);
129 KeInitializeEvent(ppiCurrent->InputIdleEvent, NotificationEvent, FALSE);
130
131
132 /* map the gdi handle table to user land */
133 Process->Peb->GdiSharedHandleTable = GDI_MapHandleTable(Process);
134 Process->Peb->GdiDCAttributeList = GDI_BATCH_LIMIT;
135 pParams = Process->Peb->ProcessParameters;
136
137 ppiCurrent->peProcess = Process;
138 /* setup process flags */
139 ppiCurrent->W32PF_flags = W32PF_THREADCONNECTED;
140
141 if ( pParams &&
142 pParams->WindowFlags & STARTF_SCRNSAVER )
143 {
144 ppiScrnSaver = ppiCurrent;
145 ppiCurrent->W32PF_flags |= W32PF_SCREENSAVER;
146 }
147
148 /* Create pools for GDI object attributes */
149 ppiCurrent->pPoolDcAttr = GdiPoolCreate(sizeof(DC_ATTR), 'acdG');
150 ppiCurrent->pPoolBrushAttr = GdiPoolCreate(sizeof(BRUSH_ATTR), 'arbG');
151 ppiCurrent->pPoolRgnAttr = GdiPoolCreate(sizeof(RGN_ATTR), 'agrG');
152 ASSERT(ppiCurrent->pPoolDcAttr);
153 ASSERT(ppiCurrent->pPoolBrushAttr);
154 ASSERT(ppiCurrent->pPoolRgnAttr);
155 }
156 else
157 {
158 /* Get the Win32 Process */
159 ppiCurrent = PsGetProcessWin32Process(Process);
160
161 ASSERT(ppiCurrent);
162
163 TRACE_CH(UserProcess, "Destroying ppi 0x%x\n", ppiCurrent);
164 ppiCurrent->W32PF_flags |= W32PF_TERMINATED;
165
166 if (ppiScrnSaver == ppiCurrent)
167 ppiScrnSaver = NULL;
168
169 if (ppiCurrent->InputIdleEvent)
170 {
171 EngFreeMem(ppiCurrent->InputIdleEvent);
172 ppiCurrent->InputIdleEvent = NULL;
173 }
174
175 IntCleanupMenus(Process, ppiCurrent);
176 IntCleanupCurIcons(Process, ppiCurrent);
177
178
179 GDI_CleanupForProcess(Process);
180
181 co_IntGraphicsCheck(FALSE);
182
183 /*
184 * Deregister logon application automatically
185 */
186 if(LogonProcess == ppiCurrent)
187 {
188 LogonProcess = NULL;
189 }
190
191 /* Close the startup desktop */
192 if(ppiCurrent->rpdeskStartup)
193 ObDereferenceObject(ppiCurrent->rpdeskStartup);
194 if(ppiCurrent->hdeskStartup)
195 ZwClose(ppiCurrent->hdeskStartup);
196
197 /* Close the current window station */
198 UserSetProcessWindowStation(NULL);
199
200 /* Destroy GDI pools */
201 GdiPoolDestroy(ppiCurrent->pPoolDcAttr);
202 GdiPoolDestroy(ppiCurrent->pPoolBrushAttr);
203 GdiPoolDestroy(ppiCurrent->pPoolRgnAttr);
204
205 if (gppiInputProvider == ppiCurrent) gppiInputProvider = NULL;
206
207 TRACE_CH(UserProcess,"Freeing ppi 0x%x\n", ppiCurrent);
208
209 /* Ftee the PROCESSINFO */
210 PsSetProcessWin32Process(Process, NULL);
211 ExFreePoolWithTag(ppiCurrent, USERTAG_PROCESSINFO);
212 }
213
214 RETURN( STATUS_SUCCESS);
215
216 CLEANUP:
217 UserLeave();
218 END_CLEANUP;
219 }
220
221 NTSTATUS NTAPI
222 UserCreateThreadInfo(struct _ETHREAD *Thread)
223 {
224 struct _EPROCESS *Process;
225 PCLIENTINFO pci;
226 PTHREADINFO ptiCurrent;
227 int i;
228 NTSTATUS Status = STATUS_SUCCESS;
229 PTEB pTeb;
230
231 Process = Thread->ThreadsProcess;
232
233 pTeb = NtCurrentTeb();
234
235 ASSERT(pTeb);
236
237 ptiCurrent = ExAllocatePoolWithTag(NonPagedPool,
238 sizeof(THREADINFO),
239 USERTAG_THREADINFO);
240 if (ptiCurrent == NULL)
241 {
242 ERR_CH(UserThread, "Failed to allocate pti for TID %d\n", Thread->Cid.UniqueThread);
243 return STATUS_NO_MEMORY;
244 }
245
246 RtlZeroMemory(ptiCurrent, sizeof(THREADINFO));
247
248 PsSetThreadWin32Thread(Thread, ptiCurrent);
249 pTeb->Win32ThreadInfo = ptiCurrent;
250 ptiCurrent->pClientInfo = (PCLIENTINFO)pTeb->Win32ClientInfo;
251
252 TRACE_CH(UserThread, "Allocated pti 0x%x for TID %d\n", ptiCurrent, Thread->Cid.UniqueThread);
253
254 /* Initialize the THREADINFO */
255 InitializeListHead(&ptiCurrent->WindowListHead);
256 InitializeListHead(&ptiCurrent->W32CallbackListHead);
257 InitializeListHead(&ptiCurrent->PtiLink);
258 for (i = 0; i < NB_HOOKS; i++)
259 {
260 InitializeListHead(&ptiCurrent->aphkStart[i]);
261 }
262 ptiCurrent->pEThread = Thread;
263 ptiCurrent->ppi = PsGetCurrentProcessWin32Process();
264 ptiCurrent->ptiSibling = ptiCurrent->ppi->ptiList;
265 ptiCurrent->ppi->ptiList = ptiCurrent;
266 ptiCurrent->ppi->cThreads++;
267 ptiCurrent->MessageQueue = MsqCreateMessageQueue(Thread);
268 if(ptiCurrent->MessageQueue == NULL)
269 {
270 ERR_CH(UserThread,"Failed to allocate message loop\n");
271 Status = STATUS_NO_MEMORY;
272 goto error;
273 }
274 ptiCurrent->KeyboardLayout = W32kGetDefaultKeyLayout();
275 if (ptiCurrent->KeyboardLayout)
276 UserReferenceObject(ptiCurrent->KeyboardLayout);
277 ptiCurrent->TIF_flags &= ~TIF_INCLEANUP;
278
279 /* Initialize the CLIENTINFO */
280 pci = (PCLIENTINFO)pTeb->Win32ClientInfo;
281 RtlZeroMemory(pci, sizeof(CLIENTINFO));
282 pci->ppi = ptiCurrent->ppi;
283 pci->fsHooks = ptiCurrent->fsHooks;
284 pci->dwTIFlags = ptiCurrent->TIF_flags;
285 if (ptiCurrent->KeyboardLayout)
286 pci->hKL = ptiCurrent->KeyboardLayout->hkl;
287
288 /* Assign a default window station and desktop to the process */
289 /* Do not try to open a desktop or window station before winlogon initializes */
290 if(ptiCurrent->ppi->hdeskStartup == NULL && LogonProcess != NULL)
291 {
292 HWINSTA hWinSta = NULL;
293 HDESK hDesk = NULL;
294 UNICODE_STRING DesktopPath;
295 PDESKTOP pdesk;
296 PRTL_USER_PROCESS_PARAMETERS ProcessParams;
297
298 /*
299 * inherit the thread desktop and process window station (if not yet inherited) from the process startup
300 * info structure. See documentation of CreateProcess()
301 */
302 ProcessParams = pTeb->ProcessEnvironmentBlock->ProcessParameters;
303
304 Status = STATUS_UNSUCCESSFUL;
305 if(ProcessParams && ProcessParams->DesktopInfo.Length > 0)
306 {
307 Status = IntSafeCopyUnicodeStringTerminateNULL(&DesktopPath, &ProcessParams->DesktopInfo);
308 }
309 if(!NT_SUCCESS(Status))
310 {
311 RtlInitUnicodeString(&DesktopPath, NULL);
312 }
313
314 Status = IntParseDesktopPath(Process,
315 &DesktopPath,
316 &hWinSta,
317 &hDesk);
318
319 if (DesktopPath.Buffer)
320 ExFreePoolWithTag(DesktopPath.Buffer, TAG_STRING);
321
322 if(!NT_SUCCESS(Status))
323 {
324 ERR_CH(UserThread, "Failed to assign default dekstop and winsta to process\n");
325 goto error;
326 }
327
328 if(!UserSetProcessWindowStation(hWinSta))
329 {
330 Status = STATUS_UNSUCCESSFUL;
331 ERR_CH(UserThread,"Failed to set initial process winsta\n");
332 goto error;
333 }
334
335 /* Validate the new desktop. */
336 Status = IntValidateDesktopHandle(hDesk, UserMode, 0, &pdesk);
337 if(!NT_SUCCESS(Status))
338 {
339 ERR_CH(UserThread,"Failed to validate initial desktop handle\n");
340 goto error;
341 }
342
343 /* Store the parsed desktop as the initial desktop */
344 ptiCurrent->ppi->hdeskStartup = hDesk;
345 ptiCurrent->ppi->rpdeskStartup = pdesk;
346 }
347
348 if (ptiCurrent->ppi->hdeskStartup != NULL)
349 {
350 if (!IntSetThreadDesktop(ptiCurrent->ppi->hdeskStartup, FALSE))
351 {
352 ERR_CH(UserThread,"Failed to set thread desktop\n");
353 Status = STATUS_UNSUCCESSFUL;
354 goto error;
355 }
356 }
357
358 /* mark the thread as fully initialized */
359 ptiCurrent->TIF_flags |= TIF_GUITHREADINITIALIZED;
360 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
361
362 return STATUS_SUCCESS;
363
364 error:
365 ERR_CH(UserThread,"UserCreateThreadInfo failed! Freeing pti 0x%x for TID %d\n", ptiCurrent, Thread->Cid.UniqueThread);
366 UserDestroyThreadInfo(Thread);
367 return Status;
368 }
369
370 NTSTATUS
371 NTAPI
372 UserDestroyThreadInfo(struct _ETHREAD *Thread)
373 {
374 PTHREADINFO *ppti;
375 PSINGLE_LIST_ENTRY psle;
376 PPROCESSINFO ppiCurrent;
377 struct _EPROCESS *Process;
378 PTHREADINFO ptiCurrent;
379
380 Process = Thread->ThreadsProcess;
381
382 /* Get the Win32 Thread */
383 ptiCurrent = PsGetThreadWin32Thread(Thread);
384
385 ASSERT(ptiCurrent);
386
387 TRACE_CH(UserThread,"Destroying pti 0x%x\n", ptiCurrent);
388
389 ppiCurrent = ptiCurrent->ppi;
390 ptiCurrent->TIF_flags |= TIF_INCLEANUP;
391 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
392
393
394 /* Decrement thread count and check if its 0 */
395 ppiCurrent->cThreads--;
396
397 if(ptiCurrent->TIF_flags & TIF_GUITHREADINITIALIZED)
398 {
399 /* Do now some process cleanup that requires a valid win32 thread */
400 if(ptiCurrent->ppi->cThreads == 0)
401 {
402 /* Check if we have registered the user api hook */
403 if(ptiCurrent->ppi == ppiUahServer)
404 {
405 /* Unregister the api hook */
406 UserUnregisterUserApiHook();
407 }
408
409 /* Notify logon application to restart shell if needed */
410 if(ptiCurrent->pDeskInfo)
411 {
412 if(ptiCurrent->pDeskInfo->ppiShellProcess == ppiCurrent)
413 {
414 DWORD ExitCode = PsGetProcessExitStatus(Process);
415
416 TRACE_CH(UserProcess, "Shell process is exiting (%d)\n", ExitCode);
417
418 UserPostMessage(hwndSAS,
419 WM_LOGONNOTIFY,
420 LN_SHELL_EXITED,
421 ExitCode);
422
423 ptiCurrent->pDeskInfo->ppiShellProcess = NULL;
424 }
425 }
426 }
427
428 DceFreeThreadDCE(ptiCurrent);
429 HOOK_DestroyThreadHooks(Thread);
430 EVENT_DestroyThreadEvents(Thread);
431 DestroyTimersForThread(ptiCurrent);
432 KeSetEvent(ptiCurrent->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
433 UnregisterThreadHotKeys(Thread);
434 /*
435 if (IsListEmpty(&ptiCurrent->WindowListHead))
436 {
437 ERR_CH(UserThread,"Thread Window List is Empty!\n");
438 }
439 */
440 co_DestroyThreadWindows(Thread);
441
442 if (ppiCurrent && ppiCurrent->ptiList == ptiCurrent && !ptiCurrent->ptiSibling)
443 {
444 //ERR_CH(UserThread,"DestroyProcessClasses\n");
445 /* no process windows should exist at this point, or the function will assert! */
446 DestroyProcessClasses(ppiCurrent);
447 ppiCurrent->W32PF_flags &= ~W32PF_CLASSESREGISTERED;
448 }
449
450 IntBlockInput(ptiCurrent, FALSE);
451 IntCleanupThreadCallbacks(ptiCurrent);
452
453 /* cleanup user object references stack */
454 psle = PopEntryList(&ptiCurrent->ReferencesList);
455 while (psle)
456 {
457 PUSER_REFERENCE_ENTRY ref = CONTAINING_RECORD(psle, USER_REFERENCE_ENTRY, Entry);
458 TRACE_CH(UserThread,"thread clean: remove reference obj 0x%x\n",ref->obj);
459 UserDereferenceObject(ref->obj);
460
461 psle = PopEntryList(&ptiCurrent->ReferencesList);
462 }
463 }
464
465 /* Free the message queue */
466 if(ptiCurrent->MessageQueue)
467 MsqDestroyMessageQueue(ptiCurrent->MessageQueue);
468
469 /* Find the THREADINFO in the PROCESSINFO's list */
470 ppti = &ppiCurrent->ptiList;
471 while (*ppti != NULL && *ppti != ptiCurrent)
472 {
473 ppti = &((*ppti)->ptiSibling);
474 }
475
476 /* we must have found it */
477 ASSERT(*ppti == ptiCurrent);
478
479 /* Remove it from the list */
480 *ppti = ptiCurrent->ptiSibling;
481
482 if (ptiCurrent->KeyboardLayout)
483 UserDereferenceObject(ptiCurrent->KeyboardLayout);
484
485 IntSetThreadDesktop(NULL, TRUE);
486
487 TRACE_CH(UserThread,"Freeing pti 0x%x\n", ptiCurrent);
488
489 /* Free the THREADINFO */
490 PsSetThreadWin32Thread(Thread, NULL);
491 ExFreePoolWithTag(ptiCurrent, USERTAG_THREADINFO);
492
493 return STATUS_SUCCESS;
494 }
495
496 NTSTATUS
497 APIENTRY
498 Win32kThreadCallback(struct _ETHREAD *Thread,
499 PSW32THREADCALLOUTTYPE Type)
500 {
501 NTSTATUS Status;
502
503 UserEnterExclusive();
504
505 ASSERT(NtCurrentTeb());
506
507 if (Type == PsW32ThreadCalloutInitialize)
508 {
509 ASSERT(PsGetThreadWin32Thread(Thread) == NULL);
510 Status = UserCreateThreadInfo(Thread);
511 }
512 else
513 {
514 ASSERT(PsGetThreadWin32Thread(Thread) != NULL);
515 Status = UserDestroyThreadInfo(Thread);
516 }
517
518 UserLeave();
519
520 return Status;
521 }
522
523 #ifdef _M_IX86
524 C_ASSERT(sizeof(SERVERINFO) <= PAGE_SIZE);
525 #endif
526
527 // Return on failure
528 #define NT_ROF(x) \
529 Status = (x); \
530 if (!NT_SUCCESS(Status)) \
531 { \
532 DPRINT1("Failed '%s' (0x%lx)\n", #x, Status); \
533 return Status; \
534 }
535
536 /*
537 * This definition doesn't work
538 */
539 INIT_FUNCTION
540 NTSTATUS
541 APIENTRY
542 DriverEntry(
543 IN PDRIVER_OBJECT DriverObject,
544 IN PUNICODE_STRING RegistryPath)
545 {
546 NTSTATUS Status;
547 BOOLEAN Result;
548 WIN32_CALLOUTS_FPNS CalloutData = {0};
549 PVOID GlobalUserHeapBase = NULL;
550
551 /*
552 * Register user mode call interface
553 * (system service table index = 1)
554 */
555 Result = KeAddSystemServiceTable(Win32kSSDT,
556 NULL,
557 Win32kNumberOfSysCalls,
558 Win32kSSPT,
559 1);
560 if (Result == FALSE)
561 {
562 DPRINT1("Adding system services failed!\n");
563 return STATUS_UNSUCCESSFUL;
564 }
565
566 hModuleWin = MmPageEntireDriver(DriverEntry);
567 DPRINT("Win32k hInstance 0x%x!\n",hModuleWin);
568
569 /* Register Object Manager Callbacks */
570 CalloutData.WindowStationParseProcedure = IntWinStaObjectParse;
571 CalloutData.WindowStationDeleteProcedure = IntWinStaObjectDelete;
572 CalloutData.DesktopDeleteProcedure = IntDesktopObjectDelete;
573 CalloutData.ProcessCallout = Win32kProcessCallback;
574 CalloutData.ThreadCallout = Win32kThreadCallback;
575 CalloutData.BatchFlushRoutine = NtGdiFlushUserBatch;
576 CalloutData.DesktopOkToCloseProcedure = IntDesktopOkToClose;
577 CalloutData.WindowStationOkToCloseProcedure = IntWinstaOkToClose;
578
579 /* Register our per-process and per-thread structures. */
580 PsEstablishWin32Callouts((PWIN32_CALLOUTS_FPNS)&CalloutData);
581
582 /* Register service hook callbacks */
583 #if DBG
584 KdSystemDebugControl('CsoR', DbgPreServiceHook, ID_Win32PreServiceHook, 0, 0, 0, 0);
585 KdSystemDebugControl('CsoR', DbgPostServiceHook, ID_Win32PostServiceHook, 0, 0, 0, 0);
586 #endif
587
588 /* Create the global USER heap */
589 GlobalUserHeap = UserCreateHeap(&GlobalUserHeapSection,
590 &GlobalUserHeapBase,
591 1 * 1024 * 1024); /* FIXME: 1 MB for now... */
592 if (GlobalUserHeap == NULL)
593 {
594 DPRINT1("Failed to initialize the global heap!\n");
595 return STATUS_UNSUCCESSFUL;
596 }
597
598 /* Allocate global server info structure */
599 gpsi = UserHeapAlloc(sizeof(SERVERINFO));
600 if (!gpsi)
601 {
602 DPRINT1("Failed allocate server info structure!\n");
603 return STATUS_UNSUCCESSFUL;
604 }
605
606 RtlZeroMemory(gpsi, sizeof(SERVERINFO));
607 DPRINT("Global Server Data -> %x\n", gpsi);
608
609 NT_ROF(InitGdiHandleTable());
610 NT_ROF(InitPaletteImpl());
611
612 /* Create stock objects, ie. precreated objects commonly
613 used by win32 applications */
614 CreateStockObjects();
615 CreateSysColorObjects();
616
617 NT_ROF(InitBrushImpl());
618 NT_ROF(InitPDEVImpl());
619 NT_ROF(InitLDEVImpl());
620 NT_ROF(InitDeviceImpl());
621 NT_ROF(InitDcImpl());
622 NT_ROF(InitUserImpl());
623 NT_ROF(InitWindowStationImpl());
624 NT_ROF(InitDesktopImpl());
625 NT_ROF(InitInputImpl());
626 NT_ROF(InitKeyboardImpl());
627 NT_ROF(MsqInitializeImpl());
628 NT_ROF(InitTimerImpl());
629 NT_ROF(InitDCEImpl());
630
631 /* Initialize FreeType library */
632 if (!InitFontSupport())
633 {
634 DPRINT1("Unable to initialize font support\n");
635 return Status;
636 }
637
638 gusLanguageID = UserGetLanguageID();
639
640 return STATUS_SUCCESS;
641 }
642
643 /* EOF */