kernel32: Add a stub for SetThreadPreferredUILanguages.
[wine] / dlls / kernel32 / thread.c
1 /*
2  * Win32 threads
3  *
4  * Copyright 1996 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <fcntl.h>
26 #include <stdarg.h>
27 #include <sys/types.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winerror.h"
37 #include "winternl.h"
38 #include "wine/exception.h"
39 #include "wine/library.h"
40 #include "wine/server.h"
41 #include "wine/debug.h"
42
43 #include "kernel_private.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(thread);
46
47
48 /***********************************************************************
49  *           CreateThread   (KERNEL32.@)
50  */
51 HANDLE WINAPI CreateThread( SECURITY_ATTRIBUTES *sa, SIZE_T stack,
52                             LPTHREAD_START_ROUTINE start, LPVOID param,
53                             DWORD flags, LPDWORD id )
54 {
55      return CreateRemoteThread( GetCurrentProcess(),
56                                 sa, stack, start, param, flags, id );
57 }
58
59
60 /***************************************************************************
61  *                  CreateRemoteThread   (KERNEL32.@)
62  *
63  * Creates a thread that runs in the address space of another process
64  *
65  * PARAMS
66  *
67  * RETURNS
68  *   Success: Handle to the new thread.
69  *   Failure: NULL. Use GetLastError() to find the error cause.
70  *
71  * BUGS
72  *   Improper memory allocation: there's no ability to free new_thread_info
73  *   in other process.
74  *   Bad start address for RtlCreateUserThread because the library
75  *   may be loaded at different address in other process.
76  */
77 HANDLE WINAPI CreateRemoteThread( HANDLE hProcess, SECURITY_ATTRIBUTES *sa, SIZE_T stack,
78                                   LPTHREAD_START_ROUTINE start, LPVOID param,
79                                   DWORD flags, LPDWORD id )
80 {
81     HANDLE handle;
82     CLIENT_ID client_id;
83     NTSTATUS status;
84     SIZE_T stack_reserve = 0, stack_commit = 0;
85
86     if (flags & STACK_SIZE_PARAM_IS_A_RESERVATION) stack_reserve = stack;
87     else stack_commit = stack;
88
89     status = RtlCreateUserThread( hProcess, NULL, TRUE,
90                                   NULL, stack_reserve, stack_commit,
91                                   (PRTL_THREAD_START_ROUTINE)start, param, &handle, &client_id );
92     if (status == STATUS_SUCCESS)
93     {
94         if (id) *id = HandleToULong(client_id.UniqueThread);
95         if (sa && (sa->nLength >= sizeof(*sa)) && sa->bInheritHandle)
96             SetHandleInformation( handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT );
97         if (!(flags & CREATE_SUSPENDED))
98         {
99             ULONG ret;
100             if (NtResumeThread( handle, &ret ))
101             {
102                 NtClose( handle );
103                 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
104                 handle = 0;
105             }
106         }
107     }
108     else
109     {
110         SetLastError( RtlNtStatusToDosError(status) );
111         handle = 0;
112     }
113     return handle;
114 }
115
116
117 /***********************************************************************
118  * OpenThread  [KERNEL32.@]   Retrieves a handle to a thread from its thread id
119  */
120 HANDLE WINAPI OpenThread( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId )
121 {
122     NTSTATUS status;
123     HANDLE handle;
124     OBJECT_ATTRIBUTES attr;
125     CLIENT_ID cid;
126
127     attr.Length = sizeof(attr);
128     attr.RootDirectory = 0;
129     attr.Attributes = bInheritHandle ? OBJ_INHERIT : 0;
130     attr.ObjectName = NULL;
131     attr.SecurityDescriptor = NULL;
132     attr.SecurityQualityOfService = NULL;
133
134     cid.UniqueProcess = 0; /* FIXME */
135     cid.UniqueThread = ULongToHandle(dwThreadId);
136     status = NtOpenThread( &handle, dwDesiredAccess, &attr, &cid );
137     if (status)
138     {
139         SetLastError( RtlNtStatusToDosError(status) );
140         handle = 0;
141     }
142     return handle;
143 }
144
145
146 /***********************************************************************
147  * ExitThread [KERNEL32.@]  Ends a thread
148  *
149  * RETURNS
150  *    None
151  */
152 void WINAPI ExitThread( DWORD code ) /* [in] Exit code for this thread */
153 {
154     RtlFreeThreadActivationContextStack();
155     RtlExitUserThread( code );
156 }
157
158
159 /**********************************************************************
160  * TerminateThread [KERNEL32.@]  Terminates a thread
161  *
162  * RETURNS
163  *    Success: TRUE
164  *    Failure: FALSE
165  */
166 BOOL WINAPI TerminateThread( HANDLE handle,    /* [in] Handle to thread */
167                              DWORD exit_code)  /* [in] Exit code for thread */
168 {
169     NTSTATUS status = NtTerminateThread( handle, exit_code );
170     if (status) SetLastError( RtlNtStatusToDosError(status) );
171     return !status;
172 }
173
174
175 /***********************************************************************
176  *           FreeLibraryAndExitThread (KERNEL32.@)
177  */
178 void WINAPI FreeLibraryAndExitThread(HINSTANCE hLibModule, DWORD dwExitCode)
179 {
180     FreeLibrary(hLibModule);
181     ExitThread(dwExitCode);
182 }
183
184
185 /**********************************************************************
186  *              GetExitCodeThread (KERNEL32.@)
187  *
188  * Gets termination status of thread.
189  *
190  * RETURNS
191  *    Success: TRUE
192  *    Failure: FALSE
193  */
194 BOOL WINAPI GetExitCodeThread(
195     HANDLE hthread, /* [in]  Handle to thread */
196     LPDWORD exitcode) /* [out] Address to receive termination status */
197 {
198     THREAD_BASIC_INFORMATION info;
199     NTSTATUS status = NtQueryInformationThread( hthread, ThreadBasicInformation,
200                                                 &info, sizeof(info), NULL );
201
202     if (status)
203     {
204         SetLastError( RtlNtStatusToDosError(status) );
205         return FALSE;
206     }
207     if (exitcode) *exitcode = info.ExitStatus;
208     return TRUE;
209 }
210
211
212 /***********************************************************************
213  * SetThreadContext [KERNEL32.@]  Sets context of thread.
214  *
215  * RETURNS
216  *    Success: TRUE
217  *    Failure: FALSE
218  */
219 BOOL WINAPI SetThreadContext( HANDLE handle,           /* [in]  Handle to thread with context */
220                               const CONTEXT *context ) /* [in] Address of context structure */
221 {
222     NTSTATUS status = NtSetContextThread( handle, context );
223     if (status) SetLastError( RtlNtStatusToDosError(status) );
224     return !status;
225 }
226
227
228 /***********************************************************************
229  * GetThreadContext [KERNEL32.@]  Retrieves context of thread.
230  *
231  * RETURNS
232  *    Success: TRUE
233  *    Failure: FALSE
234  */
235 BOOL WINAPI GetThreadContext( HANDLE handle,     /* [in]  Handle to thread with context */
236                               CONTEXT *context ) /* [out] Address of context structure */
237 {
238     NTSTATUS status = NtGetContextThread( handle, context );
239     if (status) SetLastError( RtlNtStatusToDosError(status) );
240     return !status;
241 }
242
243
244 /**********************************************************************
245  * SuspendThread [KERNEL32.@]  Suspends a thread.
246  *
247  * RETURNS
248  *    Success: Previous suspend count
249  *    Failure: 0xFFFFFFFF
250  */
251 DWORD WINAPI SuspendThread( HANDLE hthread ) /* [in] Handle to the thread */
252 {
253     DWORD ret;
254     NTSTATUS status = NtSuspendThread( hthread, &ret );
255
256     if (status)
257     {
258         ret = ~0U;
259         SetLastError( RtlNtStatusToDosError(status) );
260     }
261     return ret;
262 }
263
264
265 /**********************************************************************
266  * ResumeThread [KERNEL32.@]  Resumes a thread.
267  *
268  * Decrements a thread's suspend count.  When count is zero, the
269  * execution of the thread is resumed.
270  *
271  * RETURNS
272  *    Success: Previous suspend count
273  *    Failure: 0xFFFFFFFF
274  *    Already running: 0
275  */
276 DWORD WINAPI ResumeThread( HANDLE hthread ) /* [in] Identifies thread to restart */
277 {
278     DWORD ret;
279     NTSTATUS status = NtResumeThread( hthread, &ret );
280
281     if (status)
282     {
283         ret = ~0U;
284         SetLastError( RtlNtStatusToDosError(status) );
285     }
286     return ret;
287 }
288
289
290 /**********************************************************************
291  * GetThreadPriority [KERNEL32.@]  Returns priority for thread.
292  *
293  * RETURNS
294  *    Success: Thread's priority level.
295  *    Failure: THREAD_PRIORITY_ERROR_RETURN
296  */
297 INT WINAPI GetThreadPriority(
298     HANDLE hthread) /* [in] Handle to thread */
299 {
300     THREAD_BASIC_INFORMATION info;
301     NTSTATUS status = NtQueryInformationThread( hthread, ThreadBasicInformation,
302                                                 &info, sizeof(info), NULL );
303
304     if (status)
305     {
306         SetLastError( RtlNtStatusToDosError(status) );
307         return THREAD_PRIORITY_ERROR_RETURN;
308     }
309     return info.Priority;
310 }
311
312
313 /**********************************************************************
314  * SetThreadPriority [KERNEL32.@]  Sets priority for thread.
315  *
316  * RETURNS
317  *    Success: TRUE
318  *    Failure: FALSE
319  */
320 BOOL WINAPI SetThreadPriority(
321     HANDLE hthread, /* [in] Handle to thread */
322     INT priority)   /* [in] Thread priority level */
323 {
324     DWORD       prio = priority;
325     NTSTATUS    status;
326
327     status = NtSetInformationThread(hthread, ThreadBasePriority,
328                                     &prio, sizeof(prio));
329
330     if (status)
331     {
332         SetLastError( RtlNtStatusToDosError(status) );
333         return FALSE;
334     }
335
336     return TRUE;
337 }
338
339
340 /**********************************************************************
341  * GetThreadPriorityBoost [KERNEL32.@]  Returns priority boost for thread.
342  *
343  * Always reports that priority boost is disabled.
344  *
345  * RETURNS
346  *    Success: TRUE.
347  *    Failure: FALSE
348  */
349 BOOL WINAPI GetThreadPriorityBoost(
350     HANDLE hthread, /* [in] Handle to thread */
351     PBOOL pstate)   /* [out] pointer to var that receives the boost state */
352 {
353     if (pstate) *pstate = FALSE;
354     return TRUE;
355 }
356
357
358 /**********************************************************************
359  * SetThreadPriorityBoost [KERNEL32.@]  Sets priority boost for thread.
360  *
361  * Priority boost is not implemented. This function always returns
362  * FALSE and sets last error to ERROR_CALL_NOT_IMPLEMENTED
363  *
364  * RETURNS
365  *    Always returns FALSE to indicate a failure
366  */
367 BOOL WINAPI SetThreadPriorityBoost(
368     HANDLE hthread, /* [in] Handle to thread */
369     BOOL disable)   /* [in] TRUE to disable priority boost */
370 {
371     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
372     return FALSE;
373 }
374
375
376 /**********************************************************************
377  *           SetThreadAffinityMask   (KERNEL32.@)
378  */
379 DWORD_PTR WINAPI SetThreadAffinityMask( HANDLE hThread, DWORD_PTR dwThreadAffinityMask )
380 {
381     NTSTATUS                    status;
382     THREAD_BASIC_INFORMATION    tbi;
383
384     status = NtQueryInformationThread( hThread, ThreadBasicInformation, 
385                                        &tbi, sizeof(tbi), NULL );
386     if (status)
387     {
388         SetLastError( RtlNtStatusToDosError(status) );
389         return 0;
390     }
391     status = NtSetInformationThread( hThread, ThreadAffinityMask, 
392                                      &dwThreadAffinityMask,
393                                      sizeof(dwThreadAffinityMask));
394     if (status)
395     {
396         SetLastError( RtlNtStatusToDosError(status) );
397         return 0;
398     }
399     return tbi.AffinityMask;
400 }
401
402
403 /**********************************************************************
404  * SetThreadIdealProcessor [KERNEL32.@]  Sets preferred processor for thread.
405  *
406  * RETURNS
407  *    Success: Value of last call to SetThreadIdealProcessor
408  *    Failure: -1
409  */
410 DWORD WINAPI SetThreadIdealProcessor(
411     HANDLE hThread,          /* [in] Specifies the thread of interest */
412     DWORD dwIdealProcessor)  /* [in] Specifies the new preferred processor */
413 {
414     FIXME("(%p): stub\n",hThread);
415     if (dwIdealProcessor > MAXIMUM_PROCESSORS)
416     {
417         SetLastError(ERROR_INVALID_PARAMETER);
418         return ~0u;
419     }
420     return 0;
421 }
422
423
424 /***********************************************************************
425  *           GetThreadSelectorEntry   (KERNEL32.@)
426  */
427 BOOL WINAPI GetThreadSelectorEntry( HANDLE hthread, DWORD sel, LPLDT_ENTRY ldtent )
428 {
429     THREAD_DESCRIPTOR_INFORMATION tdi;
430     NTSTATUS status;
431
432     tdi.Selector = sel;
433     status = NtQueryInformationThread( hthread, ThreadDescriptorTableEntry, &tdi, sizeof(tdi), NULL);
434     if (status)
435     {
436         SetLastError( RtlNtStatusToDosError(status) );
437         return FALSE;
438     }
439     *ldtent = tdi.Entry;
440     return TRUE;
441 }
442
443
444 /* callback for QueueUserAPC */
445 static void CALLBACK call_user_apc( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 )
446 {
447     PAPCFUNC func = (PAPCFUNC)arg1;
448     func( arg2 );
449 }
450
451 /***********************************************************************
452  *              QueueUserAPC  (KERNEL32.@)
453  */
454 DWORD WINAPI QueueUserAPC( PAPCFUNC func, HANDLE hthread, ULONG_PTR data )
455 {
456     NTSTATUS status = NtQueueApcThread( hthread, call_user_apc, (ULONG_PTR)func, data, 0 );
457
458     if (status) SetLastError( RtlNtStatusToDosError(status) );
459     return !status;
460 }
461
462 /***********************************************************************
463  *              QueueUserWorkItem  (KERNEL32.@)
464  */
465 BOOL WINAPI QueueUserWorkItem( LPTHREAD_START_ROUTINE Function, PVOID Context, ULONG Flags )
466 {
467     NTSTATUS status;
468
469     TRACE("(%p,%p,0x%08x)\n", Function, Context, Flags);
470
471     status = RtlQueueWorkItem( Function, Context, Flags );
472
473     if (status) SetLastError( RtlNtStatusToDosError(status) );
474     return !status;
475 }
476
477 /**********************************************************************
478  * GetThreadTimes [KERNEL32.@]  Obtains timing information.
479  *
480  * RETURNS
481  *    Success: TRUE
482  *    Failure: FALSE
483  */
484 BOOL WINAPI GetThreadTimes(
485     HANDLE thread,         /* [in]  Specifies the thread of interest */
486     LPFILETIME creationtime, /* [out] When the thread was created */
487     LPFILETIME exittime,     /* [out] When the thread was destroyed */
488     LPFILETIME kerneltime,   /* [out] Time thread spent in kernel mode */
489     LPFILETIME usertime)     /* [out] Time thread spent in user mode */
490 {
491     KERNEL_USER_TIMES   kusrt;
492     NTSTATUS            status;
493
494     status = NtQueryInformationThread(thread, ThreadTimes, &kusrt,
495                                       sizeof(kusrt), NULL);
496     if (status)
497     {
498         SetLastError( RtlNtStatusToDosError(status) );
499         return FALSE;
500     }
501     if (creationtime)
502     {
503         creationtime->dwLowDateTime = kusrt.CreateTime.u.LowPart;
504         creationtime->dwHighDateTime = kusrt.CreateTime.u.HighPart;
505     }
506     if (exittime)
507     {
508         exittime->dwLowDateTime = kusrt.ExitTime.u.LowPart;
509         exittime->dwHighDateTime = kusrt.ExitTime.u.HighPart;
510     }
511     if (kerneltime)
512     {
513         kerneltime->dwLowDateTime = kusrt.KernelTime.u.LowPart;
514         kerneltime->dwHighDateTime = kusrt.KernelTime.u.HighPart;
515     }
516     if (usertime)
517     {
518         usertime->dwLowDateTime = kusrt.UserTime.u.LowPart;
519         usertime->dwHighDateTime = kusrt.UserTime.u.HighPart;
520     }
521     
522     return TRUE;
523 }
524
525 /**********************************************************************
526  * GetThreadId [KERNEL32.@]
527  *
528  * Retrieve the identifier of a thread.
529  *
530  * PARAMS
531  *  Thread [I] The thread to retrieve the identifier of.
532  *
533  * RETURNS
534  *    Success: Identifier of the target thread.
535  *    Failure: 0
536  */
537 DWORD WINAPI GetThreadId(HANDLE Thread)
538 {
539     THREAD_BASIC_INFORMATION tbi;
540     NTSTATUS status;
541
542     TRACE("(%p)\n", Thread);
543
544     status = NtQueryInformationThread(Thread, ThreadBasicInformation, &tbi,
545                                       sizeof(tbi), NULL);
546     if (status)
547     {
548         SetLastError( RtlNtStatusToDosError(status) );
549         return 0;
550     }
551
552     return HandleToULong(tbi.ClientId.UniqueThread);
553 }
554
555
556 /***********************************************************************
557  * GetCurrentThread [KERNEL32.@]  Gets pseudohandle for current thread
558  *
559  * RETURNS
560  *    Pseudohandle for the current thread
561  */
562 #undef GetCurrentThread
563 HANDLE WINAPI GetCurrentThread(void)
564 {
565     return (HANDLE)~(ULONG_PTR)1;
566 }
567
568
569 #ifdef __i386__
570
571 /***********************************************************************
572  *              SetLastError (KERNEL32.@)
573  */
574 /* void WINAPI SetLastError( DWORD error ); */
575 __ASM_STDCALL_FUNC( SetLastError, 4,
576                    "movl 4(%esp),%eax\n\t"
577                    ".byte 0x64\n\t"
578                    "movl %eax,0x34\n\t"
579                    "ret $4" )
580
581 /***********************************************************************
582  *              GetLastError (KERNEL32.@)
583  */
584 /* DWORD WINAPI GetLastError(void); */
585 __ASM_STDCALL_FUNC( GetLastError, 0, ".byte 0x64\n\tmovl 0x34,%eax\n\tret" )
586
587 /***********************************************************************
588  *              GetCurrentProcessId (KERNEL32.@)
589  */
590 /* DWORD WINAPI GetCurrentProcessId(void) */
591 __ASM_STDCALL_FUNC( GetCurrentProcessId, 0, ".byte 0x64\n\tmovl 0x20,%eax\n\tret" )
592
593 /***********************************************************************
594  *              GetCurrentThreadId (KERNEL32.@)
595  */
596 /* DWORD WINAPI GetCurrentThreadId(void) */
597 __ASM_STDCALL_FUNC( GetCurrentThreadId, 0, ".byte 0x64\n\tmovl 0x24,%eax\n\tret" )
598
599 /***********************************************************************
600  *              GetProcessHeap (KERNEL32.@)
601  */
602 /* HANDLE WINAPI GetProcessHeap(void) */
603 __ASM_STDCALL_FUNC( GetProcessHeap, 0, ".byte 0x64\n\tmovl 0x30,%eax\n\tmovl 0x18(%eax),%eax\n\tret");
604
605 #elif defined(__x86_64__)
606
607 /***********************************************************************
608  *              SetLastError (KERNEL32.@)
609  */
610 /* void WINAPI SetLastError( DWORD error ); */
611 __ASM_STDCALL_FUNC( SetLastError, 8, ".byte 0x65\n\tmovl %ecx,0x68\n\tret" );
612
613 /***********************************************************************
614  *              GetLastError (KERNEL32.@)
615  */
616 /* DWORD WINAPI GetLastError(void); */
617 __ASM_STDCALL_FUNC( GetLastError, 0, ".byte 0x65\n\tmovl 0x68,%eax\n\tret" );
618
619 /***********************************************************************
620  *              GetCurrentProcessId (KERNEL32.@)
621  */
622 /* DWORD WINAPI GetCurrentProcessId(void) */
623 __ASM_STDCALL_FUNC( GetCurrentProcessId, 0, ".byte 0x65\n\tmovl 0x40,%eax\n\tret" );
624
625 /***********************************************************************
626  *              GetCurrentThreadId (KERNEL32.@)
627  */
628 /* DWORD WINAPI GetCurrentThreadId(void) */
629 __ASM_STDCALL_FUNC( GetCurrentThreadId, 0, ".byte 0x65\n\tmovl 0x48,%eax\n\tret" );
630
631 /***********************************************************************
632  *              GetProcessHeap (KERNEL32.@)
633  */
634 /* HANDLE WINAPI GetProcessHeap(void) */
635 __ASM_STDCALL_FUNC( GetProcessHeap, 0, ".byte 0x65\n\tmovq 0x60,%rax\n\tmovq 0x30(%rax),%rax\n\tret");
636
637 #else  /* __x86_64__ */
638
639 /**********************************************************************
640  *              SetLastError (KERNEL32.@)
641  *
642  * Sets the last-error code.
643  *
644  * RETURNS
645  * Nothing.
646  */
647 void WINAPI SetLastError( DWORD error ) /* [in] Per-thread error code */
648 {
649     NtCurrentTeb()->LastErrorValue = error;
650 }
651
652 /**********************************************************************
653  *              GetLastError (KERNEL32.@)
654  *
655  * Get the last-error code.
656  *
657  * RETURNS
658  *  last-error code.
659  */
660 DWORD WINAPI GetLastError(void)
661 {
662     return NtCurrentTeb()->LastErrorValue;
663 }
664
665 /***********************************************************************
666  *              GetCurrentProcessId (KERNEL32.@)
667  *
668  * Get the current process identifier.
669  *
670  * RETURNS
671  *  current process identifier
672  */
673 DWORD WINAPI GetCurrentProcessId(void)
674 {
675     return HandleToULong(NtCurrentTeb()->ClientId.UniqueProcess);
676 }
677
678 /***********************************************************************
679  *              GetCurrentThreadId (KERNEL32.@)
680  *
681  * Get the current thread identifier.
682  *
683  * RETURNS
684  *  current thread identifier
685  */
686 DWORD WINAPI GetCurrentThreadId(void)
687 {
688     return HandleToULong(NtCurrentTeb()->ClientId.UniqueThread);
689 }
690
691 /***********************************************************************
692  *           GetProcessHeap    (KERNEL32.@)
693  */
694 HANDLE WINAPI GetProcessHeap(void)
695 {
696     return NtCurrentTeb()->Peb->ProcessHeap;
697 }
698
699 #endif  /* __i386__ */
700
701 /*************************************************************************
702  *              rtlmode_to_win32mode
703  */
704 static DWORD rtlmode_to_win32mode( DWORD rtlmode )
705 {
706     DWORD win32mode = 0;
707
708     if (rtlmode & 0x10)
709         win32mode |= SEM_FAILCRITICALERRORS;
710     if (rtlmode & 0x20)
711         win32mode |= SEM_NOGPFAULTERRORBOX;
712     if (rtlmode & 0x40)
713         win32mode |= SEM_NOOPENFILEERRORBOX;
714
715     return win32mode;
716 }
717
718 /***********************************************************************
719  *              SetThreadErrorMode (KERNEL32.@)
720  *
721  * Set the thread local error mode.
722  *
723  * PARAMS
724  *  mode    [I] The new error mode, a bitwise or of SEM_FAILCRITICALERRORS,
725  *              SEM_NOGPFAULTERRORBOX and SEM_NOOPENFILEERRORBOX.
726  *  oldmode [O] Destination of the old error mode (may be NULL)
727  *
728  * RETURNS
729  *  Success: TRUE
730  *  Failure: FALSE, check GetLastError
731  */
732 BOOL WINAPI SetThreadErrorMode( DWORD mode, LPDWORD oldmode )
733 {
734     NTSTATUS status;
735     DWORD tmp = 0;
736
737     if (mode & ~(SEM_FAILCRITICALERRORS |
738                  SEM_NOGPFAULTERRORBOX |
739                  SEM_NOOPENFILEERRORBOX))
740     {
741         SetLastError( ERROR_INVALID_PARAMETER );
742         return FALSE;
743     }
744
745     if (mode & SEM_FAILCRITICALERRORS)
746         tmp |= 0x10;
747     if (mode & SEM_NOGPFAULTERRORBOX)
748         tmp |= 0x20;
749     if (mode & SEM_NOOPENFILEERRORBOX)
750         tmp |= 0x40;
751
752     status = RtlSetThreadErrorMode( tmp, oldmode );
753     if (status)
754     {
755         SetLastError( RtlNtStatusToDosError(status) );
756         return FALSE;
757     }
758
759     if (oldmode)
760         *oldmode = rtlmode_to_win32mode(*oldmode);
761
762     return TRUE;
763 }
764
765 /***********************************************************************
766  *              GetThreadErrorMode (KERNEL32.@)
767  *
768  * Get the thread local error mode.
769  *
770  * PARAMS
771  *  None.
772  *
773  * RETURNS
774  *  The current thread local error mode.
775  */
776 DWORD WINAPI GetThreadErrorMode( void )
777 {
778     return rtlmode_to_win32mode( RtlGetThreadErrorMode() );
779 }
780
781 /***********************************************************************
782  *              GetThreadUILanguage (KERNEL32.@)
783  *
784  * Get the current thread's language identifier.
785  *
786  * PARAMS
787  *  None.
788  *
789  * RETURNS
790  *  The current thread's language identifier.
791  */
792 LANGID WINAPI GetThreadUILanguage( void )
793 {
794     LANGID lang;
795     NtQueryDefaultUILanguage( &lang );
796     FIXME(": stub, returning default language.\n");
797     return lang;
798 }
799
800 /***********************************************************************
801  *              GetThreadIOPendingFlag (KERNEL32.@)
802  */
803 BOOL WINAPI GetThreadIOPendingFlag( HANDLE thread, PBOOL io_pending )
804 {
805     FIXME("%p, %p\n", thread, io_pending);
806     *io_pending = FALSE;
807     return TRUE;
808 }
809
810 /***********************************************************************
811  *              SetThreadPreferredUILanguages (KERNEL32.@)
812  */
813 BOOL WINAPI SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, PULONG count )
814 {
815     FIXME( "%u, %p, %p\n", flags, buffer, count );
816     return TRUE;
817 }