- Compile Win32k, GDI, USER32 and CSRSS using W32API.
[reactos.git] / reactos / subsys / win32k / ntuser / desktop.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 * $Id: desktop.c,v 1.10 2004/04/09 20:03:19 navaraf Exp $
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Desktops
24 * FILE: subsys/win32k/ntuser/desktop.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * REVISION HISTORY:
27 * 06-06-2001 CSH Created
28 */
29
30 /* INCLUDES ******************************************************************/
31
32 #define __WIN32K__
33 #include <ddk/ntddmou.h>
34 #include <win32k/win32k.h>
35 #include <csrss/csrss.h>
36 #include <include/winsta.h>
37 #include <include/desktop.h>
38 #include <include/object.h>
39 #include <include/window.h>
40 #include <include/error.h>
41 #include <include/cursoricon.h>
42 #include <include/hotkey.h>
43 #include <include/color.h>
44 #include <include/mouse.h>
45 #include <include/callback.h>
46 #include <include/guicheck.h>
47 #include <include/intgdi.h>
48
49 #define NDEBUG
50 #include <debug.h>
51
52 /* GLOBALS *******************************************************************/
53
54 /* Currently active desktop */
55 PDESKTOP_OBJECT InputDesktop = NULL;
56 HDESK InputDesktopHandle = NULL;
57 HDC ScreenDeviceContext = NULL;
58
59 /* INITALIZATION FUNCTIONS ****************************************************/
60
61 NTSTATUS FASTCALL
62 InitDesktopImpl(VOID)
63 {
64 return STATUS_SUCCESS;
65 }
66
67 NTSTATUS FASTCALL
68 CleanupDesktopImpl(VOID)
69 {
70 return STATUS_SUCCESS;
71 }
72
73 /* PRIVATE FUNCTIONS **********************************************************/
74
75 /*
76 * IntValidateDesktopHandle
77 *
78 * Validates the desktop handle.
79 *
80 * Remarks
81 * If the function succeeds, the handle remains referenced. If the
82 * fucntion fails, last error is set.
83 */
84
85 NTSTATUS FASTCALL
86 IntValidateDesktopHandle(
87 HDESK Desktop,
88 KPROCESSOR_MODE AccessMode,
89 ACCESS_MASK DesiredAccess,
90 PDESKTOP_OBJECT *Object)
91 {
92 NTSTATUS Status;
93
94 Status = ObReferenceObjectByHandle(
95 Desktop,
96 DesiredAccess,
97 ExDesktopObjectType,
98 AccessMode,
99 (PVOID*)Object,
100 NULL);
101
102 if (!NT_SUCCESS(Status))
103 SetLastNtError(Status);
104
105 return Status;
106 }
107
108 PRECT FASTCALL
109 IntGetDesktopWorkArea(PDESKTOP_OBJECT Desktop)
110 {
111 PRECT Ret;
112
113 Ret = &Desktop->WorkArea;
114 if((Ret->right == -1) && ScreenDeviceContext)
115 {
116 PDC dc;
117 SURFOBJ *SurfObj;
118 dc = DC_LockDc(ScreenDeviceContext);
119 SurfObj = (SURFOBJ*)AccessUserObject((ULONG) dc->Surface);
120 if(SurfObj)
121 {
122 Ret->right = SurfObj->sizlBitmap.cx;
123 Ret->bottom = SurfObj->sizlBitmap.cy;
124 }
125 DC_UnlockDc(ScreenDeviceContext);
126 }
127
128 return Ret;
129 }
130
131 PDESKTOP_OBJECT FASTCALL
132 IntGetActiveDesktop(VOID)
133 {
134 return InputDesktop;
135 }
136
137 PUSER_MESSAGE_QUEUE FASTCALL
138 IntGetFocusMessageQueue(VOID)
139 {
140 PDESKTOP_OBJECT pdo = IntGetActiveDesktop();
141 if (!pdo)
142 {
143 DPRINT("No active desktop\n");
144 return(NULL);
145 }
146 return (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue;
147 }
148
149 VOID FASTCALL
150 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue)
151 {
152 PDESKTOP_OBJECT pdo = IntGetActiveDesktop();
153 if (!pdo)
154 {
155 DPRINT("No active desktop\n");
156 return;
157 }
158 pdo->ActiveMessageQueue = NewQueue;
159 }
160
161 HWND FASTCALL IntGetDesktopWindow(VOID)
162 {
163 PDESKTOP_OBJECT pdo = IntGetActiveDesktop();
164 if (!pdo)
165 {
166 DPRINT("No active desktop\n");
167 return NULL;
168 }
169 return pdo->DesktopWindow;
170 }
171
172 /* PUBLIC FUNCTIONS ***********************************************************/
173
174
175 static NTSTATUS FASTCALL
176 NotifyCsrss(PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply)
177 {
178 NTSTATUS Status;
179 UNICODE_STRING PortName;
180 ULONG ConnectInfoLength;
181 static HANDLE WindowsApiPort = NULL;
182
183 RtlInitUnicodeString(&PortName, L"\\Windows\\ApiPort");
184 ConnectInfoLength = 0;
185 Status = ZwConnectPort(&WindowsApiPort,
186 &PortName,
187 NULL,
188 NULL,
189 NULL,
190 NULL,
191 NULL,
192 &ConnectInfoLength);
193 if (! NT_SUCCESS(Status))
194 {
195 return Status;
196 }
197
198 Request->Header.DataSize = sizeof(CSRSS_API_REQUEST) - LPC_MESSAGE_BASE_SIZE;
199 Request->Header.MessageSize = sizeof(CSRSS_API_REQUEST);
200
201 Status = ZwRequestWaitReplyPort(WindowsApiPort,
202 &Request->Header,
203 &Reply->Header);
204 if (! NT_SUCCESS(Status) || ! NT_SUCCESS(Status = Reply->Status))
205 {
206 ZwClose(WindowsApiPort);
207 return Status;
208 }
209
210 // ZwClose(WindowsApiPort);
211
212 return STATUS_SUCCESS;
213 }
214
215 NTSTATUS FASTCALL
216 IntShowDesktop(PDESKTOP_OBJECT Desktop, ULONG Width, ULONG Height)
217 {
218 CSRSS_API_REQUEST Request;
219 CSRSS_API_REPLY Reply;
220
221 Request.Type = CSRSS_SHOW_DESKTOP;
222 Request.Data.ShowDesktopRequest.DesktopWindow = Desktop->DesktopWindow;
223 Request.Data.ShowDesktopRequest.Width = Width;
224 Request.Data.ShowDesktopRequest.Height = Height;
225
226 return NotifyCsrss(&Request, &Reply);
227 }
228
229 NTSTATUS FASTCALL
230 IntHideDesktop(PDESKTOP_OBJECT Desktop)
231 {
232 #if 0
233 CSRSS_API_REQUEST Request;
234 CSRSS_API_REPLY Reply;
235
236 Request.Type = CSRSS_HIDE_DESKTOP;
237 Request.Data.HideDesktopRequest.DesktopWindow = Desktop->DesktopWindow;
238
239 return NotifyCsrss(&Request, &Reply);
240 #else
241 PWINDOW_OBJECT DesktopWindow;
242
243 DesktopWindow = IntGetWindowObject(Desktop->DesktopWindow);
244 if (! DesktopWindow)
245 {
246 return ERROR_INVALID_WINDOW_HANDLE;
247 }
248 DesktopWindow->Style &= ~WS_VISIBLE;
249
250 return STATUS_SUCCESS;
251 #endif
252 }
253
254 /*
255 * NtUserCreateDesktop
256 *
257 * Creates a new desktop.
258 *
259 * Parameters
260 * lpszDesktopName
261 * Name of the new desktop.
262 *
263 * dwFlags
264 * Interaction flags.
265 *
266 * dwDesiredAccess
267 * Requested type of access.
268 *
269 * lpSecurity
270 * Security descriptor.
271 *
272 * hWindowStation
273 * Handle to window station on which to create the desktop.
274 *
275 * Return Value
276 * If the function succeeds, the return value is a handle to the newly
277 * created desktop. If the specified desktop already exists, the function
278 * succeeds and returns a handle to the existing desktop. When you are
279 * finished using the handle, call the CloseDesktop function to close it.
280 * If the function fails, the return value is NULL.
281 *
282 * Status
283 * @implemented
284 */
285
286 HDESK STDCALL
287 NtUserCreateDesktop(
288 PUNICODE_STRING lpszDesktopName,
289 DWORD dwFlags,
290 ACCESS_MASK dwDesiredAccess,
291 LPSECURITY_ATTRIBUTES lpSecurity,
292 HWINSTA hWindowStation)
293 {
294 OBJECT_ATTRIBUTES ObjectAttributes;
295 PWINSTATION_OBJECT WinStaObject;
296 PDESKTOP_OBJECT DesktopObject;
297 UNICODE_STRING DesktopName;
298 NTSTATUS Status;
299 HDESK Desktop;
300 CSRSS_API_REQUEST Request;
301 CSRSS_API_REPLY Reply;
302
303 Status = IntValidateWindowStationHandle(
304 hWindowStation,
305 KernelMode,
306 0,
307 &WinStaObject);
308
309 if (! NT_SUCCESS(Status))
310 {
311 DPRINT1("Failed validation of window station handle (0x%X)\n",
312 hWindowStation);
313 SetLastNtError(Status);
314 return NULL;
315 }
316
317 if (! IntGetFullWindowStationName(&DesktopName, &WinStaObject->Name,
318 lpszDesktopName))
319 {
320 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
321 ObDereferenceObject(WinStaObject);
322 return NULL;
323 }
324
325 ObDereferenceObject(WinStaObject);
326
327 /*
328 * Try to open already existing desktop
329 */
330
331 DPRINT("Trying to open desktop (%wZ)\n", &DesktopName);
332
333 /* Initialize ObjectAttributes for the desktop object */
334 InitializeObjectAttributes(
335 &ObjectAttributes,
336 &DesktopName,
337 0,
338 NULL,
339 NULL);
340
341 Status = ObOpenObjectByName(
342 &ObjectAttributes,
343 ExDesktopObjectType,
344 NULL,
345 UserMode,
346 dwDesiredAccess,
347 NULL,
348 (HANDLE*)&Desktop);
349
350 if (NT_SUCCESS(Status))
351 {
352 DPRINT("Successfully opened desktop (%wZ)\n", &DesktopName);
353 ExFreePool(DesktopName.Buffer);
354 return Desktop;
355 }
356
357 /*
358 * No existing desktop found, try to create new one
359 */
360
361 Status = ObCreateObject(
362 ExGetPreviousMode(),
363 ExDesktopObjectType,
364 &ObjectAttributes,
365 ExGetPreviousMode(),
366 NULL,
367 sizeof(DESKTOP_OBJECT),
368 0,
369 0,
370 (PVOID*)&DesktopObject);
371
372 if (! NT_SUCCESS(Status))
373 {
374 DPRINT1("Failed creating desktop (%wZ)\n", &DesktopName);
375 ExFreePool(DesktopName.Buffer);
376 SetLastNtError(STATUS_UNSUCCESSFUL);
377 return NULL;
378 }
379
380 // init desktop area
381 DesktopObject->WorkArea.left = 0;
382 DesktopObject->WorkArea.top = 0;
383 DesktopObject->WorkArea.right = -1;
384 DesktopObject->WorkArea.bottom = -1;
385 IntGetDesktopWorkArea(DesktopObject);
386
387 /* Initialize some local (to win32k) desktop state. */
388 DesktopObject->ActiveMessageQueue = NULL;
389
390 Status = ObInsertObject(
391 (PVOID)DesktopObject,
392 NULL,
393 STANDARD_RIGHTS_REQUIRED,
394 0,
395 NULL,
396 (HANDLE*)&Desktop);
397
398 DesktopObject->Self = (HANDLE)Desktop;
399
400 ObDereferenceObject(DesktopObject);
401 ExFreePool(DesktopName.Buffer);
402
403 if (! NT_SUCCESS(Status))
404 {
405 DPRINT1("Failed to create desktop handle\n");
406 SetLastNtError(Status);
407 return NULL;
408 }
409
410 Request.Type = CSRSS_CREATE_DESKTOP;
411 memcpy(Request.Data.CreateDesktopRequest.DesktopName, lpszDesktopName->Buffer,
412 lpszDesktopName->Length);
413 Request.Data.CreateDesktopRequest.DesktopName[lpszDesktopName->Length / sizeof(WCHAR)] = L'\0';
414
415 Status = NotifyCsrss(&Request, &Reply);
416 if (! NT_SUCCESS(Status))
417 {
418 DPRINT1("Failed to notify CSRSS about new desktop\n");
419 ZwClose(Desktop);
420 SetLastNtError(Status);
421 return NULL;
422 }
423
424 return Desktop;
425 }
426
427 /*
428 * NtUserOpenDesktop
429 *
430 * Opens an existing desktop.
431 *
432 * Parameters
433 * lpszDesktopName
434 * Name of the existing desktop.
435 *
436 * dwFlags
437 * Interaction flags.
438 *
439 * dwDesiredAccess
440 * Requested type of access.
441 *
442 * Return Value
443 * Handle to the desktop or zero on failure.
444 *
445 * Status
446 * @implemented
447 */
448
449 HDESK STDCALL
450 NtUserOpenDesktop(
451 PUNICODE_STRING lpszDesktopName,
452 DWORD dwFlags,
453 ACCESS_MASK dwDesiredAccess)
454 {
455 OBJECT_ATTRIBUTES ObjectAttributes;
456 PWINSTATION_OBJECT WinStaObject;
457 UNICODE_STRING DesktopName;
458 NTSTATUS Status;
459 HDESK Desktop;
460
461 /*
462 * Validate the window station handle and compose the fully
463 * qualified desktop name
464 */
465
466 Status = IntValidateWindowStationHandle(
467 PROCESS_WINDOW_STATION(),
468 KernelMode,
469 0,
470 &WinStaObject);
471
472 if (!NT_SUCCESS(Status))
473 {
474 DPRINT("Failed validation of window station handle (0x%X)\n",
475 PROCESS_WINDOW_STATION());
476 SetLastNtError(Status);
477 return 0;
478 }
479
480 if (!IntGetFullWindowStationName(&DesktopName, &WinStaObject->Name,
481 lpszDesktopName))
482 {
483 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
484 ObDereferenceObject(WinStaObject);
485 return 0;
486 }
487
488 ObDereferenceObject(WinStaObject);
489
490 DPRINT("Trying to open desktop station (%wZ)\n", &DesktopName);
491
492 /* Initialize ObjectAttributes for the desktop object */
493 InitializeObjectAttributes(
494 &ObjectAttributes,
495 &DesktopName,
496 0,
497 NULL,
498 NULL);
499
500 Status = ObOpenObjectByName(
501 &ObjectAttributes,
502 ExDesktopObjectType,
503 NULL,
504 UserMode,
505 dwDesiredAccess,
506 NULL,
507 (HANDLE*)&Desktop);
508
509 if (!NT_SUCCESS(Status))
510 {
511 SetLastNtError(Status);
512 ExFreePool(DesktopName.Buffer);
513 return 0;
514 }
515
516 DPRINT("Successfully opened desktop (%wZ)\n", &DesktopName);
517 ExFreePool(DesktopName.Buffer);
518
519 return Desktop;
520 }
521
522 /*
523 * NtUserOpenInputDesktop
524 *
525 * Opens the input (interactive) desktop.
526 *
527 * Parameters
528 * dwFlags
529 * Interaction flags.
530 *
531 * fInherit
532 * Inheritance option.
533 *
534 * dwDesiredAccess
535 * Requested type of access.
536 *
537 * Return Value
538 * Handle to the input desktop or zero on failure.
539 *
540 * Status
541 * @implemented
542 */
543
544 HDESK STDCALL
545 NtUserOpenInputDesktop(
546 DWORD dwFlags,
547 BOOL fInherit,
548 ACCESS_MASK dwDesiredAccess)
549 {
550 PDESKTOP_OBJECT Object;
551 NTSTATUS Status;
552 HDESK Desktop;
553
554 DPRINT("About to open input desktop\n");
555
556 /* Get a pointer to the desktop object */
557
558 Status = IntValidateDesktopHandle(
559 InputDesktopHandle,
560 UserMode,
561 0,
562 &Object);
563
564 if (!NT_SUCCESS(Status))
565 {
566 DPRINT("Validation of input desktop handle (0x%X) failed\n", InputDesktop);
567 return (HDESK)0;
568 }
569
570 /* Create a new handle to the object */
571
572 Status = ObOpenObjectByPointer(
573 Object,
574 0,
575 NULL,
576 dwDesiredAccess,
577 ExDesktopObjectType,
578 UserMode,
579 (HANDLE*)&Desktop);
580
581 ObDereferenceObject(Object);
582
583 if (NT_SUCCESS(Status))
584 {
585 DPRINT("Successfully opened input desktop\n");
586 return (HDESK)Desktop;
587 }
588
589 SetLastNtError(Status);
590 return (HDESK)0;
591 }
592
593 /*
594 * NtUserCloseDesktop
595 *
596 * Closes a desktop handle.
597 *
598 * Parameters
599 * hDesktop
600 * Handle to the desktop.
601 *
602 * Return Value
603 * Status
604 *
605 * Remarks
606 * The desktop handle can be created with NtUserCreateDesktop or
607 * NtUserOpenDesktop. This function will fail if any thread in the calling
608 * process is using the specified desktop handle or if the handle refers
609 * to the initial desktop of the calling process.
610 *
611 * Status
612 * @implemented
613 */
614
615 BOOL STDCALL
616 NtUserCloseDesktop(HDESK hDesktop)
617 {
618 PDESKTOP_OBJECT Object;
619 NTSTATUS Status;
620
621 DPRINT("About to close desktop handle (0x%X)\n", hDesktop);
622
623 Status = IntValidateDesktopHandle(
624 hDesktop,
625 UserMode,
626 0,
627 &Object);
628
629 if (!NT_SUCCESS(Status))
630 {
631 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
632 return FALSE;
633 }
634
635 ObDereferenceObject(Object);
636
637 DPRINT("Closing desktop handle (0x%X)\n", hDesktop);
638
639 Status = ZwClose(hDesktop);
640 if (!NT_SUCCESS(Status))
641 {
642 SetLastNtError(Status);
643 return FALSE;
644 }
645
646 return TRUE;
647 }
648
649 /*
650 * NtUserPaintDesktop
651 *
652 * The NtUserPaintDesktop function fills the clipping region in the
653 * specified device context with the desktop pattern or wallpaper. The
654 * function is provided primarily for shell desktops.
655 *
656 * Parameters
657 * hdc
658 * Handle to the device context.
659 *
660 * Status
661 * @implemented
662 */
663
664 BOOL STDCALL
665 NtUserPaintDesktop(HDC hDC)
666 {
667 RECT Rect;
668 HBRUSH DesktopBrush, PreviousBrush;
669 HWND hWndDesktop;
670
671 IntGdiGetClipBox(hDC, &Rect);
672
673 hWndDesktop = IntGetDesktopWindow();
674 DesktopBrush = (HBRUSH)NtUserGetClassLong(hWndDesktop, GCL_HBRBACKGROUND, FALSE);
675
676 /*
677 * Paint desktop background
678 */
679
680 PreviousBrush = NtGdiSelectObject(hDC, DesktopBrush);
681 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
682 NtGdiSelectObject(hDC, PreviousBrush);
683
684 return TRUE;
685 }
686
687 /*
688 * NtUserSwitchDesktop
689 *
690 * Sets the current input (interactive) desktop.
691 *
692 * Parameters
693 * hDesktop
694 * Handle to desktop.
695 *
696 * Return Value
697 * Status
698 *
699 * Status
700 * @unimplemented
701 */
702
703 BOOL STDCALL
704 NtUserSwitchDesktop(HDESK hDesktop)
705 {
706 PDESKTOP_OBJECT DesktopObject;
707 NTSTATUS Status;
708
709 DPRINT("About to switch desktop (0x%X)\n", hDesktop);
710
711 Status = IntValidateDesktopHandle(
712 hDesktop,
713 UserMode,
714 0,
715 &DesktopObject);
716
717 if (!NT_SUCCESS(Status))
718 {
719 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
720 return FALSE;
721 }
722
723 /* FIXME: Fail if the desktop belong to an invisible window station */
724 /* FIXME: Fail if the process is associated with a secured
725 desktop such as Winlogon or Screen-Saver */
726 /* FIXME: Connect to input device */
727
728 /* Set the active desktop in the desktop's window station. */
729 DesktopObject->WindowStation->ActiveDesktop = DesktopObject;
730
731 /* Set the global state. */
732 InputDesktop = DesktopObject;
733 InputDesktopHandle = hDesktop;
734 InputWindowStation = DesktopObject->WindowStation;
735
736 ObDereferenceObject(DesktopObject);
737
738 return TRUE;
739 }
740
741 /*
742 * NtUserResolveDesktopForWOW
743 *
744 * Status
745 * @unimplemented
746 */
747
748 DWORD STDCALL
749 NtUserResolveDesktopForWOW(DWORD Unknown0)
750 {
751 UNIMPLEMENTED
752 return 0;
753 }
754
755 /*
756 * NtUserGetThreadDesktop
757 *
758 * Status
759 * @implemented
760 */
761
762 HDESK STDCALL
763 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
764 {
765 PDESKTOP_OBJECT Desktop;
766 NTSTATUS Status;
767 PETHREAD Thread;
768 HDESK Ret;
769
770 if(!dwThreadId)
771 {
772 SetLastWin32Error(ERROR_INVALID_PARAMETER);
773 return 0;
774 }
775
776 Status = PsLookupThreadByThreadId((PVOID)dwThreadId, &Thread);
777 if(!NT_SUCCESS(Status))
778 {
779 SetLastWin32Error(ERROR_INVALID_PARAMETER);
780 return 0;
781 }
782
783 Desktop = Thread->Win32Thread->Desktop;
784
785 if(Desktop)
786 {
787 Status = ObReferenceObjectByPointer(Desktop,
788 0,
789 ExDesktopObjectType,
790 UserMode);
791 if(!NT_SUCCESS(Status))
792 {
793 SetLastNtError(Status);
794 return 0;
795 }
796
797 Ret = (HDESK)Desktop->Self;
798
799 ObDereferenceObject(Desktop);
800 return Ret;
801 }
802
803 return 0;
804 }
805
806 /*
807 * NtUserSetThreadDesktop
808 *
809 * Status
810 * @implemented
811 */
812
813 BOOL STDCALL
814 NtUserSetThreadDesktop(HDESK hDesktop)
815 {
816 PDESKTOP_OBJECT DesktopObject;
817 NTSTATUS Status;
818
819 /* Validate the new desktop. */
820 Status = IntValidateDesktopHandle(
821 hDesktop,
822 UserMode,
823 0,
824 &DesktopObject);
825
826 if (!NT_SUCCESS(Status))
827 {
828 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
829 return FALSE;
830 }
831
832 /* Check for setting the same desktop as before. */
833 if (DesktopObject == PsGetWin32Thread()->Desktop)
834 {
835 ObDereferenceObject(DesktopObject);
836 return TRUE;
837 }
838
839 /* FIXME: Should check here to see if the thread has any windows. */
840
841 if (PsGetWin32Thread()->Desktop != NULL)
842 {
843 ObDereferenceObject(PsGetWin32Thread()->Desktop);
844 }
845
846 PsGetWin32Thread()->Desktop = DesktopObject;
847
848 return TRUE;
849 }
850
851 /* EOF */