- Rewrite usermode callbacks. These changes should greatly optimize graphic operations. After these changes, my "idle" CPU Usage in taskmgr went from 7-8% to 4-5%, while on the performace page, from 15-18% to 10-13%:
  * Do not use ugly and messy code to create linked stacks and other such resource-wasting steps. Use our newly implemented 60KB stack support and MmGrowKernelStack when needed.
  * Write all the low-level code in assembly instead of relying on structures and hodgepodge code.
  * Add debugging/detection features for invalid calls, such as invalid IRQL, APCs being disabled, invalid previous mode detection (this allowed me to fix the KWAIT_BLOCK bug today).
  * Finally fix the last (I hope) remaning trap frame bug issue related to V86 mode bias. One of the "hacks" in syscall.S has already been removed and I can now do the promised cleanup.
  * Allow some failulre cases in callbacks (not all implemented) and extend stack space for future use of SEH in the ntdll dispatcher.
  * Fix win32k to use callbacks properly: the system fills out *Result and *ResultLength, not the caller.
  * Use SEH (ProbeForWrite) in callbacks to detect invalid user-mode memory.
  * Save NPX State and ExceptionList across callbacks (I think this wasn't fully properly done in all cases).
Modified: trunk/reactos/ntoskrnl/ke/i386/syscall.S
Modified: trunk/reactos/ntoskrnl/ke/i386/usercall_asm.S
Modified: trunk/reactos/ntoskrnl/ke/usercall.c
Modified: trunk/reactos/ntoskrnl/ke/wait.c
Modified: trunk/reactos/ntoskrnl/ps/psmgr.c
Modified: trunk/reactos/ntoskrnl/ps/win32.c
Modified: trunk/reactos/subsys/win32k/ntuser/callback.c

Modified: trunk/reactos/ntoskrnl/ke/i386/syscall.S
--- trunk/reactos/ntoskrnl/ke/i386/syscall.S	2006-01-11 23:22:15 UTC (rev 20793)
+++ trunk/reactos/ntoskrnl/ke/i386/syscall.S	2006-01-11 23:54:44 UTC (rev 20794)
@@ -470,23 +470,14 @@
     ja RestoreAll
 // ==================== END IF FULL RESTORE NEEDED ====================//
 
- /* Skip debug information and unsaved registers */
- //badbadbad
-    add esp, 0x30
-    pop gs
-    pop es
-    pop ds
-    add esp, 0x14
-//badbadbad
-
     /* Restore FS */
 RestoreFs:
-    //lea esp, [ebp+KTRAP_FRAME_FS] <= BUG IN WIN32K CALLBACKS! STACK GETS SMASHED
+    lea esp, [ebp+KTRAP_FRAME_FS]
     pop fs
 
 CommonStackClean:
     /* Skip debug information and unsaved registers */
-    //lea esp, [ebp+KTRAP_FRAME_EDI] <= BUG IN WIN32K CALLBACKS! STACK GETS SMASHED
+    lea esp, [ebp+KTRAP_FRAME_EDI]
     pop edi
     pop esi
     pop ebx

Modified: trunk/reactos/ntoskrnl/ke/i386/usercall_asm.S
--- trunk/reactos/ntoskrnl/ke/i386/usercall_asm.S	2006-01-11 23:22:15 UTC (rev 20793)
+++ trunk/reactos/ntoskrnl/ke/i386/usercall_asm.S	2006-01-11 23:54:44 UTC (rev 20794)
@@ -248,9 +248,9 @@
  * @remark This call MUST be paired with KeUserModeCallback.
  *
  *--*/
-.globl _NtCallbackReturn2@12
-.func NtCallbackReturn2@12
-_NtCallbackReturn2@12:
+.globl _NtCallbackReturn@12
+.func NtCallbackReturn@12
+_NtCallbackReturn@12:
 
     /* Get the current thread and make sure we have a callback stack */
     mov eax, fs:[KPCR_CURRENT_THREAD]

Modified: trunk/reactos/ntoskrnl/ke/usercall.c
--- trunk/reactos/ntoskrnl/ke/usercall.c	2006-01-11 23:22:15 UTC (rev 20793)
+++ trunk/reactos/ntoskrnl/ke/usercall.c	2006-01-11 23:54:44 UTC (rev 20794)
@@ -3,7 +3,6 @@
  * PROJECT:         ReactOS kernel
  * FILE:            ntoskrnl/ke/usercall.c
  * PURPOSE:         User-Mode callbacks. Portable part.
- *
  * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
  */
 
@@ -13,149 +12,21 @@
 #define NDEBUG
 #include <internal/debug.h>
 
-#if defined (ALLOC_PRAGMA)
-#pragma alloc_text(INIT, PsInitialiseW32Call)
-#endif
-
-/* FUNCTIONS *****************************************************************/
-
-#if ALEX_CB_REWRITE
-
 NTSTATUS
 STDCALL
-KiSwitchToUserMode(IN PVOID *OutputBuffer,
-                   IN PULONG OutputLength);
+KiCallUserMode(
+    IN PVOID *OutputBuffer,
+    IN PULONG OutputLength
+);
 
-#else
+PULONG
+STDCALL
+KiGetUserModeStackAddress(
+    VOID
+);
 
-typedef struct _NTW32CALL_SAVED_STATE
-{
-  ULONG_PTR SavedStackLimit;
-  PVOID SavedStackBase;
-  PVOID SavedInitialStack;
-  PVOID CallerResult;
-  PULONG CallerResultLength;
-  PNTSTATUS CallbackStatus;
-  PKTRAP_FRAME SavedTrapFrame;
-  PVOID SavedCallbackStack;
-  PVOID SavedExceptionStack;
-} NTW32CALL_SAVED_STATE, *PNTW32CALL_SAVED_STATE;
+/* FUNCTIONS *****************************************************************/
 
-typedef struct
-{
-  PVOID BaseAddress;
-  LIST_ENTRY ListEntry;
-} NTW32CALL_CALLBACK_STACK, *PNTW32CALL_CALLBACK_STACK;
-
-KSPIN_LOCK CallbackStackListLock;
-static LIST_ENTRY CallbackStackListHead;
-
-VOID 
-INIT_FUNCTION
-NTAPI
-PsInitialiseW32Call(VOID)
-{
-  InitializeListHead(&CallbackStackListHead);
-  KeInitializeSpinLock(&CallbackStackListLock);
-}
-
-VOID STATIC
-PsFreeCallbackStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
-			PFN_TYPE Page, SWAPENTRY SwapEntry,
-			BOOLEAN Dirty)
-{
-  ASSERT(SwapEntry == 0);
-  if (Page != 0)
-    {
-      MmReleasePageMemoryConsumer(MC_NPPOOL, Page);
-    }
-}
-
-VOID STATIC
-PsFreeCallbackStack(PVOID StackLimit)
-{
-  MmLockAddressSpace(MmGetKernelAddressSpace());
-  MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
-		        StackLimit,
-		        PsFreeCallbackStackPage,
-		        NULL);
-  MmUnlockAddressSpace(MmGetKernelAddressSpace());
-}
-
-VOID
-PsFreeCallbackStacks(VOID)
-{
-  PLIST_ENTRY CurrentListEntry;
-  PNTW32CALL_CALLBACK_STACK Current;
-
-  while (!IsListEmpty(&CallbackStackListHead))
-    {
-      CurrentListEntry = RemoveHeadList(&CallbackStackListHead);
-      Current = CONTAINING_RECORD(CurrentListEntry, NTW32CALL_CALLBACK_STACK,
-				  ListEntry);
-      PsFreeCallbackStack(Current->BaseAddress);
-      ExFreePool(Current);
-    }
-}
-
-PVOID STATIC
-PsAllocateCallbackStack(ULONG StackSize)
-{
-  PVOID KernelStack = NULL;
-  NTSTATUS Status;
-  PMEMORY_AREA StackArea;
-  ULONG i, j;
-  PHYSICAL_ADDRESS BoundaryAddressMultiple;
-  PPFN_TYPE Pages = alloca(sizeof(PFN_TYPE) * (StackSize /PAGE_SIZE));
-
-
-  BoundaryAddressMultiple.QuadPart = 0;
-  StackSize = PAGE_ROUND_UP(StackSize);
-  MmLockAddressSpace(MmGetKernelAddressSpace());
-  Status = MmCreateMemoryArea(MmGetKernelAddressSpace(),
-			      MEMORY_AREA_KERNEL_STACK,
-			      &KernelStack,
-			      StackSize,
-			      PAGE_READWRITE,
-			      &StackArea,
-			      FALSE,
-			      0,
-			      BoundaryAddressMultiple);
-  MmUnlockAddressSpace(MmGetKernelAddressSpace());
-  if (!NT_SUCCESS(Status))
-    {
-      DPRINT("Failed to create thread stack\n");
-      return(NULL);
-    }
-  for (i = 0; i < (StackSize / PAGE_SIZE); i++)
-    {
-      Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Pages[i]);
-      if (!NT_SUCCESS(Status))
-	{
-	  for (j = 0; j < i; j++)
-	  {
-	    MmReleasePageMemoryConsumer(MC_NPPOOL, Pages[j]);
-	  }
-	  return(NULL);
-	}
-    }
-  Status = MmCreateVirtualMapping(NULL,
-				  KernelStack,
-				  PAGE_READWRITE,
-				  Pages,
-				  StackSize / PAGE_SIZE);
-  if (!NT_SUCCESS(Status))
-    {
-      for (i = 0; i < (StackSize / PAGE_SIZE); i++)
-        {
-	  MmReleasePageMemoryConsumer(MC_NPPOOL, Pages[i]);
-	}
-      return(NULL);
-    }
-  return(KernelStack);
-}
-#endif
-
 /*
  * @implemented
  */
