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