Sync with trunk r63502.
[reactos.git] / win32ss / user / ntuser / misc.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Miscellaneous User functions
5 * FILE: win32ss/user/ntuser/misc.c
6 * PROGRAMER: Ge van Geldorp (ge@gse.nl)
7 */
8
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserMisc);
11
12 /*
13 * Test the Thread to verify and validate it. Hard to the core tests are required.
14 */
15 PTHREADINFO
16 FASTCALL
17 IntTID2PTI(HANDLE id)
18 {
19 NTSTATUS Status;
20 PETHREAD Thread;
21 PTHREADINFO pti;
22 Status = PsLookupThreadByThreadId(id, &Thread);
23 if (!NT_SUCCESS(Status))
24 {
25 return NULL;
26 }
27 if (PsIsThreadTerminating(Thread))
28 {
29 ObDereferenceObject(Thread);
30 return NULL;
31 }
32 pti = PsGetThreadWin32Thread(Thread);
33 if (!pti)
34 {
35 ObDereferenceObject(Thread);
36 return NULL;
37 }
38 // Validate and verify!
39 _SEH2_TRY
40 {
41 if (pti->TIF_flags & TIF_INCLEANUP) pti = NULL;
42 if (pti && !(pti->TIF_flags & TIF_GUITHREADINITIALIZED)) pti = NULL;
43 if (PsGetThreadId(Thread) != id) pti = NULL;
44 }
45 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
46 {
47 pti = NULL;
48 }
49 _SEH2_END
50 ObDereferenceObject(Thread);
51 return pti;
52 }
53
54 SHORT
55 FASTCALL
56 UserGetLanguageID(VOID)
57 {
58 HANDLE KeyHandle;
59 OBJECT_ATTRIBUTES ObAttr;
60 // http://support.microsoft.com/kb/324097
61 ULONG Ret = 0x409; // English
62 PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;
63 ULONG Size = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + MAX_PATH*sizeof(WCHAR);
64 UNICODE_STRING Language;
65
66 RtlInitUnicodeString( &Language,
67 L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Nls\\Language");
68
69 InitializeObjectAttributes( &ObAttr,
70 &Language,
71 OBJ_CASE_INSENSITIVE,
72 NULL,
73 NULL);
74
75 if ( NT_SUCCESS(ZwOpenKey(&KeyHandle, KEY_READ, &ObAttr)))
76 {
77 pKeyInfo = ExAllocatePoolWithTag(PagedPool, Size, TAG_STRING);
78 if ( pKeyInfo )
79 {
80 RtlInitUnicodeString(&Language, L"Default");
81
82 if ( NT_SUCCESS(ZwQueryValueKey( KeyHandle,
83 &Language,
84 KeyValuePartialInformation,
85 pKeyInfo,
86 Size,
87 &Size)) )
88 {
89 RtlInitUnicodeString(&Language, (PWSTR)pKeyInfo->Data);
90 RtlUnicodeStringToInteger(&Language, 16, &Ret);
91 }
92 ExFreePoolWithTag(pKeyInfo, TAG_STRING);
93 }
94 ZwClose(KeyHandle);
95 }
96 TRACE("Language ID = %x\n",Ret);
97 return (SHORT) Ret;
98 }
99
100 HBRUSH
101 FASTCALL
102 GetControlColor(
103 PWND pwndParent,
104 PWND pwnd,
105 HDC hdc,
106 UINT CtlMsg)
107 {
108 HBRUSH hBrush;
109
110 if (!pwndParent) pwndParent = pwnd;
111
112 if ( pwndParent->head.pti->ppi != PsGetCurrentProcessWin32Process())
113 {
114 return (HBRUSH)IntDefWindowProc( pwndParent, CtlMsg, (WPARAM)hdc, (LPARAM)UserHMGetHandle(pwnd), FALSE);
115 }
116
117 hBrush = (HBRUSH)co_IntSendMessage( UserHMGetHandle(pwndParent), CtlMsg, (WPARAM)hdc, (LPARAM)UserHMGetHandle(pwnd));
118
119 if (!hBrush || !GreIsHandleValid(hBrush))
120 {
121 hBrush = (HBRUSH)IntDefWindowProc( pwndParent, CtlMsg, (WPARAM)hdc, (LPARAM)UserHMGetHandle(pwnd), FALSE);
122 }
123 return hBrush;
124 }
125
126 HBRUSH
127 FASTCALL
128 GetControlBrush(
129 PWND pwnd,
130 HDC hdc,
131 UINT ctlType)
132 {
133 PWND pwndParent = IntGetParent(pwnd);
134 return GetControlColor( pwndParent, pwnd, hdc, ctlType);
135 }
136
137 HBRUSH
138 APIENTRY
139 NtUserGetControlBrush(
140 HWND hwnd,
141 HDC hdc,
142 UINT ctlType)
143 {
144 PWND pwnd;
145 HBRUSH hBrush = NULL;
146
147 UserEnterExclusive();
148 if ( (pwnd = UserGetWindowObject(hwnd)) &&
149 ((ctlType - WM_CTLCOLORMSGBOX) < CTLCOLOR_MAX) &&
150 hdc )
151 {
152 hBrush = GetControlBrush(pwnd, hdc, ctlType);
153 }
154 UserLeave();
155 return hBrush;
156 }
157
158 /*
159 * Called from PaintRect, works almost like wine PaintRect16 but returns hBrush.
160 */
161 HBRUSH
162 APIENTRY
163 NtUserGetControlColor(
164 HWND hwndParent,
165 HWND hwnd,
166 HDC hdc,
167 UINT CtlMsg) // Wine PaintRect: WM_CTLCOLORMSGBOX + hbrush
168 {
169 PWND pwnd, pwndParent = NULL;
170 HBRUSH hBrush = NULL;
171
172 UserEnterExclusive();
173 if ( (pwnd = UserGetWindowObject(hwnd)) &&
174 ((CtlMsg - WM_CTLCOLORMSGBOX) < CTLCOLOR_MAX) &&
175 hdc )
176 {
177 if (hwndParent) pwndParent = UserGetWindowObject(hwndParent);
178 hBrush = GetControlColor( pwndParent, pwnd, hdc, CtlMsg);
179 }
180 UserLeave();
181 return hBrush;
182 }
183
184 /*
185 * @unimplemented
186 */
187 DWORD_PTR APIENTRY
188 NtUserGetThreadState(
189 DWORD Routine)
190 {
191 DWORD_PTR ret = 0;
192
193 TRACE("Enter NtUserGetThreadState\n");
194 if (Routine != THREADSTATE_GETTHREADINFO)
195 {
196 UserEnterShared();
197 }
198 else
199 {
200 UserEnterExclusive();
201 }
202
203 switch (Routine)
204 {
205 case THREADSTATE_GETTHREADINFO:
206 GetW32ThreadInfo();
207 break;
208 case THREADSTATE_FOCUSWINDOW:
209 ret = (DWORD_PTR)IntGetThreadFocusWindow();
210 break;
211 case THREADSTATE_CAPTUREWINDOW:
212 /* FIXME: Should use UserEnterShared */
213 ret = (DWORD_PTR)IntGetCapture();
214 break;
215 case THREADSTATE_PROGMANWINDOW:
216 ret = (DWORD_PTR)GetW32ThreadInfo()->pDeskInfo->hProgmanWindow;
217 break;
218 case THREADSTATE_TASKMANWINDOW:
219 ret = (DWORD_PTR)GetW32ThreadInfo()->pDeskInfo->hTaskManWindow;
220 break;
221 case THREADSTATE_ACTIVEWINDOW:
222 ret = (DWORD_PTR)UserGetActiveWindow();
223 break;
224 case THREADSTATE_INSENDMESSAGE:
225 {
226 PUSER_SENT_MESSAGE Message =
227 ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->pusmCurrent;
228 ERR("THREADSTATE_INSENDMESSAGE\n");
229
230 ret = ISMEX_NOSEND;
231 if (Message)
232 {
233 if (Message->ptiSender)
234 ret = ISMEX_SEND;
235 else
236 {
237 if (Message->CompletionCallback)
238 ret = ISMEX_CALLBACK;
239 else
240 ret = ISMEX_NOTIFY;
241 }
242 /* If ReplyMessage */
243 if (Message->QS_Flags & QS_SMRESULT) ret |= ISMEX_REPLIED;
244 }
245
246 break;
247 }
248 case THREADSTATE_GETMESSAGETIME:
249 ret = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->timeLast;
250 break;
251
252 case THREADSTATE_UPTIMELASTREAD:
253 {
254 PTHREADINFO pti;
255 LARGE_INTEGER LargeTickCount;
256 pti = PsGetCurrentThreadWin32Thread();
257 KeQueryTickCount(&LargeTickCount);
258 pti->timeLast = LargeTickCount.u.LowPart;
259 pti->pcti->tickLastMsgChecked = LargeTickCount.u.LowPart;
260 }
261 break;
262
263 case THREADSTATE_GETINPUTSTATE:
264 ret = LOWORD(IntGetQueueStatus(QS_POSTMESSAGE|QS_TIMER|QS_PAINT|QS_SENDMESSAGE|QS_INPUT)) & (QS_KEY | QS_MOUSEBUTTON);
265 break;
266
267 case THREADSTATE_FOREGROUNDTHREAD:
268 ret = (gpqForeground == GetW32ThreadInfo()->MessageQueue);
269 break;
270 case THREADSTATE_GETCURSOR:
271 ret = (DWORD_PTR) (GetW32ThreadInfo()->MessageQueue->CursorObject ?
272 UserHMGetHandle(GetW32ThreadInfo()->MessageQueue->CursorObject) : 0);
273 break;
274 }
275
276 TRACE("Leave NtUserGetThreadState, ret=%lu\n", ret);
277 UserLeave();
278
279 return ret;
280 }
281
282 DWORD
283 APIENTRY
284 NtUserSetThreadState(
285 DWORD Set,
286 DWORD Flags)
287 {
288 PTHREADINFO pti;
289 DWORD Ret = 0;
290 // Test the only flags user can change.
291 if (Set & ~(QF_FF10STATUS|QF_DIALOGACTIVE|QF_TABSWITCHING|QF_FMENUSTATUS|QF_FMENUSTATUSBREAK)) return 0;
292 if (Flags & ~(QF_FF10STATUS|QF_DIALOGACTIVE|QF_TABSWITCHING|QF_FMENUSTATUS|QF_FMENUSTATUSBREAK)) return 0;
293 UserEnterExclusive();
294 pti = PsGetCurrentThreadWin32Thread();
295 if (pti->MessageQueue)
296 {
297 Ret = pti->MessageQueue->QF_flags; // Get the queue flags.
298 if (Set)
299 pti->MessageQueue->QF_flags |= (Set&Flags); // Set the queue flags.
300 else
301 {
302 if (Flags) pti->MessageQueue->QF_flags &= ~Flags; // Clr the queue flags.
303 }
304 }
305 UserLeave();
306 return Ret;
307 }
308
309 UINT
310 APIENTRY
311 NtUserGetDoubleClickTime(VOID)
312 {
313 UINT Result;
314
315 TRACE("Enter NtUserGetDoubleClickTime\n");
316 UserEnterShared();
317
318 // FIXME: Check if this works on non-interactive winsta
319 Result = gspv.iDblClickTime;
320
321 TRACE("Leave NtUserGetDoubleClickTime, ret=%u\n", Result);
322 UserLeave();
323 return Result;
324 }
325
326 BOOL
327 APIENTRY
328 NtUserGetGUIThreadInfo(
329 DWORD idThread, /* If NULL use foreground thread */
330 LPGUITHREADINFO lpgui)
331 {
332 NTSTATUS Status;
333 PTHRDCARETINFO CaretInfo;
334 GUITHREADINFO SafeGui;
335 PDESKTOP Desktop;
336 PUSER_MESSAGE_QUEUE MsgQueue;
337 PTHREADINFO W32Thread;
338 PETHREAD Thread = NULL;
339
340 DECLARE_RETURN(BOOLEAN);
341
342 TRACE("Enter NtUserGetGUIThreadInfo\n");
343 UserEnterShared();
344
345 Status = MmCopyFromCaller(&SafeGui, lpgui, sizeof(DWORD));
346 if(!NT_SUCCESS(Status))
347 {
348 SetLastNtError(Status);
349 RETURN( FALSE);
350 }
351
352 if(SafeGui.cbSize != sizeof(GUITHREADINFO))
353 {
354 EngSetLastError(ERROR_INVALID_PARAMETER);
355 RETURN( FALSE);
356 }
357
358 if (idThread)
359 {
360 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)idThread, &Thread);
361 if(!NT_SUCCESS(Status))
362 {
363 EngSetLastError(ERROR_ACCESS_DENIED);
364 RETURN( FALSE);
365 }
366 W32Thread = (PTHREADINFO)Thread->Tcb.Win32Thread;
367 Desktop = W32Thread->rpdesk;
368 }
369 else
370 { /* Get the foreground thread */
371 Thread = PsGetCurrentThread();
372 W32Thread = (PTHREADINFO)Thread->Tcb.Win32Thread;
373 Desktop = W32Thread->rpdesk;
374 }
375
376 if (!Thread || !Desktop )
377 {
378 if(idThread && Thread)
379 ObDereferenceObject(Thread);
380 EngSetLastError(ERROR_ACCESS_DENIED);
381 RETURN( FALSE);
382 }
383
384 if ( W32Thread->MessageQueue )
385 MsgQueue = W32Thread->MessageQueue;
386 else
387 {
388 if ( Desktop ) MsgQueue = Desktop->ActiveMessageQueue;
389 }
390
391 CaretInfo = MsgQueue->CaretInfo;
392
393 SafeGui.flags = (CaretInfo->Visible ? GUI_CARETBLINKING : 0);
394
395 if (MsgQueue->MenuOwner)
396 SafeGui.flags |= GUI_INMENUMODE | MsgQueue->MenuState;
397
398 if (MsgQueue->MoveSize)
399 SafeGui.flags |= GUI_INMOVESIZE;
400
401 /* FIXME: Add flag GUI_16BITTASK */
402
403 SafeGui.hwndActive = MsgQueue->spwndActive ? UserHMGetHandle(MsgQueue->spwndActive) : 0;
404 SafeGui.hwndFocus = MsgQueue->spwndFocus ? UserHMGetHandle(MsgQueue->spwndFocus) : 0;
405 SafeGui.hwndCapture = MsgQueue->spwndCapture ? UserHMGetHandle(MsgQueue->spwndCapture) : 0;
406 SafeGui.hwndMenuOwner = MsgQueue->MenuOwner;
407 SafeGui.hwndMoveSize = MsgQueue->MoveSize;
408 SafeGui.hwndCaret = CaretInfo->hWnd;
409
410 SafeGui.rcCaret.left = CaretInfo->Pos.x;
411 SafeGui.rcCaret.top = CaretInfo->Pos.y;
412 SafeGui.rcCaret.right = SafeGui.rcCaret.left + CaretInfo->Size.cx;
413 SafeGui.rcCaret.bottom = SafeGui.rcCaret.top + CaretInfo->Size.cy;
414
415 if (idThread)
416 ObDereferenceObject(Thread);
417
418 Status = MmCopyToCaller(lpgui, &SafeGui, sizeof(GUITHREADINFO));
419 if(!NT_SUCCESS(Status))
420 {
421 SetLastNtError(Status);
422 RETURN( FALSE);
423 }
424
425 RETURN( TRUE);
426
427 CLEANUP:
428 TRACE("Leave NtUserGetGUIThreadInfo, ret=%u\n",_ret_);
429 UserLeave();
430 END_CLEANUP;
431 }
432
433
434 DWORD
435 APIENTRY
436 NtUserGetGuiResources(
437 HANDLE hProcess,
438 DWORD uiFlags)
439 {
440 PEPROCESS Process;
441 PPROCESSINFO W32Process;
442 NTSTATUS Status;
443 DWORD Ret = 0;
444 DECLARE_RETURN(DWORD);
445
446 TRACE("Enter NtUserGetGuiResources\n");
447 UserEnterShared();
448
449 Status = ObReferenceObjectByHandle(hProcess,
450 PROCESS_QUERY_INFORMATION,
451 *PsProcessType,
452 ExGetPreviousMode(),
453 (PVOID*)&Process,
454 NULL);
455
456 if(!NT_SUCCESS(Status))
457 {
458 SetLastNtError(Status);
459 RETURN( 0);
460 }
461
462 W32Process = (PPROCESSINFO)Process->Win32Process;
463 if(!W32Process)
464 {
465 ObDereferenceObject(Process);
466 EngSetLastError(ERROR_INVALID_PARAMETER);
467 RETURN( 0);
468 }
469
470 switch(uiFlags)
471 {
472 case GR_GDIOBJECTS:
473 {
474 Ret = (DWORD)W32Process->GDIHandleCount;
475 break;
476 }
477 case GR_USEROBJECTS:
478 {
479 Ret = (DWORD)W32Process->UserHandleCount;
480 break;
481 }
482 default:
483 {
484 EngSetLastError(ERROR_INVALID_PARAMETER);
485 break;
486 }
487 }
488
489 ObDereferenceObject(Process);
490
491 RETURN( Ret);
492
493 CLEANUP:
494 TRACE("Leave NtUserGetGuiResources, ret=%lu\n",_ret_);
495 UserLeave();
496 END_CLEANUP;
497 }
498
499 VOID FASTCALL
500 IntSetWindowState(PWND pWnd, UINT Flag)
501 {
502 UINT bit;
503 if (gptiCurrent->ppi != pWnd->head.pti->ppi) return;
504 bit = 1 << LOWORD(Flag);
505 TRACE("SWS %x\n",bit);
506 switch(HIWORD(Flag))
507 {
508 case 0:
509 pWnd->state |= bit;
510 break;
511 case 1:
512 pWnd->state2 |= bit;
513 break;
514 case 2:
515 pWnd->ExStyle2 |= bit;
516 break;
517 }
518 }
519
520 VOID FASTCALL
521 IntClearWindowState(PWND pWnd, UINT Flag)
522 {
523 UINT bit;
524 if (gptiCurrent->ppi != pWnd->head.pti->ppi) return;
525 bit = 1 << LOWORD(Flag);
526 TRACE("CWS %x\n",bit);
527 switch(HIWORD(Flag))
528 {
529 case 0:
530 pWnd->state &= ~bit;
531 break;
532 case 1:
533 pWnd->state2 &= ~bit;
534 break;
535 case 2:
536 pWnd->ExStyle2 &= ~bit;
537 break;
538 }
539 }
540
541 NTSTATUS FASTCALL
542 IntSafeCopyUnicodeString(PUNICODE_STRING Dest,
543 PUNICODE_STRING Source)
544 {
545 NTSTATUS Status;
546 PWSTR Src;
547
548 Status = MmCopyFromCaller(Dest, Source, sizeof(UNICODE_STRING));
549 if(!NT_SUCCESS(Status))
550 {
551 return Status;
552 }
553
554 if(Dest->Length > 0x4000)
555 {
556 return STATUS_UNSUCCESSFUL;
557 }
558
559 Src = Dest->Buffer;
560 Dest->Buffer = NULL;
561 Dest->MaximumLength = Dest->Length;
562
563 if(Dest->Length > 0 && Src)
564 {
565 Dest->Buffer = ExAllocatePoolWithTag(PagedPool, Dest->MaximumLength, TAG_STRING);
566 if(!Dest->Buffer)
567 {
568 return STATUS_NO_MEMORY;
569 }
570
571 Status = MmCopyFromCaller(Dest->Buffer, Src, Dest->Length);
572 if(!NT_SUCCESS(Status))
573 {
574 ExFreePoolWithTag(Dest->Buffer, TAG_STRING);
575 Dest->Buffer = NULL;
576 return Status;
577 }
578
579
580 return STATUS_SUCCESS;
581 }
582
583 /* String is empty */
584 return STATUS_SUCCESS;
585 }
586
587 NTSTATUS FASTCALL
588 IntSafeCopyUnicodeStringTerminateNULL(PUNICODE_STRING Dest,
589 PUNICODE_STRING Source)
590 {
591 NTSTATUS Status;
592 PWSTR Src;
593
594 Status = MmCopyFromCaller(Dest, Source, sizeof(UNICODE_STRING));
595 if(!NT_SUCCESS(Status))
596 {
597 return Status;
598 }
599
600 if(Dest->Length > 0x4000)
601 {
602 return STATUS_UNSUCCESSFUL;
603 }
604
605 Src = Dest->Buffer;
606 Dest->Buffer = NULL;
607 Dest->MaximumLength = 0;
608
609 if(Dest->Length > 0 && Src)
610 {
611 Dest->MaximumLength = Dest->Length + sizeof(WCHAR);
612 Dest->Buffer = ExAllocatePoolWithTag(PagedPool, Dest->MaximumLength, TAG_STRING);
613 if(!Dest->Buffer)
614 {
615 return STATUS_NO_MEMORY;
616 }
617
618 Status = MmCopyFromCaller(Dest->Buffer, Src, Dest->Length);
619 if(!NT_SUCCESS(Status))
620 {
621 ExFreePoolWithTag(Dest->Buffer, TAG_STRING);
622 Dest->Buffer = NULL;
623 return Status;
624 }
625
626 /* Make sure the string is null-terminated */
627 Src = (PWSTR)((PBYTE)Dest->Buffer + Dest->Length);
628 *Src = L'\0';
629
630 return STATUS_SUCCESS;
631 }
632
633 /* String is empty */
634 return STATUS_SUCCESS;
635 }
636
637 void UserDbgAssertThreadInfo(BOOL showCaller)
638 {
639 PTEB Teb;
640 PPROCESSINFO ppi;
641 PCLIENTINFO pci;
642 PTHREADINFO pti;
643
644 ppi = PsGetCurrentProcessWin32Process();
645 pti = PsGetCurrentThreadWin32Thread();
646 Teb = NtCurrentTeb();
647 pci = GetWin32ClientInfo();
648
649 ASSERT(Teb);
650 ASSERT(pti);
651 ASSERT(pti->ppi == ppi);
652 ASSERT(pti->pClientInfo == pci);
653 ASSERT(Teb->Win32ThreadInfo == pti);
654 ASSERT(pci->ppi == ppi);
655 ASSERT(pci->fsHooks == pti->fsHooks);
656 ASSERT(pci->ulClientDelta == DesktopHeapGetUserDelta());
657 if (pti->pcti && pci->pDeskInfo)
658 ASSERT(pci->pClientThreadInfo == (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta));
659 if (pti->KeyboardLayout)
660 ASSERT(pci->hKL == pti->KeyboardLayout->hkl);
661 if(pti->rpdesk != NULL)
662 ASSERT(pti->pDeskInfo == pti->rpdesk->pDeskInfo);
663
664 /*too bad we still get this assertion*/
665
666 // Why? Not all flags are passed to the user and doing so could crash the system........
667
668 /* ASSERT(pci->dwTIFlags == pti->TIF_flags); */
669 /* if(pci->dwTIFlags != pti->TIF_flags)
670 {
671 ERR("pci->dwTIFlags(0x%x) doesn't match pti->TIF_flags(0x%x)\n", pci->dwTIFlags, pti->TIF_flags);
672 if(showCaller)
673 {
674 DbgPrint("Caller:\n");
675 KeRosDumpStackFrames(NULL, 10);
676 }
677 pci->dwTIFlags = pti->TIF_flags;
678 }
679 */
680 }
681
682 void
683 NTAPI
684 UserDbgPreServiceHook(ULONG ulSyscallId, PULONG_PTR pulArguments)
685 {
686 UserDbgAssertThreadInfo(FALSE);
687 }
688
689 ULONG_PTR
690 NTAPI
691 UserDbgPostServiceHook(ULONG ulSyscallId, ULONG_PTR ulResult)
692 {
693 /* Make sure that the first syscall is NtUserInitialize */
694 /* too bad this fails */
695 //ASSERT(gbInitialized);
696
697 UserDbgAssertThreadInfo(TRUE);
698
699 return ulResult;
700 }
701
702
703 PPROCESSINFO
704 GetW32ProcessInfo(VOID)
705 {
706 return (PPROCESSINFO)PsGetCurrentProcessWin32Process();
707 }
708
709 PTHREADINFO
710 GetW32ThreadInfo(VOID)
711 {
712 UserDbgAssertThreadInfo(TRUE);
713 return (PTHREADINFO)PsGetCurrentThreadWin32Thread();
714 }
715
716 /* EOF */