@@ -167,92 +38,63 @@
                    OUT PVOID *Result,
                    OUT PULONG ResultLength)
 {
-  PETHREAD Thread;
-  PVOID NewStack;
-  ULONG_PTR StackSize;
-  PKTRAP_FRAME NewFrame;
-  PULONG UserEsp;
-  KIRQL oldIrql;
-  NTSTATUS CallbackStatus;
-  NTW32CALL_SAVED_STATE SavedState;
-  PNTW32CALL_CALLBACK_STACK AssignedStack;
+    ULONG_PTR NewStack, OldStack;
+    PULONG UserEsp;
+    NTSTATUS CallbackStatus;
+    PEXCEPTION_REGISTRATION_RECORD ExceptionList;
+    DPRINT("KeUserModeCallback(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
+            RoutineIndex, Argument, ArgumentLength);
+    ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE);
+    ASSERT(KeGetPreviousMode() == UserMode);
 
-  PAGED_CODE();
+    /* Get the current user-mode stack */
+    UserEsp = KiGetUserModeStackAddress();
+    OldStack = *UserEsp;
 
-  DPRINT("KeUserModeCallback(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
-	  RoutineIndex, Argument, ArgumentLength);
+    /* Enter a SEH Block */
+    _SEH_TRY
+    {
+        /* Calculate and align the stack size */
+        NewStack = (OldStack - ArgumentLength) & ~3;
 
-  Thread = PsGetCurrentThread();
+        /* Make sure it's writable */
+        ProbeForWrite((PVOID)(NewStack - 6 * sizeof(ULONG_PTR)),
+                      ArgumentLength + 6 * sizeof(ULONG_PTR),
+                      sizeof(CHAR));
 
-  /* Set up the new kernel and user environment. */
-  StackSize = (ULONG_PTR)Thread->Tcb.StackBase - Thread->Tcb.StackLimit;
-  KeAcquireSpinLock(&CallbackStackListLock, &oldIrql);
-  if (IsListEmpty(&CallbackStackListHead))
-    {
-      KeReleaseSpinLock(&CallbackStackListLock, oldIrql);
-      NewStack = PsAllocateCallbackStack(StackSize);
-      AssignedStack = ExAllocatePool(NonPagedPool,
-				     sizeof(NTW32CALL_CALLBACK_STACK));
-      AssignedStack->BaseAddress = NewStack;
-    }
-  else
-    {
-      PLIST_ENTRY StackEntry;
+        /* Copy the buffer into the stack */
+        RtlCopyMemory((PVOID)NewStack, Argument, ArgumentLength);
 
-      StackEntry = RemoveHeadList(&CallbackStackListHead);
-      KeReleaseSpinLock(&CallbackStackListLock, oldIrql);
-      AssignedStack = CONTAINING_RECORD(StackEntry, NTW32CALL_CALLBACK_STACK,
-					ListEntry);
-      NewStack = AssignedStack->BaseAddress;
-      RtlZeroMemory(NewStack, StackSize);
-    }
-  /* FIXME: Need to check whether we were interrupted from v86 mode. */
-  RtlCopyMemory((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA),
-                Thread->Tcb.TrapFrame, sizeof(KTRAP_FRAME) - (4 * sizeof(ULONG)));
-  NewFrame = (PKTRAP_FRAME)((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA));
-  /* We need the stack pointer to remain 4-byte aligned */
-  NewFrame->HardwareEsp -= (((ArgumentLength + 3) & (~ 0x3)) + (4 * sizeof(ULONG)));
-  NewFrame->Eip = (ULONG)KeUserCallbackDispatcher;
-  UserEsp = (PULONG)NewFrame->HardwareEsp;
-  UserEsp[0] = 0;     /* Return address. */
-  UserEsp[1] = RoutineIndex;
-  UserEsp[2] = (ULONG)&UserEsp[4];
-  UserEsp[3] = ArgumentLength;
-  RtlCopyMemory((PVOID)&UserEsp[4], Argument, ArgumentLength);
+        /* Write the arguments */
+        NewStack -= 24;
+        *(PULONG)NewStack = 0;
+        *(PULONG)(NewStack + 4) = RoutineIndex;
+        *(PULONG)(NewStack + 8) = (NewStack + 24);
+        *(PULONG)(NewStack + 12) = ArgumentLength;
 
-  /* Switch to the new environment and return to user-mode. */
-  KeRaiseIrql(HIGH_LEVEL, &oldIrql);
-  SavedState.SavedStackLimit = Thread->Tcb.StackLimit;
-  SavedState.SavedStackBase = Thread->Tcb.StackBase;
-  SavedState.SavedInitialStack = Thread->Tcb.InitialStack;
-  SavedState.CallerResult = Result;
-  SavedState.CallerResultLength = ResultLength;
-  SavedState.CallbackStatus = &CallbackStatus;
-  SavedState.SavedTrapFrame = Thread->Tcb.TrapFrame;
-  SavedState.SavedCallbackStack = Thread->Tcb.CallbackStack;
-  SavedState.SavedExceptionStack = (PVOID)KeGetCurrentKPCR()->TSS->Esp0;
-  if ((Thread->Tcb.NpxState & NPX_STATE_VALID) &&
-      &Thread->Tcb != KeGetCurrentPrcb()->NpxThread)
+        /* Save the exception list */
+        ExceptionList = KeGetCurrentThread()->Teb->Tib.ExceptionList;
+
+        /* Jump to user mode */
+        *UserEsp = NewStack;
+        CallbackStatus = KiCallUserMode(Result, ResultLength);
+
+        /* FIXME: Handle user-mode exception status */
+
+        /* Restore exception list */
+        KeGetCurrentThread()->Teb->Tib.ExceptionList = ExceptionList;
+    }
+    _SEH_HANDLE
     {
-      RtlCopyMemory((char*)NewStack + StackSize - sizeof(FX_SAVE_AREA),
-                    (char*)SavedState.SavedInitialStack - sizeof(FX_SAVE_AREA),
-                    sizeof(FX_SAVE_AREA));
+        CallbackStatus = _SEH_GetExceptionCode();
     }
