xref: /reactos/win32ss/user/user32/misc/dllmain.c (revision 81db5e1d)
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 };
227 
228 
229 
230 VOID
231 WINAPI
232 GdiProcessSetup(VOID);
233 
234 BOOL
235 WINAPI
236 ClientThreadSetupHelper(BOOL IsCallback)
237 {
238     /*
239      * Normally we are called by win32k so the win32 thread pointers
240      * should be valid as they are set in win32k::InitThreadCallback.
241      */
242     PCLIENTINFO ClientInfo = GetWin32ClientInfo();
243     BOOLEAN IsFirstThread = _InterlockedExchange8((PCHAR)&gfFirstThread, FALSE);
244 
245     TRACE("In ClientThreadSetup(IsCallback == %s, gfServerProcess = %s, IsFirstThread = %s)\n",
246           IsCallback ? "TRUE" : "FALSE", gfServerProcess ? "TRUE" : "FALSE", IsFirstThread ? "TRUE" : "FALSE");
247 
248     if (IsFirstThread)
249         GdiProcessSetup();
250 
251     /* Check for already initialized thread, and bail out if so */
252     if (ClientInfo->CI_flags & CI_INITTHREAD)
253     {
254         ERR("ClientThreadSetup: Thread already initialized.\n");
255         return FALSE;
256     }
257 
258     /*
259      * CSRSS couldn't use user32::DllMain CSR server-to-server call to connect
260      * to win32k. So it is delayed to a manually-call to ClientThreadSetup.
261      * Also this needs to be done only for the first thread (since the connection
262      * is per-process).
263      */
264     if (gfServerProcess && IsFirstThread)
265     {
266         NTSTATUS Status;
267         USERCONNECT UserCon;
268 
269         RtlZeroMemory(&UserCon, sizeof(UserCon));
270 
271         /* Minimal setup of the connect info structure */
272         UserCon.ulVersion = USER_VERSION;
273         // UserCon.dwDispatchCount;
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         gSharedInfo = UserCon.siClient;
284         gpsi = gSharedInfo.psi;
285         gHandleTable = gSharedInfo.aheList;
286         /* ReactOS-Specific! */ gHandleEntries = SharedPtrToUser(gHandleTable->handles);
287 
288         // ERR("1 SI 0x%x : HT 0x%x : D 0x%x\n",
289         //     gSharedInfo.psi, gSharedInfo.aheList, gSharedInfo.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             // UserCon->dwDispatchCount;
422 
423             TRACE("HACK: Hackish NtUserProcessConnect call!!\n");
424             /* Connect to win32k */
425             Status = NtUserProcessConnect(NtCurrentProcess(),
426                                           UserCon,
427                                           sizeof(*UserCon));
428             if (!NT_SUCCESS(Status)) return FALSE;
429         }
430 
431         //
432         // We continue as we should do normally...
433         //
434 
435         /* Retrieve data */
436         g_ppi = GetWin32ClientInfo()->ppi; // Snapshot PI, used as pointer only!
437         gSharedInfo = UserCon->siClient;
438         gpsi = gSharedInfo.psi;
439         gHandleTable = gSharedInfo.aheList;
440         /* ReactOS-Specific! */ gHandleEntries = SharedPtrToUser(gHandleTable->handles);
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                 HINSTANCE hImm32 = NULL;
548 
549                 if (gpsi && (gpsi->dwSRVIFlags & SRVINFO_IMM32))
550                 {
551                     WCHAR szImmFile[MAX_PATH];
552                     InitializeImmEntryTable();
553                     GetImmFileName(szImmFile, _countof(szImmFile));
554                     hImm32 = GetModuleHandleW(szImmFile);
555                 }
556 
557                 if (!IMM_FN(ImmRegisterClient)(&gSharedInfo, hImm32))
558                     return FALSE;
559             }
560             break;
561         }
562 
563         case DLL_PROCESS_DETACH:
564         {
565             if (ghImm32)
566                 FreeLibrary(ghImm32);
567 
568             Cleanup();
569             break;
570         }
571     }
572 
573     /* Finally, initialize GDI */
574     return GdiDllInitialize(hInstanceDll, dwReason, reserved);
575 }
576 
577 NTSTATUS
578 WINAPI
579 User32CallClientThreadSetupFromKernel(PVOID Arguments, ULONG ArgumentLength)
580 {
581   TRACE("User32CallClientThreadSetupFromKernel -->\n");
582   // FIXME: Disabling this call is a HACK!! See also ClientThreadSetup...
583   // ClientThreadSetupHelper(TRUE);
584   TRACE("<-- User32CallClientThreadSetupFromKernel\n");
585   return ZwCallbackReturn(NULL, 0, STATUS_SUCCESS);
586 }
587 
588 NTSTATUS
589 WINAPI
590 User32CallGetCharsetInfo(PVOID Arguments, ULONG ArgumentLength)
591 {
592   BOOL Ret;
593   PGET_CHARSET_INFO pgci = (PGET_CHARSET_INFO)Arguments;
594 
595   TRACE("GetCharsetInfo\n");
596 
597   Ret = TranslateCharsetInfo((DWORD *)(ULONG_PTR)pgci->Locale, &pgci->Cs, TCI_SRCLOCALE);
598 
599   return ZwCallbackReturn(Arguments, ArgumentLength, Ret ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
600 }
601 
602 NTSTATUS
603 WINAPI
604 User32CallSetWndIconsFromKernel(PVOID Arguments, ULONG ArgumentLength)
605 {
606   PSETWNDICONS_CALLBACK_ARGUMENTS Common = Arguments;
607 
608   if (!gpsi->hIconSmWindows)
609   {
610       Common->hIconSample    = LoadImageW(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
611       Common->hIconHand      = LoadImageW(0, IDI_HAND,        IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
612       Common->hIconQuestion  = LoadImageW(0, IDI_QUESTION,    IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
613       Common->hIconBang      = LoadImageW(0, IDI_EXCLAMATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
614       Common->hIconNote      = LoadImageW(0, IDI_ASTERISK,    IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
615       Common->hIconWindows   = LoadImageW(0, IDI_WINLOGO,     IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
616       Common->hIconSmWindows = LoadImageW(0, IDI_WINLOGO, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
617       hIconWindows   = Common->hIconWindows;
618       hIconSmWindows = Common->hIconSmWindows;
619   }
620   ERR("hIconSmWindows %p hIconWindows %p \n",hIconSmWindows,hIconWindows);
621   return ZwCallbackReturn(Arguments, ArgumentLength, STATUS_SUCCESS);
622 }
623 
624 NTSTATUS
625 WINAPI
626 User32DeliverUserAPC(PVOID Arguments, ULONG ArgumentLength)
627 {
628   return ZwCallbackReturn(0, 0, STATUS_SUCCESS);
629 }
630 
631 NTSTATUS
632 WINAPI
633 User32CallOBMFromKernel(PVOID Arguments, ULONG ArgumentLength)
634 {
635   BITMAP bmp;
636   PSETOBM_CALLBACK_ARGUMENTS Common = Arguments;
637 
638   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE)), sizeof(bmp), &bmp);
639   Common->oembmi[OBI_CLOSE].cx = bmp.bmWidth;
640   Common->oembmi[OBI_CLOSE].cy = bmp.bmHeight;
641 
642   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW)), sizeof(bmp), &bmp);
643   Common->oembmi[OBI_MNARROW].cx = bmp.bmWidth;
644   Common->oembmi[OBI_MNARROW].cy = bmp.bmHeight;
645 
646   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW)), sizeof(bmp), &bmp);
647   Common->oembmi[OBI_DNARROW].cx = bmp.bmWidth;
648   Common->oembmi[OBI_DNARROW].cy = bmp.bmHeight;
649 
650   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI)), sizeof(bmp), &bmp);
651   Common->oembmi[OBI_DNARROWI].cx = bmp.bmWidth;
652   Common->oembmi[OBI_DNARROWI].cy = bmp.bmHeight;
653 
654   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW)), sizeof(bmp), &bmp);
655   Common->oembmi[OBI_UPARROW].cx = bmp.bmWidth;
656   Common->oembmi[OBI_UPARROW].cy = bmp.bmHeight;
657 
658   GetObjectW(LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI)), sizeof(bmp), &bmp);
659   Common->oembmi[OBI_UPARROWI].cx = bmp.bmWidth;
660   Common->oembmi[OBI_UPARROWI].cy = bmp.bmHeight;
661 
662   return ZwCallbackReturn(Arguments, ArgumentLength, STATUS_SUCCESS);
663 }
664 
665 NTSTATUS WINAPI User32CallLPKFromKernel(PVOID Arguments, ULONG ArgumentLength)
666 {
667     BOOL bResult;
668     PLPK_CALLBACK_ARGUMENTS Argument;
669 
670     Argument = (PLPK_CALLBACK_ARGUMENTS)Arguments;
671 
672     Argument->lpString = (LPWSTR)((ULONG_PTR)Argument->lpString + (ULONG_PTR)Argument);
673 
674     bResult = ExtTextOutW(Argument->hdc,
675                           Argument->x,
676                           Argument->y,
677                           Argument->flags,
678                           (Argument->bRect) ? &Argument->rect : NULL,
679                           Argument->lpString,
680                           Argument->count,
681                           NULL);
682 
683     return ZwCallbackReturn(&bResult, sizeof(BOOL), STATUS_SUCCESS);
684 }
685 
686 NTSTATUS WINAPI User32CallUMPDFromKernel(PVOID Arguments, ULONG ArgumentLength)
687 {
688     DWORD Buffer[MAX_USER_MODE_DRV_BUFFER];
689     INT cbSize = 0;
690     NTSTATUS Status = STATUS_SUCCESS;
691     PUMPDPKTHEAD pkt, pktOut = NULL;
692 
693     pkt = (PUMPDPKTHEAD)Arguments;
694 
695     if ( pkt->RetSize <= sizeof(Buffer) )
696     {
697         pktOut = (PUMPDPKTHEAD)Buffer;
698 
699         if ( (GdiPrinterThunk( pkt, pktOut, pkt->RetSize ) == GDI_ERROR) )
700         {
701             pktOut = NULL;
702             Status = STATUS_UNSUCCESSFUL;
703         }
704         else
705         {
706             cbSize = pkt->RetSize;
707         }
708     }
709     else
710     {
711        Status = STATUS_NO_MEMORY;
712     }
713     return ZwCallbackReturn( pktOut, cbSize, Status );
714 }
715