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