-  Thread->Tcb.InitialStack = Thread->Tcb.StackBase = (char*)NewStack + StackSize;
-  Thread->Tcb.StackLimit = (ULONG)NewStack;
-  Thread->Tcb.KernelStack = (char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA);
-  KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)Thread->Tcb.InitialStack - sizeof(FX_SAVE_AREA) - 0x10;
-  KePushAndStackSwitchAndSysRet((ULONG)&SavedState, Thread->Tcb.KernelStack);
+    _SEH_END;
 
-  /*
-   * The callback return will have already restored most of the state we
-   * modified.
-   */
-  KeLowerIrql(DISPATCH_LEVEL);
-  KeAcquireSpinLockAtDpcLevel(&CallbackStackListLock);
-  InsertTailList(&CallbackStackListHead, &AssignedStack->ListEntry);
-  KeReleaseSpinLock(&CallbackStackListLock, PASSIVE_LEVEL);
-  return(CallbackStatus);
+    /* FIXME: Flush GDI Batch */
+
+    /* Restore stack and return */
+    *UserEsp = OldStack;
+    return CallbackStatus;
 }
 
 /* EOF */

Modified: trunk/reactos/ntoskrnl/ke/wait.c
--- trunk/reactos/ntoskrnl/ke/wait.c	2006-01-11 23:22:15 UTC (rev 20793)
+++ trunk/reactos/ntoskrnl/ke/wait.c	2006-01-11 23:54:44 UTC (rev 20794)
@@ -680,7 +680,7 @@
 
     /* Release & Return */
     DPRINT("Returning, %x. Status: %d\n. We did not wait.",
-            KeGetCurrentThread(), WaitStatus);
+             KeGetCurrentThread(), WaitStatus);
     KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
     return WaitStatus;
 }

