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