xref: /reactos/win32ss/user/user32/misc/dllmain.c (revision d6eebaa4)
1 #include <user32.h>
2 #include <ndk/cmfuncs.h>
3 #include <strsafe.h>
4 
5 #define MAX_USER_MODE_DRV_BUFFER 526
6 
7 //
8 // UMPD Packet Header should match win32ss/include/ntumpd.h
9 //
10 typedef struct _UMPDPKTHEAD
11 {
12     INT       Size;
13     INT       Index;
14     INT       RetSize;
15     DWORD     Reserved;
16     HUMPD     humpd;
17     ULONG_PTR Buffer[];
18 } UMPDPKTHEAD, *PUMPDPKTHEAD;
19 
20 INT WINAPI GdiPrinterThunk(PUMPDPKTHEAD,PVOID,INT);
21 
22 WINE_DEFAULT_DEBUG_CHANNEL(user32);
23 
24 #define KEY_LENGTH 1024
25 
26 static ULONG User32TlsIndex;
27 HINSTANCE User32Instance;
28 
29 PPROCESSINFO g_ppi = NULL;
30 SHAREDINFO gSharedInfo = {0};
31 PSERVERINFO gpsi = NULL;
32 PUSER_HANDLE_TABLE gHandleTable = NULL;
33 PUSER_HANDLE_ENTRY gHandleEntries = NULL;
34 BOOLEAN gfLogonProcess  = FALSE;
35 BOOLEAN gfServerProcess = FALSE;
36 BOOLEAN gfFirstThread   = TRUE;
37 HICON hIconSmWindows = NULL, hIconWindows = NULL;
38 
39 WCHAR szAppInit[KEY_LENGTH];
40 
41 BOOL
42 GetDllList(VOID)
43 {
44     NTSTATUS Status;
45     OBJECT_ATTRIBUTES Attributes;
46     BOOL bRet = FALSE;
47     BOOL bLoad;
48     HANDLE hKey = NULL;
49     DWORD dwSize;
50     PKEY_VALUE_PARTIAL_INFORMATION kvpInfo = NULL;
51 
52     UNICODE_STRING szKeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows");
53     UNICODE_STRING szLoadName = RTL_CONSTANT_STRING(L"LoadAppInit_DLLs");
54     UNICODE_STRING szDllsName = RTL_CONSTANT_STRING(L"AppInit_DLLs");
55 
56     InitializeObjectAttributes(&Attributes, &szKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
57     Status = NtOpenKey(&hKey, KEY_READ, &Attributes);
58     if (NT_SUCCESS(Status))
59     {
60         dwSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD);
61         kvpInfo = HeapAlloc(GetProcessHeap(), 0, dwSize);
62         if (!kvpInfo)
63             goto end;
64 
65         Status = NtQueryValueKey(hKey,
66                                  &szLoadName,
67                                  KeyValuePartialInformation,
68                                  kvpInfo,
69                                  dwSize,
70                                  &dwSize);
71         if (!NT_SUCCESS(Status))
72             goto end;
73 
74         RtlMoveMemory(&bLoad,
75                       kvpInfo->Data,
76                       kvpInfo->DataLength);
77 
78         HeapFree(GetProcessHeap(), 0, kvpInfo);
79         kvpInfo = NULL;
80 
81         if (bLoad)
82         {
83             Status = NtQueryValueKey(hKey,
84                                      &szDllsName,
85                                      KeyValuePartialInformation,
86                                      NULL,
87                                      0,
88                                      &dwSize);
89             if (Status != STATUS_BUFFER_TOO_SMALL)
90                 goto end;
91 
92             kvpInfo = HeapAlloc(GetProcessHeap(), 0, dwSize);
93             if (!kvpInfo)
94                 goto end;
95 
96             Status = NtQueryValueKey(hKey,
97                                      &szDllsName,
98                                      KeyValuePartialInformation,
99                                      kvpInfo,
100                                      dwSize,
101                                      &dwSize);
102             if (NT_SUCCESS(Status))
103             {
104                 LPWSTR lpBuffer = (LPWSTR)kvpInfo->Data;
105                 if (*lpBuffer != UNICODE_NULL)
106                 {
107                     INT bytesToCopy, nullPos;
108 
109                     bytesToCopy = min(kvpInfo->DataLength, KEY_LENGTH * sizeof(WCHAR));
110 
111                     if (bytesToCopy != 0)
112                     {
113                         RtlMoveMemory(szAppInit,
114                                       kvpInfo->Data,
115                                       bytesToCopy);
116 
117                         nullPos = (bytesToCopy / sizeof(WCHAR)) - 1;
118 
119                         /* ensure string is terminated */
120                         szAppInit[nullPos] = UNICODE_NULL;
121 
122                         bRet = TRUE;
123                     }
124                 }
125             }
126         }
127     }
128 
129 end:
130     if (hKey)
131         NtClose(hKey);
132 
133     if (kvpInfo)
134         HeapFree(GetProcessHeap(), 0, kvpInfo);
135 
136     return bRet;
137 }
138 
139 
140 VOID
141 LoadAppInitDlls(VOID)
142 {
143     szAppInit[0] = UNICODE_NULL;
144 
145     if (GetDllList())
146     {
147         WCHAR buffer[KEY_LENGTH];
148         LPWSTR ptr;
149 		size_t i;
150 
151         RtlCopyMemory(buffer, szAppInit, KEY_LENGTH * sizeof(WCHAR) );
152 
153 		for (i = 0; i < KEY_LENGTH; ++ i)
154 		{
155 			if(buffer[i] == L' ' || buffer[i] == L',')
156 				buffer[i] = 0;
157 		}
158 
159 		for (i = 0; i < KEY_LENGTH; )
160 		{
161 			if(buffer[i] == 0)
162 				++ i;
163 			else
164 			{
165 				ptr = buffer + i;
166 				i += wcslen(ptr);
167 				LoadLibraryW(ptr);
168 			}
169 		}
170     }
171 }
172 
173 VOID
174 UnloadAppInitDlls(VOID)
175 {
176     if (szAppInit[0] != UNICODE_NULL)
177     {
178         WCHAR buffer[KEY_LENGTH];
179         HMODULE hModule;
180         LPWSTR ptr;
181 		size_t i;
182 
183         RtlCopyMemory(buffer, szAppInit, KEY_LENGTH * sizeof(WCHAR));
184 
185 		for (i = 0; i < KEY_LENGTH; ++ i)
186 		{
187 			if(buffer[i] == L' ' || buffer[i] == L',')
188 				buffer[i] = 0;
189 		}
190 
191 		for (i = 0; i < KEY_LENGTH; )
192 		{
193 			if(buffer[i] == 0)
194 				++ i;
195 			else
196 			{
197 				ptr = buffer + i;
198 				i += wcslen(ptr);
199 				hModule = GetModuleHandleW(ptr);
200 				FreeLibrary(hModule);
201 			}
202 		}
203     }
204 }
205 
206 #define DEFINE_USER32_CALLBACK(id, value, fn) fn,
207 
208 PVOID apfnDispatch[USER32_CALLBACK_COUNT] =
209 {
210 #include "u32cb.h"
211 };
212 
213 #undef DEFINE_USER32_CALLBACK
214 
215 VOID
216 WINAPI
217 GdiProcessSetup(VOID);
218 
219 BOOL
220 WINAPI
221 ClientThreadSetupHelper(BOOL IsCallback)
222 {
223     /*
224      * Normally we are called by win32k so the win32 thread pointers
225      * should be valid as they are set in win32k::InitThreadCallback.
226      */
227     PCLIENTINFO ClientInfo = GetWin32ClientInfo();
228     BOOLEAN IsFirstThread = _InterlockedExchange8((PCHAR)&gfFirstThread, FALSE);
229 
230     TRACE("In ClientThreadSetup(IsCallback == %s, gfServerProcess = %s, IsFirstThread = %s)\n",
231           IsCallback ? "TRUE" : "FALSE", gfServerProcess ? "TRUE" : "FALSE", IsFirstThread ? "TRUE" : "FALSE");
232 
233     if (IsFirstThread)
234         GdiProcessSetup();
235 
236     /* Check for already initialized thread, and bail out if so */
237     if (ClientInfo->CI_flags & CI_INITTHREAD)
238     {
239         ERR("ClientThreadSetup: Thread already initialized.\n");
240         return FALSE;
241     }
242 
243     /*
244      * CSRSS couldn't use user32::DllMain CSR server-to-server call to connect
245      * to win32k. So it is delayed to a manually-call to ClientThreadSetup.
246      * Also this needs to be done only for the first thread (since the connection
247      * is per-process).
248      */
249     if (gfServerProcess && IsFirstThread)
250     {
251         NTSTATUS Status;
252         USERCONNECT UserCon;
253 
254         RtlZeroMemory(&UserCon, sizeof(UserCon));
255 
256         /* Minimal setup of the connect info structure */
257         UserCon.ulVersion = USER_VERSION;
258         // UserCon.dwDispatchCount;
259 
260         /* Connect to win32k */
261         Status = NtUserProcessConnect(NtCurrentProcess(),
262                                       &UserCon,
263                                       sizeof(UserCon));
264         if (!NT_SUCCESS(Status)) return FALSE;
265 
266         /* Retrieve data */
267         g_ppi = ClientInfo->ppi; // Snapshot PI, used as pointer only!
268         gSharedInfo = UserCon.siClient;
269         gpsi = gSharedInfo.psi;
270         gHandleTable = gSharedInfo.aheList;
271         /* ReactOS-Specific! */ gHandleEntries = SharedPtrToUser(gHandleTable->handles);
272 
273         // ERR("1 SI 0x%x : HT 0x%x : D 0x%x\n",
274         //     gSharedInfo.psi, gSharedInfo.aheList, gSharedInfo.ulSharedDelta);
275     }
276 
277     TRACE("Checkpoint (register PFN)\n");
278     if (!RegisterClientPFN())
279     {
280         ERR("RegisterClientPFN failed\n");
281         return FALSE;
282     }
283 
284     /* Mark this thread as initialized */
285     ClientInfo->CI_flags |= CI_INITTHREAD;
286 
287     /* Initialization that should be done once per process */
288     if (IsFirstThread)
289     {
290         TRACE("Checkpoint (Allocating TLS)\n");
291 
292         /* Allocate an index for user32 thread local data */
293         User32TlsIndex = TlsAlloc();
294         if (User32TlsIndex == TLS_OUT_OF_INDEXES)
295             return FALSE;
296 
297         // HAAAAAAAAAACK!!!!!!
298         // ASSERT(gpsi);
299         if (!gpsi) ERR("AAAAAAAAAAAHHHHHHHHHHHHHH!!!!!!!! gpsi == NULL !!!!\n");
300         if (gpsi)
301         {
302         TRACE("Checkpoint (MessageInit)\n");
303 
304         if (MessageInit())
305         {
306             TRACE("Checkpoint (MenuInit)\n");
307             if (MenuInit())
308             {
309                 TRACE("Checkpoint initialization done OK\n");
310                 InitializeCriticalSection(&U32AccelCacheLock);
311                 LoadAppInitDlls();
312                 return TRUE;
313             }
314             MessageCleanup();
315         }
316 
317         TlsFree(User32TlsIndex);
318         return FALSE;
319         }
320     }
321 
322     return TRUE;
323 }
324 
325 /*
326  * @implemented
327  */
328 BOOL
329 WINAPI
330 ClientThreadSetup(VOID)
331 {
332     //
333     // This routine, in Windows, does a lot of what Init does, but in a radically
334     // different way.
335     //
336     // In Windows, because CSRSS's threads have TIF_CSRSSTHREAD set (we have this
337     // flag in ROS but not sure if we use it), the xxxClientThreadSetup callback
338     // isn't made when CSRSS first loads WINSRV.DLL (which loads USER32.DLL).
339     //
340     // However, all the other calls are made as normal, and WINSRV.DLL loads
341     // USER32.dll, the DllMain runs, and eventually the first NtUser system call is
342     // made which initializes Win32k (and initializes the thread, but as mentioned
343     // above, the thread is marked as TIF_CSRSSTHREAD).
344     //
345     // In the DllMain of User32, there is also a CsrClientConnectToServer call to
346     // server 2 (winsrv). When this is done from CSRSS, the "InServer" flag is set,
347     // so user32 will remember that it's running inside of CSRSS. Also, another
348     // flag, called "FirstThread" is manually set by DllMain.
349     //
350     // Then, WINSRV finishes loading, and CSRSRV starts the API thread/loop. This
351     // code then calls CsrConnectToUser, which calls... ClientThreadStartup. Now
352     // this routine detects that it's in the server process, which means it's CSRSS
353     // and that the callback never happened. It does some first-time-Win32k connection
354     // initialization and caches a bunch of things -- if it's the first thread. It also
355     // acquires a critical section to initialize GDI -- and then resets the first thread
356     // flag.
357     //
358     // For now, we'll do none of this, but to support Windows' CSRSRV.DLL which calls
359     // CsrConnectToUser, we'll pretend we "did something" here. Then the rest will
360     // continue as normal.
361     //
362 
363     // FIXME: Disabling this call is a HACK!! See also User32CallClientThreadSetupFromKernel...
364     // return ClientThreadSetupHelper(FALSE);
365     TRACE("ClientThreadSetup is not implemented\n");
366     return TRUE;
367 }
368 
369 BOOL
370 Init(PUSERCONNECT UserCon /*PUSERSRV_API_CONNECTINFO*/)
371 {
372     NTSTATUS Status = STATUS_SUCCESS;
373 
374     TRACE("user32::Init(0x%p) -->\n", UserCon);
375 
376     RtlInitializeCriticalSection(&gcsUserApiHook);
377 
378     /* Initialize callback table in PEB data */
379     NtCurrentPeb()->KernelCallbackTable = apfnDispatch;
380     NtCurrentPeb()->PostProcessInitRoutine = NULL;
381 
382     // This is a HACK!! //
383     gfServerProcess = FALSE;
384     gfFirstThread   = TRUE;
385     //// End of HACK!! ///
386 
387     /*
388      * Retrieve data from the connect info structure if the initializing
389      * process is not CSRSS. In case it is, this will be done from inside
390      * ClientThreadSetup.
391      */
392     if (!gfServerProcess)
393     {
394         // FIXME: HACK!! We should fixup for the NtUserProcessConnect fixups
395         // because it was made in the context of CSRSS process and not ours!!
396         // So... as long as we don't fix that, we need to redo again a call
397         // to NtUserProcessConnect... How perverse is that?!
398         //
399         // HACK(2): This call is necessary since we disabled
400         // the CSR call in DllMain...
401         {
402             RtlZeroMemory(UserCon, sizeof(*UserCon));
403 
404             /* Minimal setup of the connect info structure */
405             UserCon->ulVersion = USER_VERSION;
406             // UserCon->dwDispatchCount;
407 
408             TRACE("HACK: Hackish NtUserProcessConnect call!!\n");
409             /* Connect to win32k */
410             Status = NtUserProcessConnect(NtCurrentProcess(),
411                                           UserCon,
412                                           sizeof(*UserCon));
413             if (!NT_SUCCESS(Status)) return FALSE;
414         }
415 
416         //
417         // We continue as we should do normally...
418         //
419 
420         /* Retrieve data */
421         g_ppi = GetWin32ClientInfo()->ppi; // Snapshot PI, used as pointer only!
422         gSharedInfo = UserCon->siClient;
423         gpsi = gSharedInfo.psi;
424         gHandleTable = gSharedInfo.aheList;
425         /* ReactOS-Specific! */ gHandleEntries = SharedPtrToUser(gHandleTable->handles);
426     }
427 
428     // FIXME: Yet another hack... This call should normally not be done here, but
429     // instead in ClientThreadSetup, and in User32CallClientThreadSetupFromKernel as well.
430     TRACE("HACK: Using Init-ClientThreadSetupHelper hack!!\n");
431     if (!ClientThreadSetupHelper(FALSE))
432     {
433         TRACE("Init-ClientThreadSetupHelper hack failed!\n");
434         return FALSE;
435     }
436 
437     TRACE("<-- user32::Init()\n");
438 
439     return NT_SUCCESS(Status);
440 }
441 
442 VOID
443 Cleanup(VOID)
444 {
445     UnloadAppInitDlls();
446     DeleteCriticalSection(&U32AccelCacheLock);
447     MenuCleanup();
448     MessageCleanup();
449     TlsFree(User32TlsIndex);
450     DeleteFrameBrushes();
451 }
452 
453 // UserClientDllInitialize
454 BOOL
455 WINAPI
456 DllMain(
457     _In_ HANDLE hDll,
458     _In_ ULONG dwReason,
459     _In_opt_ PVOID pReserved)
460 {
461     switch (dwReason)
462     {
463         case DLL_PROCESS_ATTACH:
464         {
465 
466 #define WIN_OBJ_DIR L"\\Windows"
467 #define SESSION_DIR L"\\Sessions"
468 
469             USERSRV_API_CONNECTINFO ConnectInfo; // USERCONNECT
470 
471 #if 0 // Disabling this code is a BIG HACK!!
472 
473             NTSTATUS Status;
474             ULONG ConnectInfoSize = sizeof(ConnectInfo);
475             WCHAR SessionDir[256];
476 
477             /* Cache the PEB and Session ID */
478             PPEB Peb = NtCurrentPeb();
479             ULONG SessionId = Peb->SessionId; // gSessionId
480 
481             TRACE("user32::DllMain\n");
482 
483             /* Don't bother us for each thread */
484             DisableThreadLibraryCalls(hDll);
485 
486             RtlZeroMemory(&ConnectInfo, sizeof(ConnectInfo));
487 
488             /* Minimal setup of the connect info structure */
489             ConnectInfo.ulVersion = USER_VERSION;
490 
491             /* Setup the Object Directory path */
492             if (!SessionId)
493             {
494                 /* Use the raw path */
495                 wcscpy(SessionDir, WIN_OBJ_DIR);
496             }
497             else
498             {
499                 /* Use the session path */
500                 swprintf(SessionDir,
501                          L"%ws\\%ld%ws",
502                          SESSION_DIR,
503                          SessionId,
504                          WIN_OBJ_DIR);
505             }
506 
507             TRACE("Checkpoint (call CSR)\n");
508 
509             /* Connect to the USER Server */
510             Status = CsrClientConnectToServer(SessionDir,
511                                               USERSRV_SERVERDLL_INDEX,
512                                               &ConnectInfo,
513                                               &ConnectInfoSize,
514                                               &gfServerProcess);
515             if (!NT_SUCCESS(Status))
516             {
517                 ERR("Failed to connect to CSR (Status %lx)\n", Status);
518                 return FALSE;
519             }
520 
521             TRACE("Checkpoint (CSR called)\n");
522 
523 #endif
524 
525             User32Instance = hDll;
526 
527             /* Finish initialization */
528             TRACE("Checkpoint (call Init)\n");
529             if (!Init(&ConnectInfo))
530                 return FALSE;
531 
532             if (!gfServerProcess)
533             {
534                 HINSTANCE hImm32 = NULL;
535 
536                 if (gpsi && (gpsi->dwSRVIFlags & SRVINFO_IMM32))
537                 {
538                     WCHAR szImmFile[MAX_PATH];
539                     InitializeImmEntryTable();
540                     User32GetImmFileName(szImmFile, _countof(szImmFile));
541                     hImm32 = GetModuleHandleW(szImmFile);
542                 }
543 
544                 if (!IMM_FN(ImmRegisterClient)(&gSharedInfo, hImm32))
545                     return FALSE;
546             }
547             break;
548         }
549 
550         case DLL_PROCESS_DETACH:
551         {
552             if (ghImm32)
553                 FreeLibrary(ghImm32);
554 
555             Cleanup();
556             break;
557         }
558     }
559 
560     /* Finally, initialize GDI */
561     return GdiDllInitialize(hDll, dwReason, pReserved);
562 }
563 
564 NTSTATUS
565 WINAPI
566 User32CallClientThreadSetupFromKernel(PVOID Arguments, ULONG ArgumentLength)
567 {
568   TRACE("User32CallClientThreadSetupFromKernel -->\n");
569   // FIXME: Disabling this call is a HACK!! See also ClientThreadSetup...
570   // ClientThreadSetupHelper(TRUE);
571   TRACE("<-- User32CallClientThreadSetupFromKernel\n");
572   return ZwCallbackReturn(NULL, 0, STATUS_SUCCESS);
573 }
574 
575 NTSTATUS
576 WINAPI
577 User32CallGetCharsetInfo(PVOID Arguments, ULONG ArgumentLength)
578 {
579   BOOL Ret;
580   PGET_CHARSET_INFO pgci = (PGET_CHARSET_INFO)Arguments;
581 
582   TRACE("GetCharsetInfo\n");
583 
584   Ret = TranslateCharsetInfo((DWORD *)(ULONG_PTR)pgci->Locale, &pgci->Cs, TCI_SRCLOCALE);
585 
586   return ZwCallbackReturn(Arguments, ArgumentLength, Ret ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
587 }
588 
589 NTSTATUS
590 WINAPI
591 User32CallSetWndIconsFromKernel(PVOID Arguments, ULONG ArgumentLength)
592 {
593   PSETWNDICONS_CALLBACK_ARGUMENTS Common = Arguments;
594 
595   if (!gpsi->hIconSmWindows)
596   {
597       Common->hIconSample    = LoadImageW(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
598       Common->hIconHand      = LoadImageW(0, IDI_HAND,        IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
599       Common->hIconQuestion  = LoadImageW(0, IDI_QUESTION,    IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
600       Common->hIconBang      = LoadImageW(0, IDI_EXCLAMATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
601       Common->hIconNote      = LoadImageW(0, IDI_ASTERISK,    IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
602       Common->hIconWindows   = LoadImageW(0, IDI_WINLOGO,     IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
603       Common->hIconSmWindows = LoadImageW(0, IDI_WINLOGO, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
604       hIconWindows   = Common->hIconWindows;
605       hIconSmWindows = Common->hIconSmWindows;
606   }
607   ERR("hIconSmWindows %p hIconWindows %p \n",hIconSmWindows,hIconWindows);
608   return ZwCallbackReturn(Arguments, ArgumentLength, STATUS_SUCCESS);
609 }
610 
611 NTSTATUS
612 WINAPI
613 User32DeliverUserAPC(PVOID Arguments, ULONG ArgumentLength)
614 {
615   return ZwCallbackReturn(0, 0, STATUS_SUCCESS);
616 }
617 
618 NTSTATUS
619 WINAPI
620 User32CallOBMFromKernel(PVOID Arguments, ULONG ArgumentLength)
621 {
622   BITMAP bmp;
623   PSETOBM_CALLBACK_ARGUMENTS Common = Arguments;
624 
625   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE)), sizeof(bmp), &bmp);
626   Common->oembmi[OBI_CLOSE].cx = bmp.bmWidth;
627   Common->oembmi[OBI_CLOSE].cy = bmp.bmHeight;
628 
629   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW)), sizeof(bmp), &bmp);
630   Common->oembmi[OBI_MNARROW].cx = bmp.bmWidth;
631   Common->oembmi[OBI_MNARROW].cy = bmp.bmHeight;
632 
633   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW)), sizeof(bmp), &bmp);
634   Common->oembmi[OBI_DNARROW].cx = bmp.bmWidth;
635   Common->oembmi[OBI_DNARROW].cy = bmp.bmHeight;
636 
637   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI)), sizeof(bmp), &bmp);
638   Common->oembmi[OBI_DNARROWI].cx = bmp.bmWidth;
639   Common->oembmi[OBI_DNARROWI].cy = bmp.bmHeight;
640 
641   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW)), sizeof(bmp), &bmp);
642   Common->oembmi[OBI_UPARROW].cx = bmp.bmWidth;
643   Common->oembmi[OBI_UPARROW].cy = bmp.bmHeight;
644 
645   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI)), sizeof(bmp), &bmp);
646   Common->oembmi[OBI_UPARROWI].cx = bmp.bmWidth;
647   Common->oembmi[OBI_UPARROWI].cy = bmp.bmHeight;
648 
649   return ZwCallbackReturn(Arguments, ArgumentLength, STATUS_SUCCESS);
650 }
651 
652 NTSTATUS WINAPI User32CallLPKFromKernel(PVOID Arguments, ULONG ArgumentLength)
653 {
654     BOOL bResult;
655     PLPK_CALLBACK_ARGUMENTS Argument;
656 
657     Argument = (PLPK_CALLBACK_ARGUMENTS)Arguments;
658 
659     Argument->lpString = (LPWSTR)((ULONG_PTR)Argument->lpString + (ULONG_PTR)Argument);
660 
661     bResult = ExtTextOutW(Argument->hdc,
662                           Argument->x,
663                           Argument->y,
664                           Argument->flags,
665                           (Argument->bRect) ? &Argument->rect : NULL,
666                           Argument->lpString,
667                           Argument->count,
668                           NULL);
669 
670     return ZwCallbackReturn(&bResult, sizeof(BOOL), STATUS_SUCCESS);
671 }
672 
673 NTSTATUS WINAPI User32CallUMPDFromKernel(PVOID Arguments, ULONG ArgumentLength)
674 {
675     DWORD Buffer[MAX_USER_MODE_DRV_BUFFER];
676     INT cbSize = 0;
677     NTSTATUS Status = STATUS_SUCCESS;
678     PUMPDPKTHEAD pkt, pktOut = NULL;
679 
680     pkt = (PUMPDPKTHEAD)Arguments;
681 
682     if ( pkt->RetSize <= sizeof(Buffer) )
683     {
684         pktOut = (PUMPDPKTHEAD)Buffer;
685 
686         if ( (GdiPrinterThunk( pkt, pktOut, pkt->RetSize ) == GDI_ERROR) )
687         {
688             pktOut = NULL;
689             Status = STATUS_UNSUCCESSFUL;
690         }
691         else
692         {
693             cbSize = pkt->RetSize;
694         }
695     }
696     else
697     {
698        Status = STATUS_NO_MEMORY;
699     }
700     return ZwCallbackReturn( pktOut, cbSize, Status );
701 }
702 
703 NTSTATUS WINAPI
704 User32CallImmProcessKeyFromKernel(PVOID Arguments, ULONG ArgumentLength)
705 {
706     PIMMPROCESSKEY_CALLBACK_ARGUMENTS Common = Arguments;
707     DWORD Result = IMM_FN(ImmProcessKey)(Common->hWnd,
708                                          Common->hKL,
709                                          Common->vKey,
710                                          Common->lParam,
711                                          Common->dwHotKeyID);
712 
713     return ZwCallbackReturn(&Result, sizeof(DWORD), STATUS_SUCCESS);
714 }
715 
716 NTSTATUS WINAPI
717 User32CallImmLoadLayoutFromKernel(PVOID Arguments, ULONG ArgumentLength)
718 {
719     PIMMLOADLAYOUT_CALLBACK_ARGUMENTS Common = Arguments;
720     IMMLOADLAYOUT_CALLBACK_OUTPUT Result;
721     Result.ret = IMM_FN(ImmLoadLayout)(Common->hKL, &Result.iiex);
722     return ZwCallbackReturn(&Result, sizeof(Result), STATUS_SUCCESS);
723 }
724