Modified: trunk/reactos/ntoskrnl/ps/psmgr.c
--- trunk/reactos/ntoskrnl/ps/psmgr.c	2006-01-11 23:22:15 UTC (rev 20793)
+++ trunk/reactos/ntoskrnl/ps/psmgr.c	2006-01-11 23:54:44 UTC (rev 20794)
@@ -78,7 +78,6 @@
    PsInitProcessManagment();
    PsInitThreadManagment();
    PsInitIdleThread();
-   PsInitialiseW32Call();
 }
 
 VOID

Modified: trunk/reactos/ntoskrnl/ps/win32.c
--- trunk/reactos/ntoskrnl/ps/win32.c	2006-01-11 23:22:15 UTC (rev 20793)
+++ trunk/reactos/ntoskrnl/ps/win32.c	2006-01-11 23:54:44 UTC (rev 20794)
@@ -212,89 +212,4 @@
     return(CallbackStatus);
 }
 
-#ifndef ALEX_CB_REWRITE
-NTSTATUS STDCALL
-NtCallbackReturn (PVOID		Result,
-		  ULONG		ResultLength,
-		  NTSTATUS	Status)
-{
-  PULONG OldStack;
-  PETHREAD Thread;
-  PNTSTATUS CallbackStatus;
-  PULONG CallerResultLength;
-  PVOID* CallerResult;
-  PVOID InitialStack;
-  PVOID StackBase;
-  ULONG_PTR StackLimit;
-  KIRQL oldIrql;
-  PNTW32CALL_SAVED_STATE State;
-  PKTRAP_FRAME SavedTrapFrame;
-  PVOID SavedCallbackStack;
-  PVOID SavedExceptionStack;
-
-  PAGED_CODE();
-
-  Thread = PsGetCurrentThread();
-  if (Thread->Tcb.CallbackStack == NULL)
-    {
-      return(STATUS_NO_CALLBACK_ACTIVE);
-    }
-
-  OldStack = (PULONG)Thread->Tcb.CallbackStack;
-
-  /*
-   * Get the values that NtW32Call left on the inactive stack for us.
-   */
-  State = (PNTW32CALL_SAVED_STATE)OldStack[0];
-  CallbackStatus = State->CallbackStatus;
-  CallerResultLength = State->CallerResultLength;
-  CallerResult = State->CallerResult;
-  InitialStack = State->SavedInitialStack;
-  StackBase = State->SavedStackBase;
-  StackLimit = State->SavedStackLimit;
-  SavedTrapFrame = State->SavedTrapFrame;
-  SavedCallbackStack = State->SavedCallbackStack;
-  SavedExceptionStack = State->SavedExceptionStack;
-
-  /*
-   * Copy the callback status and the callback result to NtW32Call
-   */
-  *CallbackStatus = Status;
-  if (CallerResult != NULL && CallerResultLength != NULL)
-    {
-      if (Result == NULL)
-	{
-	  *CallerResultLength = 0;
-	}
-      else
-	{
-	  *CallerResultLength = min(ResultLength, *CallerResultLength);
-	  RtlCopyMemory(*CallerResult, Result, *CallerResultLength);
-	}
-    }
-
-  /*
-   * Restore the old stack.
-   */
-  KeRaiseIrql(HIGH_LEVEL, &oldIrql);
-  if ((Thread->Tcb.NpxState & NPX_STATE_VALID) &&
-      &Thread->Tcb != KeGetCurrentPrcb()->NpxThread)
-    {
-      RtlCopyMemory((char*)InitialStack - sizeof(FX_SAVE_AREA),
-                    (char*)Thread->Tcb.InitialStack - sizeof(FX_SAVE_AREA),
-                    sizeof(FX_SAVE_AREA));
-    }
-  Thread->Tcb.InitialStack = InitialStack;
-  Thread->Tcb.StackBase = StackBase;
-  Thread->Tcb.StackLimit = StackLimit;
-  Thread->Tcb.TrapFrame = SavedTrapFrame;
-  Thread->Tcb.CallbackStack = SavedCallbackStack;
-  KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)SavedExceptionStack;
-  KeStackSwitchAndRet((PVOID)(OldStack + 1));
-
-  /* Should never return. */
-  KEBUGCHECK(0);
-  return(STATUS_UNSUCCESSFUL);
-}
-#endif
 /* EOF */

