mirror of
https://github.com/reactos/reactos.git
synced 2024-08-29 22:58:34 +00:00
666 lines
16 KiB
C
666 lines
16 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Win32k subsystem
|
|
* PURPOSE: Window scrolling function
|
|
* FILE: win32ss/user/ntuser/scrollex.c
|
|
* PROGRAMER: Filip Navara (xnavara@volny.cz)
|
|
*/
|
|
|
|
#include <win32k.h>
|
|
|
|
DBG_DEFAULT_CHANNEL(UserPainting);
|
|
|
|
static
|
|
HWND FASTCALL
|
|
co_IntFixCaret(PWND Window, RECTL *lprc, UINT flags)
|
|
{
|
|
PTHRDCARETINFO CaretInfo;
|
|
PTHREADINFO pti;
|
|
PUSER_MESSAGE_QUEUE ThreadQueue;
|
|
HWND hWndCaret;
|
|
PWND WndCaret;
|
|
|
|
ASSERT_REFS_CO(Window);
|
|
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
ThreadQueue = pti->MessageQueue;
|
|
CaretInfo = &ThreadQueue->CaretInfo;
|
|
hWndCaret = CaretInfo->hWnd;
|
|
|
|
WndCaret = ValidateHwndNoErr(hWndCaret);
|
|
|
|
// FIXME: Check for WndCaret can be NULL
|
|
if (WndCaret == Window ||
|
|
((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
|
|
{
|
|
POINT pt, FromOffset, ToOffset;
|
|
RECTL rcCaret;
|
|
|
|
pt.x = CaretInfo->Pos.x;
|
|
pt.y = CaretInfo->Pos.y;
|
|
IntGetClientOrigin(WndCaret, &FromOffset);
|
|
IntGetClientOrigin(Window, &ToOffset);
|
|
rcCaret.left = pt.x;
|
|
rcCaret.top = pt.y;
|
|
rcCaret.right = pt.x + CaretInfo->Size.cx;
|
|
rcCaret.bottom = pt.y + CaretInfo->Size.cy;
|
|
if (RECTL_bIntersectRect(lprc, lprc, &rcCaret))
|
|
{
|
|
co_UserHideCaret(0);
|
|
lprc->left = pt.x;
|
|
lprc->top = pt.y;
|
|
return hWndCaret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Old GetUpdateRgn, for scrolls, see above note.
|
|
*/
|
|
INT FASTCALL
|
|
co_IntGetUpdateRgn(PWND Window, PREGION Rgn, BOOL bErase)
|
|
{
|
|
int RegionType;
|
|
RECTL Rect;
|
|
PREGION UpdateRgn;
|
|
|
|
ASSERT_REFS_CO(Window);
|
|
|
|
if (bErase)
|
|
{
|
|
co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE);
|
|
}
|
|
|
|
Window->state &= ~WNDS_UPDATEDIRTY;
|
|
|
|
if (Window->hrgnUpdate == NULL)
|
|
{
|
|
REGION_SetRectRgn(Rgn, 0, 0, 0, 0);
|
|
return NULLREGION;
|
|
}
|
|
|
|
UpdateRgn = REGION_LockRgn(Window->hrgnUpdate);
|
|
if (!UpdateRgn)
|
|
return ERROR;
|
|
|
|
Rect = Window->rcClient;
|
|
IntIntersectWithParents(Window, &Rect);
|
|
REGION_SetRectRgn(Rgn, Rect.left, Rect.top, Rect.right, Rect.bottom);
|
|
RegionType = IntGdiCombineRgn(Rgn, Rgn, UpdateRgn, RGN_AND);
|
|
REGION_bOffsetRgn(Rgn, -Window->rcClient.left, -Window->rcClient.top);
|
|
REGION_UnlockRgn(UpdateRgn);
|
|
|
|
return RegionType;
|
|
}
|
|
|
|
static
|
|
INT FASTCALL
|
|
UserScrollDC(
|
|
HDC hDC,
|
|
INT dx,
|
|
INT dy,
|
|
const RECTL *prcScroll,
|
|
const RECTL *prcClip,
|
|
HRGN hrgnUpdate,
|
|
PREGION RgnUpdate,
|
|
RECTL *prcUpdate)
|
|
{
|
|
PDC pDC;
|
|
RECTL rcScroll, rcClip, rcSrc, rcDst;
|
|
INT Result;
|
|
|
|
if (GdiGetClipBox(hDC, &rcClip) == ERROR)
|
|
{
|
|
ERR("GdiGetClipBox failed for HDC %p\n", hDC);
|
|
return ERROR;
|
|
}
|
|
|
|
rcScroll = rcClip;
|
|
if (prcClip)
|
|
{
|
|
RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
|
|
}
|
|
|
|
if (prcScroll)
|
|
{
|
|
rcScroll = *prcScroll;
|
|
RECTL_bIntersectRect(&rcSrc, &rcClip, prcScroll);
|
|
}
|
|
else
|
|
{
|
|
rcSrc = rcClip;
|
|
}
|
|
|
|
rcDst = rcSrc;
|
|
RECTL_vOffsetRect(&rcDst, dx, dy);
|
|
RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
|
|
|
|
if (!NtGdiBitBlt( hDC,
|
|
rcDst.left,
|
|
rcDst.top,
|
|
rcDst.right - rcDst.left,
|
|
rcDst.bottom - rcDst.top,
|
|
hDC,
|
|
rcDst.left - dx,
|
|
rcDst.top - dy,
|
|
SRCCOPY,
|
|
0,
|
|
0))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* Calculate the region that was invalidated by moving or
|
|
could not be copied, because it was not visible */
|
|
if (RgnUpdate || hrgnUpdate || prcUpdate)
|
|
{
|
|
PREGION RgnOwn, RgnTmp;
|
|
|
|
pDC = DC_LockDc(hDC);
|
|
if (!pDC)
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
if (hrgnUpdate)
|
|
{
|
|
NT_ASSERT(RgnUpdate == NULL);
|
|
RgnUpdate = REGION_LockRgn(hrgnUpdate);
|
|
if (!RgnUpdate)
|
|
{
|
|
DC_UnlockDc(pDC);
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
/* Begin with the shifted and then clipped scroll rect */
|
|
rcDst = rcScroll;
|
|
RECTL_vOffsetRect(&rcDst, dx, dy);
|
|
RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
|
|
if (RgnUpdate)
|
|
{
|
|
RgnOwn = RgnUpdate;
|
|
REGION_SetRectRgn(RgnOwn, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom);
|
|
}
|
|
else
|
|
{
|
|
RgnOwn = IntSysCreateRectpRgnIndirect(&rcDst);
|
|
}
|
|
|
|
/* Add the source rect */
|
|
RgnTmp = IntSysCreateRectpRgnIndirect(&rcSrc);
|
|
IntGdiCombineRgn(RgnOwn, RgnOwn, RgnTmp, RGN_OR);
|
|
|
|
/* Substract the part of the dest that was visible in source */
|
|
IntGdiCombineRgn(RgnTmp, RgnTmp, pDC->prgnVis, RGN_AND);
|
|
REGION_bOffsetRgn(RgnTmp, dx, dy);
|
|
Result = IntGdiCombineRgn(RgnOwn, RgnOwn, RgnTmp, RGN_DIFF);
|
|
|
|
/* DO NOT Unlock DC while messing with prgnVis! */
|
|
DC_UnlockDc(pDC);
|
|
|
|
REGION_Delete(RgnTmp);
|
|
|
|
if (prcUpdate)
|
|
{
|
|
REGION_GetRgnBox(RgnOwn, prcUpdate);
|
|
}
|
|
|
|
if (hrgnUpdate)
|
|
{
|
|
REGION_UnlockRgn(RgnUpdate);
|
|
}
|
|
else if (!RgnUpdate)
|
|
{
|
|
REGION_Delete(RgnOwn);
|
|
}
|
|
}
|
|
else
|
|
Result = NULLREGION;
|
|
|
|
return Result;
|
|
}
|
|
|
|
DWORD
|
|
FASTCALL
|
|
IntScrollWindowEx(
|
|
PWND Window,
|
|
INT dx,
|
|
INT dy,
|
|
const RECT *prcScroll,
|
|
const RECT *prcClip,
|
|
HRGN hrgnUpdate,
|
|
LPRECT prcUpdate,
|
|
UINT flags)
|
|
{
|
|
INT Result;
|
|
RECTL rcScroll, rcClip, rcCaret;
|
|
PWND CaretWnd;
|
|
HDC hDC;
|
|
PREGION RgnUpdate = NULL, RgnTemp, RgnWinupd = NULL;
|
|
HWND hwndCaret;
|
|
DWORD dcxflags = 0;
|
|
int rdw_flags;
|
|
USER_REFERENCE_ENTRY CaretRef;
|
|
|
|
if (!Window || !IntIsWindowDrawable(Window))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
IntGetClientRect(Window, &rcClip);
|
|
|
|
if (prcScroll)
|
|
RECTL_bIntersectRect(&rcScroll, &rcClip, prcScroll);
|
|
else
|
|
rcScroll = rcClip;
|
|
|
|
if (prcClip)
|
|
RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
|
|
|
|
if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
|
|
(dx == 0 && dy == 0))
|
|
{
|
|
return NULLREGION;
|
|
}
|
|
|
|
/* We must use a copy of the region, as we can't hold an exclusive lock
|
|
* on it while doing callouts to user-mode */
|
|
RgnUpdate = IntSysCreateRectpRgn(0, 0, 0, 0);
|
|
if(!RgnUpdate)
|
|
{
|
|
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return ERROR;
|
|
}
|
|
|
|
if (hrgnUpdate)
|
|
{
|
|
RgnTemp = REGION_LockRgn(hrgnUpdate);
|
|
if (!RgnTemp)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_HANDLE);
|
|
Result = ERROR;
|
|
goto Cleanup;
|
|
}
|
|
IntGdiCombineRgn(RgnUpdate, RgnTemp, NULL, RGN_COPY);
|
|
REGION_UnlockRgn(RgnTemp);
|
|
}
|
|
|
|
/* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
|
|
if (flags & SW_SCROLLWNDDCE)
|
|
{
|
|
dcxflags = DCX_USESTYLE;
|
|
|
|
if (!(Window->pcls->style & (CS_OWNDC|CS_CLASSDC)))
|
|
dcxflags |= DCX_CACHE; // AH??? wine~ If not Powned or with Class go Cheap!
|
|
|
|
if (flags & SW_SCROLLCHILDREN && Window->style & WS_CLIPCHILDREN)
|
|
dcxflags |= DCX_CACHE|DCX_NOCLIPCHILDREN;
|
|
}
|
|
else
|
|
{
|
|
/* So in this case ScrollWindowEx uses Cache DC. */
|
|
dcxflags = DCX_CACHE|DCX_USESTYLE;
|
|
if (flags & SW_SCROLLCHILDREN) dcxflags |= DCX_NOCLIPCHILDREN;
|
|
}
|
|
|
|
hDC = UserGetDCEx(Window, 0, dcxflags);
|
|
if (!hDC)
|
|
{
|
|
/* FIXME: SetLastError? */
|
|
Result = ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ? RDW_INVALIDATE | RDW_ERASE : RDW_INVALIDATE ;
|
|
|
|
rcCaret = rcScroll;
|
|
hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
|
|
|
|
Result = UserScrollDC( hDC,
|
|
dx,
|
|
dy,
|
|
&rcScroll,
|
|
&rcClip,
|
|
NULL,
|
|
RgnUpdate,
|
|
prcUpdate);
|
|
|
|
UserReleaseDC(Window, hDC, FALSE);
|
|
|
|
/*
|
|
* Take into account the fact that some damage may have occurred during
|
|
* the scroll. Keep a copy in hrgnWinupd to be added to hrngUpdate at the end.
|
|
*/
|
|
|
|
RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0);
|
|
if (!RgnTemp)
|
|
{
|
|
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
Result = ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (co_IntGetUpdateRgn(Window, RgnTemp, FALSE) != NULLREGION)
|
|
{
|
|
PREGION RgnClip = IntSysCreateRectpRgnIndirect(&rcClip);
|
|
if (RgnClip)
|
|
{
|
|
if (hrgnUpdate)
|
|
{
|
|
RgnWinupd = IntSysCreateRectpRgn(0, 0, 0, 0);
|
|
// FIXME: What to do if RgnWinupd == NULL??
|
|
IntGdiCombineRgn( RgnWinupd, RgnTemp, 0, RGN_COPY);
|
|
}
|
|
|
|
REGION_bOffsetRgn(RgnTemp, dx, dy);
|
|
|
|
IntGdiCombineRgn(RgnTemp, RgnTemp, RgnClip, RGN_AND);
|
|
|
|
if (hrgnUpdate)
|
|
IntGdiCombineRgn( RgnWinupd, RgnWinupd, RgnTemp, RGN_OR );
|
|
|
|
co_UserRedrawWindow(Window, NULL, RgnTemp, rdw_flags );
|
|
|
|
REGION_Delete(RgnClip);
|
|
}
|
|
}
|
|
REGION_Delete(RgnTemp);
|
|
|
|
if (flags & SW_SCROLLCHILDREN)
|
|
{
|
|
PWND Child;
|
|
RECTL rcChild;
|
|
POINT ClientOrigin;
|
|
USER_REFERENCE_ENTRY WndRef;
|
|
RECTL rcDummy;
|
|
LPARAM lParam;
|
|
|
|
IntGetClientOrigin(Window, &ClientOrigin);
|
|
|
|
for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
|
|
{
|
|
rcChild = Child->rcWindow;
|
|
RECTL_vOffsetRect(&rcChild, -ClientOrigin.x, -ClientOrigin.y);
|
|
|
|
/* Adjust window positions */
|
|
RECTL_vOffsetRect(&Child->rcWindow, dx, dy);
|
|
RECTL_vOffsetRect(&Child->rcClient, dx, dy);
|
|
|
|
if (!prcScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
|
|
{
|
|
UserRefObjectCo(Child, &WndRef);
|
|
|
|
if (UserIsDesktopWindow(Window->spwndParent))
|
|
lParam = MAKELONG(Child->rcClient.left, Child->rcClient.top);
|
|
else
|
|
lParam = MAKELONG(rcChild.left + dx, rcChild.top + dy);
|
|
|
|
/* wine sends WM_POSCHANGING, WM_POSCHANGED messages */
|
|
/* windows sometimes a WM_MOVE */
|
|
co_IntSendMessage(UserHMGetHandle(Child), WM_MOVE, 0, lParam);
|
|
|
|
UserDerefObjectCo(Child);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flags & (SW_INVALIDATE | SW_ERASE))
|
|
{
|
|
co_UserRedrawWindow( Window,
|
|
NULL,
|
|
RgnUpdate,
|
|
rdw_flags | /* HACK */
|
|
((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : RDW_NOCHILDREN) );
|
|
}
|
|
|
|
if (hwndCaret && (CaretWnd = UserGetWindowObject(hwndCaret)))
|
|
{
|
|
UserRefObjectCo(CaretWnd, &CaretRef);
|
|
|
|
co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
|
|
co_UserShowCaret(CaretWnd);
|
|
|
|
UserDerefObjectCo(CaretWnd);
|
|
}
|
|
|
|
if (hrgnUpdate && (Result != ERROR))
|
|
{
|
|
/* Give everything back to the caller */
|
|
RgnTemp = REGION_LockRgn(hrgnUpdate);
|
|
/* The handle should still be valid */
|
|
ASSERT(RgnTemp);
|
|
if (RgnWinupd)
|
|
IntGdiCombineRgn(RgnTemp, RgnUpdate, RgnWinupd, RGN_OR);
|
|
else
|
|
IntGdiCombineRgn(RgnTemp, RgnUpdate, NULL, RGN_COPY);
|
|
REGION_UnlockRgn(RgnTemp);
|
|
}
|
|
|
|
Cleanup:
|
|
if (RgnWinupd)
|
|
{
|
|
REGION_Delete(RgnWinupd);
|
|
}
|
|
|
|
if (RgnUpdate)
|
|
{
|
|
REGION_Delete(RgnUpdate);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
BOOL FASTCALL
|
|
IntScrollWindow(PWND pWnd,
|
|
int dx,
|
|
int dy,
|
|
CONST RECT *lpRect,
|
|
CONST RECT *prcClip)
|
|
{
|
|
return IntScrollWindowEx( pWnd, dx, dy, lpRect, prcClip, 0, NULL,
|
|
(lpRect ? 0 : SW_SCROLLCHILDREN) | (SW_ERASE|SW_INVALIDATE|SW_SCROLLWNDDCE)) != ERROR;
|
|
}
|
|
|
|
/*
|
|
* NtUserScrollDC
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
BOOL APIENTRY
|
|
NtUserScrollDC(
|
|
HDC hDC,
|
|
INT dx,
|
|
INT dy,
|
|
const RECT *prcUnsafeScroll,
|
|
const RECT *prcUnsafeClip,
|
|
HRGN hrgnUpdate,
|
|
LPRECT prcUnsafeUpdate)
|
|
{
|
|
DECLARE_RETURN(DWORD);
|
|
DWORD Result;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RECTL rcScroll, rcClip, rcUpdate;
|
|
|
|
TRACE("Enter NtUserScrollDC\n");
|
|
UserEnterExclusive();
|
|
|
|
_SEH2_TRY
|
|
{
|
|
if (prcUnsafeScroll)
|
|
{
|
|
ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
|
|
rcScroll = *prcUnsafeScroll;
|
|
}
|
|
if (prcUnsafeClip)
|
|
{
|
|
ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
|
|
rcClip = *prcUnsafeClip;
|
|
}
|
|
if (prcUnsafeUpdate)
|
|
{
|
|
ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
Result = UserScrollDC( hDC,
|
|
dx,
|
|
dy,
|
|
prcUnsafeScroll ? &rcScroll : NULL,
|
|
prcUnsafeClip ? &rcClip : NULL,
|
|
hrgnUpdate,
|
|
NULL,
|
|
prcUnsafeUpdate ? &rcUpdate : NULL);
|
|
if(Result == ERROR)
|
|
{
|
|
/* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
if (prcUnsafeUpdate)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
*prcUnsafeUpdate = rcUpdate;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* FIXME: SetLastError? */
|
|
/* FIXME: correct? We have already scrolled! */
|
|
RETURN(FALSE);
|
|
}
|
|
}
|
|
|
|
RETURN(TRUE);
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserScrollDC, ret=%lu\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
/*
|
|
* NtUserScrollWindowEx
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
DWORD APIENTRY
|
|
NtUserScrollWindowEx(
|
|
HWND hWnd,
|
|
INT dx,
|
|
INT dy,
|
|
const RECT *prcUnsafeScroll,
|
|
const RECT *prcUnsafeClip,
|
|
HRGN hrgnUpdate,
|
|
LPRECT prcUnsafeUpdate,
|
|
UINT flags)
|
|
{
|
|
DECLARE_RETURN(DWORD);
|
|
INT Result;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PWND Window = NULL;
|
|
RECTL rcScroll, rcClip, rcUpdate;
|
|
USER_REFERENCE_ENTRY Ref;
|
|
|
|
TRACE("Enter NtUserScrollWindowEx\n");
|
|
UserEnterExclusive();
|
|
|
|
Window = UserGetWindowObject(hWnd);
|
|
if (!Window || !IntIsWindowDrawable(Window))
|
|
{
|
|
Window = NULL; /* prevent deref at cleanup */
|
|
RETURN(ERROR);
|
|
}
|
|
UserRefObjectCo(Window, &Ref);
|
|
|
|
_SEH2_TRY
|
|
{
|
|
if (prcUnsafeScroll)
|
|
{
|
|
ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
|
|
rcScroll = *prcUnsafeScroll;
|
|
}
|
|
|
|
if (prcUnsafeClip)
|
|
{
|
|
ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
|
|
rcClip = *prcUnsafeClip;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
RETURN(ERROR);
|
|
}
|
|
|
|
Result = IntScrollWindowEx(Window,
|
|
dx, dy,
|
|
prcUnsafeScroll ? &rcScroll : NULL,
|
|
prcUnsafeClip ? &rcClip : NULL,
|
|
hrgnUpdate,
|
|
prcUnsafeUpdate ? &rcUpdate : NULL,
|
|
flags);
|
|
|
|
if (prcUnsafeUpdate)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe here, to not fail on invalid pointer before scrolling */
|
|
ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
|
|
*prcUnsafeUpdate = rcUpdate;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
RETURN(ERROR);
|
|
}
|
|
}
|
|
|
|
RETURN(Result);
|
|
|
|
CLEANUP:
|
|
if (Window)
|
|
UserDerefObjectCo(Window);
|
|
|
|
TRACE("Leave NtUserScrollWindowEx, ret=%lu\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
/* EOF */
|