xref: /reactos/win32ss/user/user32/misc/dllmain.c (revision ccef43f3)
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 PVOID apfnDispatch[USER32_CALLBACK_MAXIMUM + 1] =
207 {
208     User32CallWindowProcFromKernel,
209     User32CallSendAsyncProcForKernel,
210     User32LoadSysMenuTemplateForKernel,
211     User32SetupDefaultCursors,
212     User32CallHookProcFromKernel,
213     User32CallEventProcFromKernel,
214     User32CallLoadMenuFromKernel,
215     User32CallClientThreadSetupFromKernel,
216     User32CallClientLoadLibraryFromKernel,
217     User32CallGetCharsetInfo,
218     User32CallCopyImageFromKernel,
219     User32CallSetWndIconsFromKernel,
220     User32DeliverUserAPC,
221     User32CallDDEPostFromKernel,
222     User32CallDDEGetFromKernel,
223     User32CallOBMFromKernel,
224     User32CallLPKFromKernel,
225     User32CallUMPDFromKernel,
226     User32CallImmProcessKeyFromKernel,
227     User32CallImmLoadLayoutFromKernel,
228 };
229 
230 
231 VOID
232 WINAPI
233 GdiProcessSetup(VOID);
234 
235 BOOL
236 WINAPI
237 ClientThreadSetupHelper(BOOL IsCallback)
238 {
239     /*
240      * Normally we are called by win32k so the win32 thread pointers
241      * should be valid as they are set in win32k::InitThreadCallback.
242      */
243     PCLIENTINFO ClientInfo = GetWin32ClientInfo();
244     BOOLEAN IsFirstThread = _InterlockedExchange8((PCHAR)&gfFirstThread, FALSE);
245 
246     TRACE("In ClientThreadSetup(IsCallback == %s, gfServerProcess = %s, IsFirstThread = %s)\n",
247           IsCallback ? "TRUE" : "FALSE", gfServerProcess ? "TRUE" : "FALSE", IsFirstThread ? "TRUE" : "FALSE");
248 
249     if (IsFirstThread)
250         GdiProcessSetup();
251 
252     /* Check for already initialized thread, and bail out if so */
253     if (ClientInfo->CI_flags & CI_INITTHREAD)
254     {
255         ERR("ClientThreadSetup: Thread already initialized.\n");
256         return FALSE;
257     }
258 
259     /*
260      * CSRSS couldn't use user32::DllMain CSR server-to-server call to connect
261      * to win32k. So it is delayed to a manually-call to ClientThreadSetup.
262      * Also this needs to be done only for the first thread (since the connection
263      * is per-process).
264      */
265     if (gfServerProcess && IsFirstThread)
266     {
267         NTSTATUS Status;
268         USERCONNECT UserCon;
269 
270         RtlZeroMemory(&UserCon, sizeof(UserCon));
271 
272         /* Minimal setup of the connect info structure */
273         UserCon.ulVersion = USER_VERSION;
274         // UserCon.dwDispatchCount;
275 
276         /* Connect to win32k */
277         Status = NtUserProcessConnect(NtCurrentProcess(),
278                                       &UserCon,
279                                       sizeof(UserCon));
280         if (!NT_SUCCESS(Status)) return FALSE;
281 
282         /* Retrieve data */
283         g_ppi = ClientInfo->ppi; // Snapshot PI, used as pointer only!
284         gSharedInfo = UserCon.siClient;
285         gpsi = gSharedInfo.psi;
286         gHandleTable = gSharedInfo.aheList;
287         /* ReactOS-Specific! */ gHandleEntries = SharedPtrToUser(gHandleTable->handles);
288 
289         // ERR("1 SI 0x%x : HT 0x%x : D 0x%x\n",
290         //     gSharedInfo.psi, gSharedInfo.aheList, gSharedInfo.ulSharedDelta);
291     }
292 
293     TRACE("Checkpoint (register PFN)\n");
294     if (!RegisterClientPFN())
295     {
296         ERR("RegisterClientPFN failed\n");
297         return FALSE;
298     }
299 
300     /* Mark this thread as initialized */
301     ClientInfo->CI_flags |= CI_INITTHREAD;
302 
303     /* Initialization that should be done once per process */
304     if (IsFirstThread)
305     {
306         TRACE("Checkpoint (Allocating TLS)\n");
307 
308         /* Allocate an index for user32 thread local data */
309         User32TlsIndex = TlsAlloc();
310         if (User32TlsIndex == TLS_OUT_OF_INDEXES)
311             return FALSE;
312 
313         // HAAAAAAAAAACK!!!!!!
314         // ASSERT(gpsi);
315         if (!gpsi) ERR("AAAAAAAAAAAHHHHHHHHHHHHHH!!!!!!!! gpsi == NULL !!!!\n");
316         if (gpsi)
317         {
318         TRACE("Checkpoint (MessageInit)\n");
319 
320         if (MessageInit())
321         {
322             TRACE("Checkpoint (MenuInit)\n");
323             if (MenuInit())
324             {
325                 TRACE("Checkpoint initialization done OK\n");
326                 InitializeCriticalSection(&U32AccelCacheLock);
327                 LoadAppInitDlls();
328                 return TRUE;
329             }
330             MessageCleanup();
331         }
332 
333         TlsFree(User32TlsIndex);
334         return FALSE;
335         }
336     }
337 
338     return TRUE;
339 }
340 
341 /*
342  * @implemented
343  */
344 BOOL
345 WINAPI
346 ClientThreadSetup(VOID)
347 {
348     //
349     // This routine, in Windows, does a lot of what Init does, but in a radically
350     // different way.
351     //
352     // In Windows, because CSRSS's threads have TIF_CSRSSTHREAD set (we have this
353     // flag in ROS but not sure if we use it), the xxxClientThreadSetup callback
354     // isn't made when CSRSS first loads WINSRV.DLL (which loads USER32.DLL).
355     //
356     // However, all the other calls are made as normal, and WINSRV.DLL loads
357     // USER32.dll, the DllMain runs, and eventually the first NtUser system call is
358     // made which initializes Win32k (and initializes the thread, but as mentioned
359     // above, the thread is marked as TIF_CSRSSTHREAD).
360     //
361     // In the DllMain of User32, there is also a CsrClientConnectToServer call to
362     // server 2 (winsrv). When this is done from CSRSS, the "InServer" flag is set,
363     // so user32 will remember that it's running inside of CSRSS. Also, another
364     // flag, called "FirstThread" is manually set by DllMain.
365     //
366     // Then, WINSRV finishes loading, and CSRSRV starts the API thread/loop. This
367     // code then calls CsrConnectToUser, which calls... ClientThreadStartup. Now
368     // this routine detects that it's in the server process, which means it's CSRSS
369     // and that the callback never happened. It does some first-time-Win32k connection
370     // initialization and caches a bunch of things -- if it's the first thread. It also
371     // acquires a critical section to initialize GDI -- and then resets the first thread
372     // flag.
373     //
374     // For now, we'll do none of this, but to support Windows' CSRSRV.DLL which calls
375     // CsrConnectToUser, we'll pretend we "did something" here. Then the rest will
376     // continue as normal.
377     //
378 
379     // FIXME: Disabling this call is a HACK!! See also User32CallClientThreadSetupFromKernel...
380     // return ClientThreadSetupHelper(FALSE);
381     TRACE("ClientThreadSetup is not implemented\n");
382     return TRUE;
383 }
384 
385 BOOL
386 Init(PUSERCONNECT UserCon /*PUSERSRV_API_CONNECTINFO*/)
387 {
388     NTSTATUS Status = STATUS_SUCCESS;
389 
390     TRACE("user32::Init(0x%p) -->\n", UserCon);
391 
392     RtlInitializeCriticalSection(&gcsUserApiHook);
393 
394     /* Initialize callback table in PEB data */
395     NtCurrentPeb()->KernelCallbackTable = apfnDispatch;
396     NtCurrentPeb()->PostProcessInitRoutine = NULL;
397 
398     // This is a HACK!! //
399     gfServerProcess = FALSE;
400     gfFirstThread   = TRUE;
401     //// End of HACK!! ///
402 
403     /*
404      * Retrieve data from the connect info structure if the initializing
405      * process is not CSRSS. In case it is, this will be done from inside
406      * ClientThreadSetup.
407      */
408     if (!gfServerProcess)
409     {
410         // FIXME: HACK!! We should fixup for the NtUserProcessConnect fixups
411         // because it was made in the context of CSRSS process and not ours!!
412         // So... as long as we don't fix that, we need to redo again a call
413         // to NtUserProcessConnect... How perverse is that?!
414         //
415         // HACK(2): This call is necessary since we disabled
416         // the CSR call in DllMain...
417         {
418             RtlZeroMemory(UserCon, sizeof(*UserCon));
419 
420             /* Minimal setup of the connect info structure */
421             UserCon->ulVersion = USER_VERSION;
422             // UserCon->dwDispatchCount;
423 
424             TRACE("HACK: Hackish NtUserProcessConnect call!!\n");
425             /* Connect to win32k */
426             Status = NtUserProcessConnect(NtCurrentProcess(),
427                                           UserCon,
428                                           sizeof(*UserCon));
429             if (!NT_SUCCESS(Status)) return FALSE;
430         }
431 
432         //
433         // We continue as we should do normally...
434         //
435 
436         /* Retrieve data */
437         g_ppi = GetWin32ClientInfo()->ppi; // Snapshot PI, used as pointer only!
438         gSharedInfo = UserCon->siClient;
439         gpsi = gSharedInfo.psi;
440         gHandleTable = gSharedInfo.aheList;
441         /* ReactOS-Specific! */ gHandleEntries = SharedPtrToUser(gHandleTable->handles);
442     }
443 
444     // FIXME: Yet another hack... This call should normally not be done here, but
445     // instead in ClientThreadSetup, and in User32CallClientThreadSetupFromKernel as well.
446     TRACE("HACK: Using Init-ClientThreadSetupHelper hack!!\n");
447     if (!ClientThreadSetupHelper(FALSE))
448     {
449         TRACE("Init-ClientThreadSetupHelper hack failed!\n");
450         return FALSE;
451     }
452 
453     TRACE("<-- user32::Init()\n");
454 
455     return NT_SUCCESS(Status);
456 }
457 
458 VOID
459 Cleanup(VOID)
460 {
461     UnloadAppInitDlls();
462     DeleteCriticalSection(&U32AccelCacheLock);
463     MenuCleanup();
464     MessageCleanup();
465     TlsFree(User32TlsIndex);
466     DeleteFrameBrushes();
467 }
468 
469 // UserClientDllInitialize
470 BOOL
471 WINAPI
472 DllMain(
473     _In_ HANDLE hDll,
474     _In_ ULONG dwReason,
475     _In_opt_ PVOID pReserved)
476 {
477     switch (dwReason)
478     {
479         case DLL_PROCESS_ATTACH:
480         {
481 
482 #define WIN_OBJ_DIR L"\\Windows"
483 #define SESSION_DIR L"\\Sessions"
484 
485             USERSRV_API_CONNECTINFO ConnectInfo; // USERCONNECT
486 
487 #if 0 // Disabling this code is a BIG HACK!!
488 
489             NTSTATUS Status;
490             ULONG ConnectInfoSize = sizeof(ConnectInfo);
491             WCHAR SessionDir[256];
492 
493             /* Cache the PEB and Session ID */
494             PPEB Peb = NtCurrentPeb();
495             ULONG SessionId = Peb->SessionId; // gSessionId
496 
497             TRACE("user32::DllMain\n");
498 
499             /* Don't bother us for each thread */
500             DisableThreadLibraryCalls(hDll);
501 
502             RtlZeroMemory(&ConnectInfo, sizeof(ConnectInfo));
503 
504             /* Minimal setup of the connect info structure */
505             ConnectInfo.ulVersion = USER_VERSION;
506 
507             /* Setup the Object Directory path */
508             if (!SessionId)
509             {
510                 /* Use the raw path */
511                 wcscpy(SessionDir, WIN_OBJ_DIR);
512             }
513             else
514             {
515                 /* Use the session path */
516                 swprintf(SessionDir,
517                          L"%ws\\%ld%ws",
518                          SESSION_DIR,
519                          SessionId,
520                          WIN_OBJ_DIR);
521             }
522 
523             TRACE("Checkpoint (call CSR)\n");
524 
525             /* Connect to the USER Server */
526             Status = CsrClientConnectToServer(SessionDir,
527                                               USERSRV_SERVERDLL_INDEX,
528                                               &ConnectInfo,
529                                               &ConnectInfoSize,
530                                               &gfServerProcess);
531             if (!NT_SUCCESS(Status))
532             {
533                 ERR("Failed to connect to CSR (Status %lx)\n", Status);
534                 return FALSE;
535             }
536 
537             TRACE("Checkpoint (CSR called)\n");
538 
539 #endif
540 
541             User32Instance = hDll;
542 
543             /* Finish initialization */
544             TRACE("Checkpoint (call Init)\n");
545             if (!Init(&ConnectInfo))
546                 return FALSE;
547 
548             if (!gfServerProcess)
549             {
550                 HINSTANCE hImm32 = NULL;
551 
552                 if (gpsi && (gpsi->dwSRVIFlags & SRVINFO_IMM32))
553                 {
554                     WCHAR szImmFile[MAX_PATH];
555                     InitializeImmEntryTable();
556                     User32GetImmFileName(szImmFile, _countof(szImmFile));
557                     hImm32 = GetModuleHandleW(szImmFile);
558                 }
559 
560                 if (!IMM_FN(ImmRegisterClient)(&gSharedInfo, hImm32))
561                     return FALSE;
562             }
563             break;
564         }
565 
566         case DLL_PROCESS_DETACH:
567         {
568             if (ghImm32)
569                 FreeLibrary(ghImm32);
570 
571             Cleanup();
572             break;
573         }
574     }
575 
576     /* Finally, initialize GDI */
577     return GdiDllInitialize(hDll, dwReason, pReserved);
578 }
579 
580 NTSTATUS
581 WINAPI
582 User32CallClientThreadSetupFromKernel(PVOID Arguments, ULONG ArgumentLength)
583 {
584   TRACE("User32CallClientThreadSetupFromKernel -->\n");
585   // FIXME: Disabling this call is a HACK!! See also ClientThreadSetup...
586   // ClientThreadSetupHelper(TRUE);
587   TRACE("<-- User32CallClientThreadSetupFromKernel\n");
588   return ZwCallbackReturn(NULL, 0, STATUS_SUCCESS);
589 }
590 
591 NTSTATUS
592 WINAPI
593 User32CallGetCharsetInfo(PVOID Arguments, ULONG ArgumentLength)
594 {
595   BOOL Ret;
596   PGET_CHARSET_INFO pgci = (PGET_CHARSET_INFO)Arguments;
597 
598   TRACE("GetCharsetInfo\n");
599 
600   Ret = TranslateCharsetInfo((DWORD *)(ULONG_PTR)pgci->Locale, &pgci->Cs, TCI_SRCLOCALE);
601 
602   return ZwCallbackReturn(Arguments, ArgumentLength, Ret ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
603 }
604 
605 NTSTATUS
606 WINAPI
607 User32CallSetWndIconsFromKernel(PVOID Arguments, ULONG ArgumentLength)
608 {
609   PSETWNDICONS_CALLBACK_ARGUMENTS Common = Arguments;
610 
611   if (!gpsi->hIconSmWindows)
612   {
613       Common->hIconSample    = LoadImageW(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
614       Common->hIconHand      = LoadImageW(0, IDI_HAND,        IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
615       Common->hIconQuestion  = LoadImageW(0, IDI_QUESTION,    IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
616       Common->hIconBang      = LoadImageW(0, IDI_EXCLAMATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
617       Common->hIconNote      = LoadImageW(0, IDI_ASTERISK,    IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
618       Common->hIconWindows   = LoadImageW(0, IDI_WINLOGO,     IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
619       Common->hIconSmWindows = LoadImageW(0, IDI_WINLOGO, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
620       hIconWindows   = Common->hIconWindows;
621       hIconSmWindows = Common->hIconSmWindows;
622   }
623   ERR("hIconSmWindows %p hIconWindows %p \n",hIconSmWindows,hIconWindows);
624   return ZwCallbackReturn(Arguments, ArgumentLength, STATUS_SUCCESS);
625 }
626 
627 NTSTATUS
628 WINAPI
629 User32DeliverUserAPC(PVOID Arguments, ULONG ArgumentLength)
630 {
631   return ZwCallbackReturn(0, 0, STATUS_SUCCESS);
632 }
633 
634 NTSTATUS
635 WINAPI
636 User32CallOBMFromKernel(PVOID Arguments, ULONG ArgumentLength)
637 {
638   BITMAP bmp;
639   PSETOBM_CALLBACK_ARGUMENTS Common = Arguments;
640 
641   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE)), sizeof(bmp), &bmp);
642   Common->oembmi[OBI_CLOSE].cx = bmp.bmWidth;
643   Common->oembmi[OBI_CLOSE].cy = bmp.bmHeight;
644 
645   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW)), sizeof(bmp), &bmp);
646   Common->oembmi[OBI_MNARROW].cx = bmp.bmWidth;
647   Common->oembmi[OBI_MNARROW].cy = bmp.bmHeight;
648 
649   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW)), sizeof(bmp), &bmp);
650   Common->oembmi[OBI_DNARROW].cx = bmp.bmWidth;
651   Common->oembmi[OBI_DNARROW].cy = bmp.bmHeight;
652 
653   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI)), sizeof(bmp), &bmp);
654   Common->oembmi[OBI_DNARROWI].cx = bmp.bmWidth;
655   Common->oembmi[OBI_DNARROWI].cy = bmp.bmHeight;
656 
657   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW)), sizeof(bmp), &bmp);
658   Common->oembmi[OBI_UPARROW].cx = bmp.bmWidth;
659   Common->oembmi[OBI_UPARROW].cy = bmp.bmHeight;
660 
661   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI)), sizeof(bmp), &bmp);
662   Common->oembmi[OBI_UPARROWI].cx = bmp.bmWidth;
663   Common->oembmi[OBI_UPARROWI].cy = bmp.bmHeight;
664 
665   return ZwCallbackReturn(Arguments, ArgumentLength, STATUS_SUCCESS);
666 }
667 
668 NTSTATUS WINAPI User32CallLPKFromKernel(PVOID Arguments, ULONG ArgumentLength)
669 {
670     BOOL bResult;
671     PLPK_CALLBACK_ARGUMENTS Argument;
672 
673     Argument = (PLPK_CALLBACK_ARGUMENTS)Arguments;
674 
675     Argument->lpString = (LPWSTR)((ULONG_PTR)Argument->lpString + (ULONG_PTR)Argument);
676 
677     bResult = ExtTextOutW(Argument->hdc,
678                           Argument->x,
679                           Argument->y,
680                           Argument->flags,
681                           (Argument->bRect) ? &Argument->rect : NULL,
682                           Argument->lpString,
683                           Argument->count,
684                           NULL);
685 
686     return ZwCallbackReturn(&bResult, sizeof(BOOL), STATUS_SUCCESS);
687 }
688 
689 NTSTATUS WINAPI User32CallUMPDFromKernel(PVOID Arguments, ULONG ArgumentLength)
690 {
691     DWORD Buffer[MAX_USER_MODE_DRV_BUFFER];
692     INT cbSize = 0;
693     NTSTATUS Status = STATUS_SUCCESS;
694     PUMPDPKTHEAD pkt, pktOut = NULL;
695 
696     pkt = (PUMPDPKTHEAD)Arguments;
697 
698     if ( pkt->RetSize <= sizeof(Buffer) )
699     {
700         pktOut = (PUMPDPKTHEAD)Buffer;
701 
702         if ( (GdiPrinterThunk( pkt, pktOut, pkt->RetSize ) == GDI_ERROR) )
703         {
704             pktOut = NULL;
705             Status = STATUS_UNSUCCESSFUL;
706         }
707         else
708         {
709             cbSize = pkt->RetSize;
710         }
711     }
712     else
713     {
714        Status = STATUS_NO_MEMORY;
715     }
716     return ZwCallbackReturn( pktOut, cbSize, Status );
717 }
718 
719 NTSTATUS WINAPI
720 User32CallImmProcessKeyFromKernel(PVOID Arguments, ULONG ArgumentLength)
721 {
722     PIMMPROCESSKEY_CALLBACK_ARGUMENTS Common = Arguments;
723     DWORD Result = IMM_FN(ImmProcessKey)(Common->hWnd,
724                                          Common->hKL,
725                                          Common->vKey,
726                                          Common->lParam,
727                                          Common->dwHotKeyID);
728 
729     return ZwCallbackReturn(&Result, sizeof(DWORD), STATUS_SUCCESS);
730 }
731 
732 NTSTATUS WINAPI
733 User32CallImmLoadLayoutFromKernel(PVOID Arguments, ULONG ArgumentLength)
734 {
735     PIMMLOADLAYOUT_CALLBACK_ARGUMENTS Common = Arguments;
736     IMMLOADLAYOUT_CALLBACK_OUTPUT Result;
737     Result.ret = IMM_FN(ImmLoadLayout)(Common->hKL, &Result.iiex);
738     return ZwCallbackReturn(&Result, sizeof(Result), STATUS_SUCCESS);
739 }
740