Modified: trunk/reactos/subsys/win32k/ntuser/callback.c
--- trunk/reactos/subsys/win32k/ntuser/callback.c	2006-01-11 23:22:15 UTC (rev 20793)
+++ trunk/reactos/subsys/win32k/ntuser/callback.c	2006-01-11 23:54:44 UTC (rev 20794)
@@ -182,7 +182,7 @@
    Arguments->wParam = wParam;
    Arguments->lParam = lParam;
    Arguments->lParamBufferSize = lParamBufferSize;
-   ResultPointer = Arguments;
+   ResultPointer = NULL;
    ResultLength = ArgumentLength;
 
    UserLeaveCo();
@@ -193,6 +193,9 @@
                       &ResultPointer,
                       &ResultLength);
 
+   /* Simulate old behaviour: copy into our local buffer */
+   RtlMoveMemory(Arguments, ResultPointer, ArgumentLength);
+
    UserEnterCo();
 
    if (!NT_SUCCESS(Status))
@@ -224,7 +227,7 @@
    PVOID ResultPointer;
    ULONG ResultLength;
 
-   ResultPointer = &Result;
+   ResultPointer = NULL;
    ResultLength = sizeof(LRESULT);
 
    UserLeaveCo();
@@ -235,6 +238,9 @@
                       &ResultPointer,
                       &ResultLength);
 
+   /* Simulate old behaviour: copy into our local buffer */
+   Result = *(LRESULT*)ResultPointer;
+
    UserEnterCo();
 
    if (!NT_SUCCESS(Status))
@@ -253,7 +259,7 @@
    ULONG ResultLength;
    BOOL DefaultCursor = TRUE;
 
-   ResultPointer = &Result;
+   ResultPointer = NULL;
    ResultLength = sizeof(LRESULT);
 
    UserLeaveCo();
@@ -264,6 +270,9 @@
                       &ResultPointer,
                       &ResultLength);
 
+   /* Simulate old behaviour: copy into our local buffer */
+   Result = *(LRESULT*)ResultPointer;
+
    UserEnterCo();
 
    if (!NT_SUCCESS(Status))
@@ -384,7 +393,7 @@
          break;
    }
 
-   ResultPointer = &Result;
+   ResultPointer = NULL;
    ResultLength = sizeof(LRESULT);
 
    UserLeaveCo();
@@ -395,6 +404,9 @@
                       &ResultPointer,
                       &ResultLength);
 
+   /* Simulate old behaviour: copy into our local buffer */
+   Result = *(LRESULT*)ResultPointer;
+
    UserEnterCo();
 
    IntCbFreeMemory(Argument);