xref: /reactos/win32ss/user/user32/misc/dllmain.c (revision 3f411c5d)
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 INT WINAPI
470 DllMain(
471    IN PVOID hInstanceDll,
472    IN ULONG dwReason,
473    IN PVOID reserved)
474 {
475     switch (dwReason)
476     {
477         case DLL_PROCESS_ATTACH:
478         {
479 
480 #define WIN_OBJ_DIR L"\\Windows"
481 #define SESSION_DIR L"\\Sessions"
482 
483             USERSRV_API_CONNECTINFO ConnectInfo; // USERCONNECT
484 
485 #if 0 // Disabling this code is a BIG HACK!!
486 
487             NTSTATUS Status;
488             ULONG ConnectInfoSize = sizeof(ConnectInfo);
489             WCHAR SessionDir[256];
490 
491             /* Cache the PEB and Session ID */
492             PPEB Peb = NtCurrentPeb();
493             ULONG SessionId = Peb->SessionId; // gSessionId
494 
495             TRACE("user32::DllMain\n");
496 
497             /* Don't bother us for each thread */
498             DisableThreadLibraryCalls(hInstanceDll);
499 
500             RtlZeroMemory(&ConnectInfo, sizeof(ConnectInfo));
501 
502             /* Minimal setup of the connect info structure */
503             ConnectInfo.ulVersion = USER_VERSION;
504 
505             /* Setup the Object Directory path */
506             if (!SessionId)
507             {
508                 /* Use the raw path */
509                 wcscpy(SessionDir, WIN_OBJ_DIR);
510             }
511             else
512             {
513                 /* Use the session path */
514                 swprintf(SessionDir,
515                          L"%ws\\%ld%ws",
516                          SESSION_DIR,
517                          SessionId,
518                          WIN_OBJ_DIR);
519             }
520 
521             TRACE("Checkpoint (call CSR)\n");
522 
523             /* Connect to the USER Server */
524             Status = CsrClientConnectToServer(SessionDir,
525                                               USERSRV_SERVERDLL_INDEX,
526                                               &ConnectInfo,
527                                               &ConnectInfoSize,
528                                               &gfServerProcess);
529             if (!NT_SUCCESS(Status))
530             {
531                 ERR("Failed to connect to CSR (Status %lx)\n", Status);
532                 return FALSE;
533             }
534 
535             TRACE("Checkpoint (CSR called)\n");
536 
537 #endif
538 
539             User32Instance = hInstanceDll;
540 
541             /* Finish initialization */
542             TRACE("Checkpoint (call Init)\n");
543             if (!Init(&ConnectInfo))
544                 return FALSE;
545 
546             if (!gfServerProcess)
547             {
548                 HINSTANCE hImm32 = NULL;
549 
550                 if (gpsi && (gpsi->dwSRVIFlags & SRVINFO_IMM32))
551                 {
552                     WCHAR szImmFile[MAX_PATH];
553                     InitializeImmEntryTable();
554                     User32GetImmFileName(szImmFile, _countof(szImmFile));
555                     hImm32 = GetModuleHandleW(szImmFile);
556                 }
557 
558                 if (!IMM_FN(ImmRegisterClient)(&gSharedInfo, hImm32))
559                     return FALSE;
560             }
561             break;
562         }
563 
564         case DLL_PROCESS_DETACH:
565         {
566             if (ghImm32)
567                 FreeLibrary(ghImm32);
568 
569             Cleanup();
570             break;
571         }
572     }
573 
574     /* Finally, initialize GDI */
575     return GdiDllInitialize(hInstanceDll, dwReason, reserved);
576 }
577 
578 NTSTATUS
579 WINAPI
580 User32CallClientThreadSetupFromKernel(PVOID Arguments, ULONG ArgumentLength)
581 {
582   TRACE("User32CallClientThreadSetupFromKernel -->\n");
583   // FIXME: Disabling this call is a HACK!! See also ClientThreadSetup...
584   // ClientThreadSetupHelper(TRUE);
585   TRACE("<-- User32CallClientThreadSetupFromKernel\n");
586   return ZwCallbackReturn(NULL, 0, STATUS_SUCCESS);
587 }
588 
589 NTSTATUS
590 WINAPI
591 User32CallGetCharsetInfo(PVOID Arguments, ULONG ArgumentLength)
592 {
593   BOOL Ret;
594   PGET_CHARSET_INFO pgci = (PGET_CHARSET_INFO)Arguments;
595 
596   TRACE("GetCharsetInfo\n");
597 
598   Ret = TranslateCharsetInfo((DWORD *)(ULONG_PTR)pgci->Locale, &pgci->Cs, TCI_SRCLOCALE);
599 
600   return ZwCallbackReturn(Arguments, ArgumentLength, Ret ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
601 }
602 
603 NTSTATUS
604 WINAPI
605 User32CallSetWndIconsFromKernel(PVOID Arguments, ULONG ArgumentLength)
606 {
607   PSETWNDICONS_CALLBACK_ARGUMENTS Common = Arguments;
608 
609   if (!gpsi->hIconSmWindows)
610   {
611       Common->hIconSample    = LoadImageW(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
612       Common->hIconHand      = LoadImageW(0, IDI_HAND,        IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
613       Common->hIconQuestion  = LoadImageW(0, IDI_QUESTION,    IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
614       Common->hIconBang      = LoadImageW(0, IDI_EXCLAMATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
615       Common->hIconNote      = LoadImageW(0, IDI_ASTERISK,    IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
616       Common->hIconWindows   = LoadImageW(0, IDI_WINLOGO,     IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
617       Common->hIconSmWindows = LoadImageW(0, IDI_WINLOGO, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
618       hIconWindows   = Common->hIconWindows;
619       hIconSmWindows = Common->hIconSmWindows;
620   }
621   ERR("hIconSmWindows %p hIconWindows %p \n",hIconSmWindows,hIconWindows);
622   return ZwCallbackReturn(Arguments, ArgumentLength, STATUS_SUCCESS);
623 }
624 
625 NTSTATUS
626 WINAPI
627 User32DeliverUserAPC(PVOID Arguments, ULONG ArgumentLength)
628 {
629   return ZwCallbackReturn(0, 0, STATUS_SUCCESS);
630 }
631 
632 NTSTATUS
633 WINAPI
634 User32CallOBMFromKernel(PVOID Arguments, ULONG ArgumentLength)
635 {
636   BITMAP bmp;
637   PSETOBM_CALLBACK_ARGUMENTS Common = Arguments;
638 
639   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE)), sizeof(bmp), &bmp);
640   Common->oembmi[OBI_CLOSE].cx = bmp.bmWidth;
641   Common->oembmi[OBI_CLOSE].cy = bmp.bmHeight;
642 
643   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW)), sizeof(bmp), &bmp);
644   Common->oembmi[OBI_MNARROW].cx = bmp.bmWidth;
645   Common->oembmi[OBI_MNARROW].cy = bmp.bmHeight;
646 
647   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW)), sizeof(bmp), &bmp);
648   Common->oembmi[OBI_DNARROW].cx = bmp.bmWidth;
649   Common->oembmi[OBI_DNARROW].cy = bmp.bmHeight;
650 
651   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI)), sizeof(bmp), &bmp);
652   Common->oembmi[OBI_DNARROWI].cx = bmp.bmWidth;
653   Common->oembmi[OBI_DNARROWI].cy = bmp.bmHeight;
654 
655   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW)), sizeof(bmp), &bmp);
656   Common->oembmi[OBI_UPARROW].cx = bmp.bmWidth;
657   Common->oembmi[OBI_UPARROW].cy = bmp.bmHeight;
658 
659   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI)), sizeof(bmp), &bmp);
660   Common->oembmi[OBI_UPARROWI].cx = bmp.bmWidth;
661   Common->oembmi[OBI_UPARROWI].cy = bmp.bmHeight;
662 
663   return ZwCallbackReturn(Arguments, ArgumentLength, STATUS_SUCCESS);
664 }
665 
666 NTSTATUS WINAPI User32CallLPKFromKernel(PVOID Arguments, ULONG ArgumentLength)
667 {
668     BOOL bResult;
669     PLPK_CALLBACK_ARGUMENTS Argument;
670 
671     Argument = (PLPK_CALLBACK_ARGUMENTS)Arguments;
672 
673     Argument->lpString = (LPWSTR)((ULONG_PTR)Argument->lpString + (ULONG_PTR)Argument);
674 
675     bResult = ExtTextOutW(Argument->hdc,
676                           Argument->x,
677                           Argument->y,
678                           Argument->flags,
679                           (Argument->bRect) ? &Argument->rect : NULL,
680                           Argument->lpString,
681                           Argument->count,
682                           NULL);
683 
684     return ZwCallbackReturn(&bResult, sizeof(BOOL), STATUS_SUCCESS);
685 }
686 
687 NTSTATUS WINAPI User32CallUMPDFromKernel(PVOID Arguments, ULONG ArgumentLength)
688 {
689     DWORD Buffer[MAX_USER_MODE_DRV_BUFFER];
690     INT cbSize = 0;
691     NTSTATUS Status = STATUS_SUCCESS;
692     PUMPDPKTHEAD pkt, pktOut = NULL;
693 
694     pkt = (PUMPDPKTHEAD)Arguments;
695 
696     if ( pkt->RetSize <= sizeof(Buffer) )
697     {
698         pktOut = (PUMPDPKTHEAD)Buffer;
699 
700         if ( (GdiPrinterThunk( pkt, pktOut, pkt->RetSize ) == GDI_ERROR) )
701         {
702             pktOut = NULL;
703             Status = STATUS_UNSUCCESSFUL;
704         }
705         else
706         {
707             cbSize = pkt->RetSize;
708         }
709     }
710     else
711     {
712        Status = STATUS_NO_MEMORY;
713     }
714     return ZwCallbackReturn( pktOut, cbSize, Status );
715 }
716 
717 NTSTATUS WINAPI
718 User32CallImmProcessKeyFromKernel(PVOID Arguments, ULONG ArgumentLength)
719 {
720     PIMMPROCESSKEY_CALLBACK_ARGUMENTS Common = Arguments;
721     DWORD Result = IMM_FN(ImmProcessKey)(Common->hWnd,
722                                          Common->hKL,
723                                          Common->vKey,
724                                          Common->lParam,
725                                          Common->dwHotKeyID);
726 
727     return ZwCallbackReturn(&Result, sizeof(DWORD), STATUS_SUCCESS);
728 }
729 
730 NTSTATUS WINAPI
731 User32CallImmLoadLayoutFromKernel(PVOID Arguments, ULONG ArgumentLength)
732 {
733     PIMMLOADLAYOUT_CALLBACK_ARGUMENTS Common = Arguments;
734     IMMLOADLAYOUT_CALLBACK_OUTPUT Result;
735     Result.ret = IMM_FN(ImmLoadLayout)(Common->hKL, &Result.iiex);
736     return ZwCallbackReturn(&Result, sizeof(Result), STATUS_SUCCESS);
737 }
738