/*
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS user32.dll
 * FILE:            win32ss/user/user32/windows/window.c
 * PURPOSE:         Window management
 * PROGRAMMERS:     Casper S. Hornstrup (chorns@users.sourceforge.net)
 *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
 * UPDATE HISTORY:
 *      06-06-2001  CSH  Created
 */

#define DEBUG
#include <user32.h>

WINE_DEFAULT_DEBUG_CHANNEL(user32);

void MDI_CalcDefaultChildPos( HWND hwndClient, INT total, LPPOINT lpPos, INT delta, UINT *id );
extern LPCWSTR FASTCALL ClassNameToVersion(const void *lpszClass, LPCWSTR lpszMenuName, LPCWSTR *plpLibFileName, HANDLE *pContext, BOOL bAnsi);

/* FUNCTIONS *****************************************************************/


NTSTATUS WINAPI
User32CallSendAsyncProcForKernel(PVOID Arguments, ULONG ArgumentLength)
{
    PSENDASYNCPROC_CALLBACK_ARGUMENTS CallbackArgs;

    TRACE("User32CallSendAsyncProcKernel()\n");

    CallbackArgs = (PSENDASYNCPROC_CALLBACK_ARGUMENTS)Arguments;

    if (ArgumentLength != sizeof(SENDASYNCPROC_CALLBACK_ARGUMENTS))
    {
        return(STATUS_INFO_LENGTH_MISMATCH);
    }

    CallbackArgs->Callback(CallbackArgs->Wnd,
                           CallbackArgs->Msg,
                           CallbackArgs->Context,
                           CallbackArgs->Result);
    return(STATUS_SUCCESS);
}


/*
 * @implemented
 */
BOOL WINAPI
AllowSetForegroundWindow(DWORD dwProcessId)
{
    return NtUserxAllowSetForegroundWindow(dwProcessId);
}


/*
 * @unimplemented
 */
HDWP WINAPI
BeginDeferWindowPos(int nNumWindows)
{
    return NtUserxBeginDeferWindowPos(nNumWindows);
}


/*
 * @implemented
 */
BOOL WINAPI
BringWindowToTop(HWND hWnd)
{
    return NtUserSetWindowPos(hWnd,
                              HWND_TOP,
                              0,
                              0,
                              0,
                              0,
                              SWP_NOSIZE | SWP_NOMOVE);
}


VOID WINAPI
SwitchToThisWindow(HWND hwnd, BOOL fAltTab)
{
    NtUserxSwitchToThisWindow(hwnd, fAltTab);
}


/*
 * @implemented
 */
HWND WINAPI
ChildWindowFromPoint(HWND hWndParent,
                     POINT Point)
{
    return (HWND) NtUserChildWindowFromPointEx(hWndParent, Point.x, Point.y, 0);
}


/*
 * @implemented
 */
HWND WINAPI
ChildWindowFromPointEx(HWND hwndParent,
                       POINT pt,
                       UINT uFlags)
{
    return (HWND) NtUserChildWindowFromPointEx(hwndParent, pt.x, pt.y, uFlags);
}


/*
 * @implemented
 */
BOOL WINAPI
CloseWindow(HWND hWnd)
{
    /* NOTE: CloseWindow does minimizes, and doesn't close. */
    SetActiveWindow(hWnd);
    return ShowWindow(hWnd, SW_SHOWMINIMIZED);
}

FORCEINLINE
VOID
RtlInitLargeString(
    OUT PLARGE_STRING plstr,
    LPCVOID psz,
    BOOL bUnicode)
{
    if(bUnicode)
    {
        RtlInitLargeUnicodeString((PLARGE_UNICODE_STRING)plstr, (PWSTR)psz, 0);
    }
    else
    {
        RtlInitLargeAnsiString((PLARGE_ANSI_STRING)plstr, (PSTR)psz, 0);
    }
}

VOID
NTAPI
RtlFreeLargeString(
    IN PLARGE_STRING LargeString)
{
    if (LargeString->Buffer)
    {
        RtlFreeHeap(GetProcessHeap(), 0, LargeString->Buffer);
        RtlZeroMemory(LargeString, sizeof(LARGE_STRING));
    }
}

DWORD
FASTCALL
RtlGetExpWinVer( HMODULE hModule )
{
    DWORD dwMajorVersion = 3;  // Set default to Windows 3.10.
    DWORD dwMinorVersion = 10;
    PIMAGE_NT_HEADERS pinth;

    if ( hModule && !((ULONG_PTR)hModule >> 16))
    {
        pinth = RtlImageNtHeader( hModule );

        dwMajorVersion = pinth->OptionalHeader.MajorSubsystemVersion;

        if ( dwMajorVersion == 1 )
        {
            dwMajorVersion = 3;
        }
        else
        {
            dwMinorVersion = pinth->OptionalHeader.MinorSubsystemVersion;
        }
    }
    return MAKELONG(MAKEWORD(dwMinorVersion, dwMajorVersion), 0);
}

HWND WINAPI
User32CreateWindowEx(DWORD dwExStyle,
                     LPCSTR lpClassName,
                     LPCSTR lpWindowName,
                     DWORD dwStyle,
                     int x,
                     int y,
                     int nWidth,
                     int nHeight,
                     HWND hWndParent,
                     HMENU hMenu,
                     HINSTANCE hInstance,
                     LPVOID lpParam,
                     DWORD dwFlags)
{
    LARGE_STRING WindowName;
    LARGE_STRING lstrClassName, *plstrClassName;
    LARGE_STRING lstrClassVersion, *plstrClassVersion;
    UNICODE_STRING ClassName;
    UNICODE_STRING ClassVersion;
    WNDCLASSEXA wceA;
    WNDCLASSEXW wceW;
    HMODULE hLibModule = NULL;
    DWORD dwLastError;
    BOOL Unicode, ClassFound = FALSE;
    HWND Handle = NULL;
    LPCWSTR lpszClsVersion;
    LPCWSTR lpLibFileName = NULL;
    HANDLE pCtx = NULL;
    DWORD dwFlagsVer;

#if 0
    DbgPrint("[window] User32CreateWindowEx style %d, exstyle %d, parent %d\n", dwStyle, dwExStyle, hWndParent);
#endif

    dwFlagsVer = RtlGetExpWinVer( hInstance ? hInstance : GetModuleHandleW(NULL) );
    TRACE("Module Version %x\n",dwFlagsVer);

    if (!RegisterDefaultClasses)
    {
        TRACE("RegisterSystemControls\n");
        RegisterSystemControls();
    }

    Unicode = !(dwFlags & NUCWE_ANSI);

    if (IS_ATOM(lpClassName))
    {
        plstrClassName = (PVOID)lpClassName;
    }
    else
    {
        if (Unicode)
        {
            RtlInitUnicodeString(&ClassName, (PCWSTR)lpClassName);
        }
        else
        {
            if (!RtlCreateUnicodeStringFromAsciiz(&ClassName, (PCSZ)lpClassName))
            {
                SetLastError(ERROR_OUTOFMEMORY);
                return NULL;
            }
        }

        /* Copy it to a LARGE_STRING */
        lstrClassName.Buffer = ClassName.Buffer;
        lstrClassName.Length = ClassName.Length;
        lstrClassName.MaximumLength = ClassName.MaximumLength;
        plstrClassName = &lstrClassName;
    }

    /* Initialize a LARGE_STRING */
    RtlInitLargeString(&WindowName, lpWindowName, Unicode);

    // HACK: The current implementation expects the Window name to be UNICODE
    if (!Unicode)
    {
        NTSTATUS Status;
        PSTR AnsiBuffer = WindowName.Buffer;
        ULONG AnsiLength = WindowName.Length;

        WindowName.Length = 0;
        WindowName.MaximumLength = AnsiLength * sizeof(WCHAR);
        WindowName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
                                            0,
                                            WindowName.MaximumLength);
        if (!WindowName.Buffer)
        {
            SetLastError(ERROR_OUTOFMEMORY);
            goto cleanup;
        }

        Status = RtlMultiByteToUnicodeN(WindowName.Buffer,
                                        WindowName.MaximumLength,
                                        &WindowName.Length,
                                        AnsiBuffer,
                                        AnsiLength);
        if (!NT_SUCCESS(Status))
        {
            goto cleanup;
        }
    }

    if (!hMenu && (dwStyle & (WS_OVERLAPPEDWINDOW | WS_POPUP)))
    {
        if (Unicode)
        {
            wceW.cbSize = sizeof(wceW);
            if (GetClassInfoExW(hInstance, (LPCWSTR)lpClassName, &wceW) && wceW.lpszMenuName)
            {
                hMenu = LoadMenuW(hInstance, wceW.lpszMenuName);
            }
        }
        else
        {
            wceA.cbSize = sizeof(wceA);
            if (GetClassInfoExA(hInstance, lpClassName, &wceA) && wceA.lpszMenuName)
            {
                hMenu = LoadMenuA(hInstance, wceA.lpszMenuName);
            }
        }
    }

    if (!Unicode) dwExStyle |= WS_EX_SETANSICREATOR;

    lpszClsVersion = ClassNameToVersion(lpClassName, NULL, &lpLibFileName, &pCtx, !Unicode);
    if (!lpszClsVersion)
    {
        plstrClassVersion = plstrClassName;
    }
    else
    {
        RtlInitUnicodeString(&ClassVersion, lpszClsVersion);
        lstrClassVersion.Buffer = ClassVersion.Buffer;
        lstrClassVersion.Length = ClassVersion.Length;
        lstrClassVersion.MaximumLength = ClassVersion.MaximumLength;
        plstrClassVersion = &lstrClassVersion;
    }

    for (;;)
    {
        Handle = NtUserCreateWindowEx(dwExStyle,
                                      plstrClassName,
                                      plstrClassVersion,
                                      &WindowName,
                                      dwStyle,
                                      x,
                                      y,
                                      nWidth,
                                      nHeight,
                                      hWndParent,
                                      hMenu,
                                      hInstance,
                                      lpParam,
                                      dwFlagsVer,
                                      pCtx );
        if (Handle) break;
        if (!lpLibFileName) break;
        if (!ClassFound)
        {
            dwLastError = GetLastError();
            if (dwLastError == ERROR_CANNOT_FIND_WND_CLASS)
            {
                ClassFound = VersionRegisterClass(ClassName.Buffer, lpLibFileName, pCtx, &hLibModule);
                if (ClassFound) continue;
            }
        }
        if (hLibModule)
        {
            dwLastError = GetLastError();
            FreeLibrary(hLibModule);
            SetLastError(dwLastError);
            hLibModule = NULL;
        }
        break;
    }

#if 0
    DbgPrint("[window] NtUserCreateWindowEx() == %d\n", Handle);
#endif

cleanup:
    if (!Unicode)
    {
        if (!IS_ATOM(lpClassName))
            RtlFreeUnicodeString(&ClassName);

        RtlFreeLargeString(&WindowName);
    }

    return Handle;
}


/*
 * @implemented
 */
HWND
WINAPI
DECLSPEC_HOTPATCH
CreateWindowExA(DWORD dwExStyle,
                LPCSTR lpClassName,
                LPCSTR lpWindowName,
                DWORD dwStyle,
                int x,
                int y,
                int nWidth,
                int nHeight,
                HWND hWndParent,
                HMENU hMenu,
                HINSTANCE hInstance,
                LPVOID lpParam)
{
    MDICREATESTRUCTA mdi;
    HWND hwnd;

    if (!RegisterDefaultClasses)
    {
       TRACE("CreateWindowExA RegisterSystemControls\n");
       RegisterSystemControls();
    }

    if (dwExStyle & WS_EX_MDICHILD)
    {
        POINT mPos[2];
        UINT id = 0;
        HWND top_child;
        PWND pWndParent;

        pWndParent = ValidateHwnd(hWndParent);

        if (!pWndParent) return NULL;

        if (pWndParent->fnid != FNID_MDICLIENT) // wine uses WIN_ISMDICLIENT
        {
           WARN("WS_EX_MDICHILD, but parent %p is not MDIClient\n", hWndParent);
           return NULL;
        }

        /* lpParams of WM_[NC]CREATE is different for MDI children.
        * MDICREATESTRUCT members have the originally passed values.
        */
        mdi.szClass = lpClassName;
        mdi.szTitle = lpWindowName;
        mdi.hOwner = hInstance;
        mdi.x = x;
        mdi.y = y;
        mdi.cx = nWidth;
        mdi.cy = nHeight;
        mdi.style = dwStyle;
        mdi.lParam = (LPARAM)lpParam;

        lpParam = (LPVOID)&mdi;

        if (pWndParent->style & MDIS_ALLCHILDSTYLES)
        {
            if (dwStyle & WS_POPUP)
            {
                WARN("WS_POPUP with MDIS_ALLCHILDSTYLES is not allowed\n");
                return(0);
            }
            dwStyle |= (WS_CHILD | WS_CLIPSIBLINGS);
        }
        else
        {
            dwStyle &= ~WS_POPUP;
            dwStyle |= (WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CAPTION |
                WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
        }

        top_child = GetWindow(hWndParent, GW_CHILD);

        if (top_child)
        {
            /* Restore current maximized child */
            if((dwStyle & WS_VISIBLE) && IsZoomed(top_child))
            {
                TRACE("Restoring current maximized child %p\n", top_child);
                SendMessageW( top_child, WM_SETREDRAW, FALSE, 0 );
                ShowWindow(top_child, SW_RESTORE);
                SendMessageW( top_child, WM_SETREDRAW, TRUE, 0 );
            }
        }

        MDI_CalcDefaultChildPos(hWndParent, -1, mPos, 0, &id);

        if (!(dwStyle & WS_POPUP)) hMenu = UlongToHandle(id);

        if (dwStyle & (WS_CHILD | WS_POPUP))
        {
            if (x == CW_USEDEFAULT || x == CW_USEDEFAULT16)
            {
                x = mPos[0].x;
                y = mPos[0].y;
            }
            if (nWidth == CW_USEDEFAULT || nWidth == CW_USEDEFAULT16 || !nWidth)
                nWidth = mPos[1].x;
            if (nHeight == CW_USEDEFAULT || nHeight == CW_USEDEFAULT16 || !nHeight)
                nHeight = mPos[1].y;
        }
    }

    hwnd = User32CreateWindowEx(dwExStyle,
                                lpClassName,
                                lpWindowName,
                                dwStyle,
                                x,
                                y,
                                nWidth,
                                nHeight,
                                hWndParent,
                                hMenu,
                                hInstance,
                                lpParam,
                                NUCWE_ANSI);
    return hwnd;
}


/*
 * @implemented
 */
HWND
WINAPI
DECLSPEC_HOTPATCH
CreateWindowExW(DWORD dwExStyle,
                LPCWSTR lpClassName,
                LPCWSTR lpWindowName,
                DWORD dwStyle,
                int x,
                int y,
                int nWidth,
                int nHeight,
                HWND hWndParent,
                HMENU hMenu,
                HINSTANCE hInstance,
                LPVOID lpParam)
{
    MDICREATESTRUCTW mdi;
    HWND hwnd;

    if (!RegisterDefaultClasses)
    {
       ERR("CreateWindowExW RegisterSystemControls\n");
       RegisterSystemControls();
    }

    if (dwExStyle & WS_EX_MDICHILD)
    {
        POINT mPos[2];
        UINT id = 0;
        HWND top_child;
        PWND pWndParent;

        pWndParent = ValidateHwnd(hWndParent);

        if (!pWndParent) return NULL;

        if (pWndParent->fnid != FNID_MDICLIENT)
        {
           WARN("WS_EX_MDICHILD, but parent %p is not MDIClient\n", hWndParent);
           return NULL;
        }

        /* lpParams of WM_[NC]CREATE is different for MDI children.
        * MDICREATESTRUCT members have the originally passed values.
        */
        mdi.szClass = lpClassName;
        mdi.szTitle = lpWindowName;
        mdi.hOwner = hInstance;
        mdi.x = x;
        mdi.y = y;
        mdi.cx = nWidth;
        mdi.cy = nHeight;
        mdi.style = dwStyle;
        mdi.lParam = (LPARAM)lpParam;

        lpParam = (LPVOID)&mdi;

        if (pWndParent->style & MDIS_ALLCHILDSTYLES)
        {
            if (dwStyle & WS_POPUP)
            {
                WARN("WS_POPUP with MDIS_ALLCHILDSTYLES is not allowed\n");
                return(0);
            }
            dwStyle |= (WS_CHILD | WS_CLIPSIBLINGS);
        }
        else
        {
            dwStyle &= ~WS_POPUP;
            dwStyle |= (WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CAPTION |
                WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
        }

        top_child = GetWindow(hWndParent, GW_CHILD);

        if (top_child)
        {
            /* Restore current maximized child */
            if((dwStyle & WS_VISIBLE) && IsZoomed(top_child))
            {
                TRACE("Restoring current maximized child %p\n", top_child);
                SendMessageW( top_child, WM_SETREDRAW, FALSE, 0 );
                ShowWindow(top_child, SW_RESTORE);
                SendMessageW( top_child, WM_SETREDRAW, TRUE, 0 );
            }
        }

        MDI_CalcDefaultChildPos(hWndParent, -1, mPos, 0, &id);

        if (!(dwStyle & WS_POPUP)) hMenu = UlongToHandle(id);

        if (dwStyle & (WS_CHILD | WS_POPUP))
        {
            if (x == CW_USEDEFAULT || x == CW_USEDEFAULT16)
            {
                x = mPos[0].x;
                y = mPos[0].y;
            }
            if (nWidth == CW_USEDEFAULT || nWidth == CW_USEDEFAULT16 || !nWidth)
                nWidth = mPos[1].x;
            if (nHeight == CW_USEDEFAULT || nHeight == CW_USEDEFAULT16 || !nHeight)
                nHeight = mPos[1].y;
        }
    }

    hwnd = User32CreateWindowEx(dwExStyle,
                                (LPCSTR)lpClassName,
                                (LPCSTR)lpWindowName,
                                dwStyle,
                                x,
                                y,
                                nWidth,
                                nHeight,
                                hWndParent,
                                hMenu,
                                hInstance,
                                lpParam,
                                0);
    return hwnd;
}

/*
 * @unimplemented
 */
HDWP WINAPI
DeferWindowPos(HDWP hWinPosInfo,
               HWND hWnd,
               HWND hWndInsertAfter,
               int x,
               int y,
               int cx,
               int cy,
               UINT uFlags)
{
    return NtUserDeferWindowPos(hWinPosInfo, hWnd, hWndInsertAfter, x, y, cx, cy, uFlags);
}


/*
 * @unimplemented
 */
BOOL WINAPI
EndDeferWindowPos(HDWP hWinPosInfo)
{
    return NtUserEndDeferWindowPosEx(hWinPosInfo, 0);
}


/*
 * @implemented
 */
HWND WINAPI
GetDesktopWindow(VOID)
{
    PWND Wnd;
    HWND Ret = NULL;

    _SEH2_TRY
    {
        Wnd = GetThreadDesktopWnd();
        if (Wnd != NULL)
            Ret = UserHMGetHandle(Wnd);
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        /* Do nothing */
    }
    _SEH2_END;

    return Ret;
}


static BOOL
User32EnumWindows(HDESK hDesktop,
                  HWND hWndparent,
                  WNDENUMPROC lpfn,
                  LPARAM lParam,
                  DWORD dwThreadId,
                  BOOL bChildren)
{
    DWORD i, dwCount = 0;
    HWND* pHwnd = NULL;
    HANDLE hHeap;
    NTSTATUS Status;

    if (!lpfn)
    {
        SetLastError ( ERROR_INVALID_PARAMETER );
        return FALSE;
    }

    /* FIXME instead of always making two calls, should we use some
       sort of persistent buffer and only grow it ( requiring a 2nd
       call ) when the buffer wasn't already big enough? */
    /* first get how many window entries there are */
    Status = NtUserBuildHwndList(hDesktop,
                                 hWndparent,
                                 bChildren,
                                 dwThreadId,
                                 lParam,
                                 NULL,
                                 &dwCount);
    if (!NT_SUCCESS(Status))
        return FALSE;

    if (!dwCount)
    {
       if (!dwThreadId)
          return FALSE;
       else
          return TRUE;
    }

    /* allocate buffer to receive HWND handles */
    hHeap = GetProcessHeap();
    pHwnd = HeapAlloc(hHeap, 0, sizeof(HWND)*(dwCount+1));
    if (!pHwnd)
    {
        SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
        return FALSE;
    }

    /* now call kernel again to fill the buffer this time */
    Status = NtUserBuildHwndList(hDesktop,
                                 hWndparent,
                                 bChildren,
                                 dwThreadId,
                                 lParam,
                                 pHwnd,
                                 &dwCount);
    if (!NT_SUCCESS(Status))
    {
        if (pHwnd)
            HeapFree(hHeap, 0, pHwnd);
        return FALSE;
    }

    /* call the user's callback function until we're done or
       they tell us to quit */
    for ( i = 0; i < dwCount; i++ )
    {
        /* FIXME I'm only getting NULLs from Thread Enumeration, and it's
         * probably because I'm not doing it right in NtUserBuildHwndList.
         * Once that's fixed, we shouldn't have to check for a NULL HWND
         * here
         * This is now fixed in revision 50205. (jt)
         */
        if (!pHwnd[i]) /* don't enumerate a NULL HWND */
            continue;
        if (!(*lpfn)(pHwnd[i], lParam))
        {
            HeapFree ( hHeap, 0, pHwnd );
            return FALSE;
        }
    }
    if (pHwnd)
        HeapFree(hHeap, 0, pHwnd);
    return TRUE;
}


/*
 * @implemented
 */
BOOL WINAPI
EnumChildWindows(HWND hWndParent,
                 WNDENUMPROC lpEnumFunc,
                 LPARAM lParam)
{
    if (!hWndParent)
    {
        return EnumWindows(lpEnumFunc, lParam);
    }
    return User32EnumWindows(NULL, hWndParent, lpEnumFunc, lParam, 0, TRUE);
}


/*
 * @implemented
 */
BOOL WINAPI
EnumThreadWindows(DWORD dwThreadId,
                  WNDENUMPROC lpfn,
                  LPARAM lParam)
{
    if (!dwThreadId)
        dwThreadId = GetCurrentThreadId();
    return User32EnumWindows(NULL, NULL, lpfn, lParam, dwThreadId, FALSE);
}


/*
 * @implemented
 */
BOOL WINAPI
EnumWindows(WNDENUMPROC lpEnumFunc,
            LPARAM lParam)
{
    return User32EnumWindows(NULL, NULL, lpEnumFunc, lParam, 0, FALSE);
}


/*
 * @implemented
 */
BOOL WINAPI
EnumDesktopWindows(HDESK hDesktop,
                   WNDENUMPROC lpfn,
                   LPARAM lParam)
{
    return User32EnumWindows(hDesktop, NULL, lpfn, lParam, 0, FALSE);
}


/*
 * @implemented
 */
HWND WINAPI
FindWindowExA(HWND hwndParent,
              HWND hwndChildAfter,
              LPCSTR lpszClass,
              LPCSTR lpszWindow)
{
    LPWSTR titleW = NULL;
    HWND hwnd = 0;

    if (lpszWindow)
    {
        DWORD len = MultiByteToWideChar( CP_ACP, 0, lpszWindow, -1, NULL, 0 );
        if (!(titleW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return 0;
        MultiByteToWideChar( CP_ACP, 0, lpszWindow, -1, titleW, len );
    }

    if (!IS_INTRESOURCE(lpszClass))
    {
        WCHAR classW[256];
        if (MultiByteToWideChar( CP_ACP, 0, lpszClass, -1, classW, sizeof(classW)/sizeof(WCHAR) ))
            hwnd = FindWindowExW( hwndParent, hwndChildAfter, classW, titleW );
    }
    else
    {
        hwnd = FindWindowExW( hwndParent, hwndChildAfter, (LPCWSTR)lpszClass, titleW );
    }

    HeapFree( GetProcessHeap(), 0, titleW );
    return hwnd;
}


/*
 * @implemented
 */
HWND WINAPI
FindWindowExW(HWND hwndParent,
              HWND hwndChildAfter,
              LPCWSTR lpszClass,
              LPCWSTR lpszWindow)
{
    UNICODE_STRING ucClassName, *pucClassName = NULL;
    UNICODE_STRING ucWindowName, *pucWindowName = NULL;

    if (IS_ATOM(lpszClass))
    {
        ucClassName.Length = 0;
        ucClassName.Buffer = (LPWSTR)lpszClass;
        pucClassName = &ucClassName;
    }
    else if (lpszClass != NULL)
    {
        RtlInitUnicodeString(&ucClassName,
                             lpszClass);
        pucClassName = &ucClassName;
    }

    if (lpszWindow != NULL)
    {
        RtlInitUnicodeString(&ucWindowName,
                             lpszWindow);
        pucWindowName = &ucWindowName;
    }

    return NtUserFindWindowEx(hwndParent,
                              hwndChildAfter,
                              pucClassName,
                              pucWindowName,
                              0);
}


/*
 * @implemented
 */
HWND WINAPI
FindWindowA(LPCSTR lpClassName, LPCSTR lpWindowName)
{
    //FIXME: FindWindow does not search children, but FindWindowEx does.
    //       what should we do about this?
    return FindWindowExA (NULL, NULL, lpClassName, lpWindowName);
}


/*
 * @implemented
 */
HWND WINAPI
FindWindowW(LPCWSTR lpClassName, LPCWSTR lpWindowName)
{
    /*

    There was a FIXME here earlier, but I think it is just a documentation unclarity.

    FindWindow only searches top level windows. What they mean is that child
    windows of other windows than the desktop can be searched.
    FindWindowExW never does a recursive search.

    / Joakim
    */

    return FindWindowExW(NULL, NULL, lpClassName, lpWindowName);
}



/*
 * @implemented
 */
BOOL WINAPI
GetAltTabInfoA(HWND hwnd,
               int iItem,
               PALTTABINFO pati,
               LPSTR pszItemText,
               UINT cchItemText)
{
    return NtUserGetAltTabInfo(hwnd,iItem,pati,(LPWSTR)pszItemText,cchItemText,TRUE);
}


/*
 * @implemented
 */
BOOL WINAPI
GetAltTabInfoW(HWND hwnd,
               int iItem,
               PALTTABINFO pati,
               LPWSTR pszItemText,
               UINT cchItemText)
{
    return NtUserGetAltTabInfo(hwnd,iItem,pati,pszItemText,cchItemText,FALSE);
}


/*
 * @implemented
 */
HWND WINAPI
GetAncestor(HWND hwnd, UINT gaFlags)
{
    HWND Ret = NULL;
    PWND Ancestor, Wnd;

    Wnd = ValidateHwnd(hwnd);
    if (!Wnd)
        return NULL;

    _SEH2_TRY
    {
        Ancestor = NULL;
        switch (gaFlags)
        {
            case GA_PARENT:
                if (Wnd->spwndParent != NULL)
                    Ancestor = DesktopPtrToUser(Wnd->spwndParent);
                break;

            default:
                /* FIXME: Call win32k for now */
                Wnd = NULL;
                break;
        }

        if (Ancestor != NULL)
            Ret = UserHMGetHandle(Ancestor);
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        /* Do nothing */
    }
    _SEH2_END;

    if (!Wnd) /* Fall back */
        Ret = NtUserGetAncestor(hwnd, gaFlags);

    return Ret;
}


/*
 * @implemented
 */
BOOL WINAPI
GetClientRect(HWND hWnd, LPRECT lpRect)
{
    PWND Wnd = ValidateHwnd(hWnd);

    if (!Wnd) return FALSE;
    if (Wnd->style & WS_MINIMIZED)
    {
       lpRect->left = lpRect->top = 0;
       lpRect->right = GetSystemMetrics(SM_CXMINIMIZED);
       lpRect->bottom = GetSystemMetrics(SM_CYMINIMIZED);
       return TRUE;
    }
    if ( hWnd != GetDesktopWindow()) // Wnd->fnid != FNID_DESKTOP )
    {
/*        lpRect->left = lpRect->top = 0;
        lpRect->right = Wnd->rcClient.right - Wnd->rcClient.left;
        lpRect->bottom = Wnd->rcClient.bottom - Wnd->rcClient.top;
*/
        *lpRect = Wnd->rcClient;
        OffsetRect(lpRect, -Wnd->rcClient.left, -Wnd->rcClient.top);
    }
    else
    {
        lpRect->left = lpRect->top = 0;
        lpRect->right = Wnd->rcClient.right;
        lpRect->bottom = Wnd->rcClient.bottom;
/* Do this until Init bug is fixed. This sets 640x480, see InitMetrics.
        lpRect->right = GetSystemMetrics(SM_CXSCREEN);
        lpRect->bottom = GetSystemMetrics(SM_CYSCREEN);
*/    }
    return TRUE;
}


/*
 * @implemented
 */
HWND WINAPI
GetLastActivePopup(HWND hWnd)
{
    PWND Wnd;
    HWND Ret = hWnd;

    Wnd = ValidateHwnd(hWnd);
    if (Wnd != NULL)
    {
        _SEH2_TRY
        {
            if (Wnd->spwndLastActive)
            {
               PWND LastActive = DesktopPtrToUser(Wnd->spwndLastActive);
               Ret = UserHMGetHandle(LastActive);
            }
        }
        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
        {
            /* Do nothing */
        }
        _SEH2_END;
    }
    return Ret;
}


/*
 * @implemented
 */
HWND WINAPI
GetParent(HWND hWnd)
{
    PWND Wnd, WndParent;
    HWND Ret = NULL;

    Wnd = ValidateHwnd(hWnd);
    if (Wnd != NULL)
    {
        _SEH2_TRY
        {
            WndParent = NULL;
            if (Wnd->style & WS_POPUP)
            {
                if (Wnd->spwndOwner != NULL)
                    WndParent = DesktopPtrToUser(Wnd->spwndOwner);
            }
            else if (Wnd->style & WS_CHILD)
            {
                if (Wnd->spwndParent != NULL)
                    WndParent = DesktopPtrToUser(Wnd->spwndParent);
            }

            if (WndParent != NULL)
                Ret = UserHMGetHandle(WndParent);
        }
        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
        {
            /* Do nothing */
        }
        _SEH2_END;
    }

    return Ret;
}


/*
 * @implemented
 */
BOOL WINAPI
GetProcessDefaultLayout(DWORD *pdwDefaultLayout)
{
return (BOOL)NtUserCallOneParam( (DWORD_PTR)pdwDefaultLayout, ONEPARAM_ROUTINE_GETPROCDEFLAYOUT);
}


/*
 * @implemented
 */
HWND WINAPI
GetWindow(HWND hWnd,
          UINT uCmd)
{
    PWND Wnd, FoundWnd;
    HWND Ret = NULL;

    Wnd = ValidateHwnd(hWnd);
    if (!Wnd)
        return NULL;

    _SEH2_TRY
    {
        FoundWnd = NULL;
        switch (uCmd)
        {
            case GW_OWNER:
                if (Wnd->spwndOwner != NULL)
                    FoundWnd = DesktopPtrToUser(Wnd->spwndOwner);
                break;

            case GW_HWNDFIRST:
                if(Wnd->spwndParent != NULL)
                {
                    FoundWnd = DesktopPtrToUser(Wnd->spwndParent);
                    if (FoundWnd->spwndChild != NULL)
                        FoundWnd = DesktopPtrToUser(FoundWnd->spwndChild);
                }
                break;
            case GW_HWNDNEXT:
                if (Wnd->spwndNext != NULL)
                    FoundWnd = DesktopPtrToUser(Wnd->spwndNext);
                break;

            case GW_HWNDPREV:
                if (Wnd->spwndPrev != NULL)
                    FoundWnd = DesktopPtrToUser(Wnd->spwndPrev);
                break;

            case GW_CHILD:
                if (Wnd->spwndChild != NULL)
                    FoundWnd = DesktopPtrToUser(Wnd->spwndChild);
                break;

            case GW_HWNDLAST:
                FoundWnd = Wnd;
                while ( FoundWnd->spwndNext != NULL)
                    FoundWnd = DesktopPtrToUser(FoundWnd->spwndNext);
                break;

            default:
                Wnd = NULL;
                break;
        }

        if (FoundWnd != NULL)
            Ret = UserHMGetHandle(FoundWnd);
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        /* Do nothing */
    }
    _SEH2_END;

    return Ret;
}


/*
 * @implemented
 */
HWND WINAPI
GetTopWindow(HWND hWnd)
{
    if (!hWnd) hWnd = GetDesktopWindow();
    return GetWindow(hWnd, GW_CHILD);
}


/*
 * @implemented
 */
BOOL
WINAPI
DECLSPEC_HOTPATCH
GetWindowInfo(HWND hWnd,
              PWINDOWINFO pwi)
{
    PWND pWnd;
    PCLS pCls = NULL;
    SIZE Size = {0,0};
    BOOL Ret = FALSE;

    if ( !pwi || pwi->cbSize != sizeof(WINDOWINFO))
       SetLastError(ERROR_INVALID_PARAMETER); // Just set the error and go!

    pWnd = ValidateHwnd(hWnd);
    if (!pWnd)
        return Ret;

    UserGetWindowBorders(pWnd->style, pWnd->ExStyle, &Size, FALSE);

    _SEH2_TRY
    {
       pCls = DesktopPtrToUser(pWnd->pcls);
       pwi->rcWindow = pWnd->rcWindow;
       pwi->rcClient = pWnd->rcClient;
       pwi->dwStyle = pWnd->style;
       pwi->dwExStyle = pWnd->ExStyle;
       pwi->cxWindowBorders = Size.cx;
       pwi->cyWindowBorders = Size.cy;
       pwi->dwWindowStatus = 0;
       if (pWnd->state & WNDS_ACTIVEFRAME || (GetActiveWindow() == hWnd))
          pwi->dwWindowStatus = WS_ACTIVECAPTION;
       pwi->atomWindowType = (pCls ? pCls->atomClassName : 0 );

       if ( pWnd->state2 & WNDS2_WIN50COMPAT )
       {
          pwi->wCreatorVersion = 0x500;
       }
       else if ( pWnd->state2 & WNDS2_WIN40COMPAT )
       {
          pwi->wCreatorVersion = 0x400;
       }
       else if ( pWnd->state2 & WNDS2_WIN31COMPAT )
       {
          pwi->wCreatorVersion =  0x30A;
       }
       else
       {
          pwi->wCreatorVersion = 0x300;
       }

       Ret = TRUE;
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        /* Do nothing */
    }
    _SEH2_END;

   return Ret;
}


/*
 * @implemented
 */
UINT WINAPI
GetWindowModuleFileNameA(HWND hwnd,
                         LPSTR lpszFileName,
                         UINT cchFileNameMax)
{
    PWND Wnd = ValidateHwnd(hwnd);

    if (!Wnd)
        return 0;

    return GetModuleFileNameA(Wnd->hModule, lpszFileName, cchFileNameMax);
}


/*
 * @implemented
 */
UINT WINAPI
GetWindowModuleFileNameW(HWND hwnd,
                         LPWSTR lpszFileName,
                         UINT cchFileNameMax)
{
       PWND Wnd = ValidateHwnd(hwnd);

    if (!Wnd)
        return 0;

    return GetModuleFileNameW( Wnd->hModule, lpszFileName, cchFileNameMax );
}

/*
 * @implemented
 */
BOOL WINAPI
GetWindowRect(HWND hWnd,
              LPRECT lpRect)
{
    PWND Wnd = ValidateHwnd(hWnd);

    if (!Wnd) return FALSE;
    if ( hWnd != GetDesktopWindow()) // Wnd->fnid != FNID_DESKTOP )
    {
        *lpRect = Wnd->rcWindow;
    }
    else
    {
        lpRect->left = lpRect->top = 0;
        lpRect->right = Wnd->rcWindow.right;
        lpRect->bottom = Wnd->rcWindow.bottom;
/* Do this until Init bug is fixed. This sets 640x480, see InitMetrics.
        lpRect->right = GetSystemMetrics(SM_CXSCREEN);
        lpRect->bottom = GetSystemMetrics(SM_CYSCREEN);
*/    }
    return TRUE;
}

/*
 * @implemented
 */
int WINAPI
GetWindowTextA(HWND hWnd, LPSTR lpString, int nMaxCount)
{
    PWND Wnd;
    INT Length = 0;

    if (lpString == NULL || nMaxCount == 0)
        return 0;

    Wnd = ValidateHwnd(hWnd);
    if (!Wnd)
        return 0;

    lpString[0] = '\0';

    if (!TestWindowProcess(Wnd))
    {
        _SEH2_TRY
        {
            Length = DefWindowProcA(hWnd, WM_GETTEXT, nMaxCount, (LPARAM)lpString);
        }
        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
        {
            Length = 0;
        }
        _SEH2_END;
    }
    else
    {
        Length = SendMessageA(hWnd, WM_GETTEXT, nMaxCount, (LPARAM)lpString);
    }
    //ERR("GWTA Len %d : %s\n",Length,lpString);
    return Length;
}

/*
 * @implemented
 */
int WINAPI
GetWindowTextLengthA(HWND hWnd)
{
    PWND Wnd;

    Wnd = ValidateHwnd(hWnd);
    if (!Wnd)
        return 0;

    if (!TestWindowProcess(Wnd))
    {
        return DefWindowProcA(hWnd, WM_GETTEXTLENGTH, 0, 0);
    }
    else
    {
        return SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
    }
}

/*
 * @implemented
 */
int WINAPI
GetWindowTextLengthW(HWND hWnd)
{
    PWND Wnd;

    Wnd = ValidateHwnd(hWnd);
    if (!Wnd)
        return 0;

    if (!TestWindowProcess(Wnd))
    {
        return DefWindowProcW(hWnd, WM_GETTEXTLENGTH, 0, 0);
    }
    else
    {
        return SendMessageW(hWnd, WM_GETTEXTLENGTH, 0, 0);
    }
}

/*
 * @implemented
 */
int WINAPI
GetWindowTextW(HWND hWnd, LPWSTR lpString, int nMaxCount)
{
    PWND Wnd;
    INT Length = 0;

    if (lpString == NULL || nMaxCount == 0)
        return 0;

    Wnd = ValidateHwnd(hWnd);
    if (!Wnd)
        return 0;

    lpString[0] = L'\0';

    if (!TestWindowProcess(Wnd))
    {
        _SEH2_TRY
        {
            Length = DefWindowProcW(hWnd, WM_GETTEXT, nMaxCount, (LPARAM)lpString);
        }
        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
        {
            Length = 0;
        }
        _SEH2_END;
    }
    else
    {
        Length = SendMessageW(hWnd, WM_GETTEXT, nMaxCount, (LPARAM)lpString);
    }
    //ERR("GWTW Len %d : %S\n",Length,lpString);
    return Length;
}

DWORD WINAPI
GetWindowThreadProcessId(HWND hWnd,
                         LPDWORD lpdwProcessId)
{
    DWORD Ret = 0;
    PTHREADINFO ti;
    PWND pWnd = ValidateHwnd(hWnd);

    if (!pWnd) return Ret;

    ti = pWnd->head.pti;

    if (ti)
    {
        if (ti == GetW32ThreadInfo())
        { // We are current.
          //FIXME("Current!\n");
            if (lpdwProcessId)
                *lpdwProcessId = (DWORD_PTR)NtCurrentTeb()->ClientId.UniqueProcess;
            Ret = (DWORD_PTR)NtCurrentTeb()->ClientId.UniqueThread;
        }
        else
        { // Ask kernel for info.
          //FIXME("Kernel call!\n");
            if (lpdwProcessId)
                *lpdwProcessId = NtUserQueryWindow(hWnd, QUERY_WINDOW_UNIQUE_PROCESS_ID);
            Ret = NtUserQueryWindow(hWnd, QUERY_WINDOW_UNIQUE_THREAD_ID);
        }
    }
    return Ret;
}


/*
 * @implemented
 */
BOOL WINAPI
IsChild(HWND hWndParent,
    HWND hWnd)
{
    PWND WndParent, DesktopWnd,  Wnd;
    BOOL Ret = FALSE;

    WndParent = ValidateHwnd(hWndParent);
    if (!WndParent)
        return FALSE;
    Wnd = ValidateHwnd(hWnd);
    if (!Wnd)
        return FALSE;

    DesktopWnd = GetThreadDesktopWnd();
    if (!DesktopWnd)
        return FALSE;

    _SEH2_TRY
    {
        while (Wnd != NULL && ((Wnd->style & (WS_POPUP|WS_CHILD)) == WS_CHILD))
        {
            if (Wnd->spwndParent != NULL)
            {
                Wnd = DesktopPtrToUser(Wnd->spwndParent);

                if (Wnd == WndParent)
                {
                    Ret = TRUE;
                    break;
                }
            }
            else
                break;
        }
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        /* Do nothing */
    }
    _SEH2_END;

    return Ret;
}


/*
 * @implemented
 */
BOOL WINAPI
IsIconic(HWND hWnd)
{
    PWND Wnd = ValidateHwnd(hWnd);

    if (Wnd != NULL)
        return (Wnd->style & WS_MINIMIZE) != 0;

    return FALSE;
}


/*
 * @implemented
 */
BOOL WINAPI
IsWindow(HWND hWnd)
{
    PWND Wnd = ValidateHwndNoErr(hWnd);
    if (Wnd != NULL)
    {
        if (Wnd->state & WNDS_DESTROYED ||
            Wnd->state2 & WNDS2_INDESTROY)
           return FALSE;
        return TRUE;
    }

    return FALSE;
}


/*
 * @implemented
 */
BOOL WINAPI
IsWindowUnicode(HWND hWnd)
{
    PWND Wnd = ValidateHwnd(hWnd);

    if (Wnd != NULL)
        return Wnd->Unicode;

    return FALSE;
}


/*
 * @implemented
 */
BOOL WINAPI
IsWindowVisible(HWND hWnd)
{
    BOOL Ret = FALSE;
    PWND Wnd = ValidateHwnd(hWnd);

    if (Wnd != NULL)
    {
        _SEH2_TRY
        {
            Ret = TRUE;

            do
            {
                if (!(Wnd->style & WS_VISIBLE))
                {
                    Ret = FALSE;
                    break;
                }

                if (Wnd->spwndParent != NULL)
                    Wnd = DesktopPtrToUser(Wnd->spwndParent);
                else
                    break;

            } while (Wnd != NULL);
        }
        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
        {
            Ret = FALSE;
        }
        _SEH2_END;
    }

    return Ret;
}


/*
 * @implemented
 */
BOOL WINAPI
IsWindowEnabled(HWND hWnd)
{
    // AG: I don't know if child windows are affected if the parent is
    // disabled. I think they stop processing messages but stay appearing
    // as enabled.

    return !(GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_DISABLED);
}


/*
 * @implemented
 */
BOOL WINAPI
IsZoomed(HWND hWnd)
{
    return (GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_MAXIMIZE) != 0;
}


/*
 * @implemented
 */
BOOL WINAPI
LockSetForegroundWindow(UINT uLockCode)
{
    return NtUserxLockSetForegroundWindow(uLockCode);
}


/*
 * @implemented
 */
BOOL WINAPI
AnimateWindow(HWND hwnd,
              DWORD dwTime,
              DWORD dwFlags)
{
    /* FIXME Add animation code */

    /* If trying to show/hide and it's already   *
     * shown/hidden or invalid window, fail with *
     * invalid parameter                         */

    BOOL visible;
    visible = IsWindowVisible(hwnd);
    if(!IsWindow(hwnd) ||
       (visible && !(dwFlags & AW_HIDE)) ||
       (!visible && (dwFlags & AW_HIDE)))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    ShowWindow(hwnd, (dwFlags & AW_HIDE) ? SW_HIDE : ((dwFlags & AW_ACTIVATE) ? SW_SHOW : SW_SHOWNA));

    return TRUE;
}


/*
 * @implemented
 */
BOOL WINAPI
OpenIcon(HWND hWnd)
{
    if (!(GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_MINIMIZE))
        return FALSE;

    ShowWindow(hWnd,SW_RESTORE);
    return TRUE;
}


/*
 * @implemented
 */
HWND WINAPI
RealChildWindowFromPoint(HWND hwndParent,
                         POINT ptParentClientCoords)
{
    return NtUserRealChildWindowFromPoint(hwndParent, ptParentClientCoords.x, ptParentClientCoords.y);
}

/*
 * @unimplemented
 */
BOOL WINAPI
SetForegroundWindow(HWND hWnd)
{
    return NtUserxSetForegroundWindow(hWnd);
}


/*
 * @implemented
 */
BOOL WINAPI
SetProcessDefaultLayout(DWORD dwDefaultLayout)
{
return NtUserCallOneParam( (DWORD_PTR)dwDefaultLayout, ONEPARAM_ROUTINE_SETPROCDEFLAYOUT);
}


/*
 * @implemented
 */
BOOL
WINAPI
DECLSPEC_HOTPATCH
SetWindowTextA(HWND hWnd,
               LPCSTR lpString)
{
  PWND pwnd;

  pwnd = ValidateHwnd(hWnd);
  if (pwnd)
  {
     if (!TestWindowProcess(pwnd))
     {
        /* do not send WM_GETTEXT messages to other processes */
        return (DefWindowProcA(hWnd, WM_SETTEXT, 0, (LPARAM)lpString) >= 0);
     }
     return (SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)lpString) >= 0);
  }
  return FALSE;
}


/*
 * @implemented
 */
BOOL
WINAPI
DECLSPEC_HOTPATCH
SetWindowTextW(HWND hWnd,
               LPCWSTR lpString)
{
  PWND pwnd;

  pwnd = ValidateHwnd(hWnd);
  if (pwnd)
  {
     if (!TestWindowProcess(pwnd))
     {
        /* do not send WM_GETTEXT messages to other processes */
        return (DefWindowProcW(hWnd, WM_SETTEXT, 0, (LPARAM)lpString) >= 0);
     }
     return (SendMessageW(hWnd, WM_SETTEXT, 0, (LPARAM)lpString) >= 0);
  }
  return FALSE;
}


/*
 * @implemented
 */
BOOL WINAPI
ShowOwnedPopups(HWND hWnd, BOOL fShow)
{
    return NtUserxShowOwnedPopups(hWnd, fShow);
}


/*
 * @implemented
 */
BOOL WINAPI
UpdateLayeredWindow( HWND hwnd,
                     HDC hdcDst,
                     POINT *pptDst,
                     SIZE *psize,
                     HDC hdcSrc,
                     POINT *pptSrc,
                     COLORREF crKey,
                     BLENDFUNCTION *pbl,
                     DWORD dwFlags)
{
  if (dwFlags & ULW_EX_NORESIZE)  /* only valid for UpdateLayeredWindowIndirect */
  {
     SetLastError( ERROR_INVALID_PARAMETER );
     return FALSE;
  }
  return NtUserUpdateLayeredWindow( hwnd,
                                    hdcDst,
                                    pptDst,
                                    psize,
                                    hdcSrc,
                                    pptSrc,
                                    crKey,
                                    pbl,
                                    dwFlags,
                                    NULL);
}

/*
 * @implemented
 */
BOOL WINAPI
UpdateLayeredWindowIndirect(HWND hwnd,
                            const UPDATELAYEREDWINDOWINFO *info)
{
  if (info && info->cbSize == sizeof(*info))
  {
     return NtUserUpdateLayeredWindow( hwnd,
                                       info->hdcDst,
                                       (POINT *)info->pptDst,
                                       (SIZE *)info->psize,
                                       info->hdcSrc,
                                       (POINT *)info->pptSrc,
                                       info->crKey,
                                       (BLENDFUNCTION *)info->pblend,
                                       info->dwFlags,
                                       (RECT *)info->prcDirty);
  }
  SetLastError(ERROR_INVALID_PARAMETER);
  return FALSE;
}

/*
 * @implemented
 */
BOOL WINAPI
SetWindowContextHelpId(HWND hwnd,
                       DWORD dwContextHelpId)
{
    return NtUserxSetWindowContextHelpId(hwnd, dwContextHelpId);
}

/*
 * @implemented
 */
DWORD WINAPI
GetWindowContextHelpId(HWND hwnd)
{
    return NtUserxGetWindowContextHelpId(hwnd);
}

/*
 * @implemented
 */
int WINAPI
InternalGetWindowText(HWND hWnd, LPWSTR lpString, int nMaxCount)
{
    INT Ret = NtUserInternalGetWindowText(hWnd, lpString, nMaxCount);
    if (Ret == 0 && lpString)
        *lpString = L'\0';
    return Ret;
}

/*
 * @implemented
 */
BOOL WINAPI
IsHungAppWindow(HWND hwnd)
{
    PWND Window;
    UNICODE_STRING ClassName;
    WCHAR szClass[16];
    static const UNICODE_STRING GhostClass = RTL_CONSTANT_STRING(L"Ghost");

    /* Ghost is a hung window */
    RtlInitEmptyUnicodeString(&ClassName, szClass, sizeof(szClass));
    Window = ValidateHwnd(hwnd);
    if (Window && Window->fnid == FNID_GHOST &&
        NtUserGetClassName(hwnd, FALSE, &ClassName) &&
        RtlEqualUnicodeString(&ClassName, &GhostClass, TRUE))
    {
        return TRUE;
    }

    return (NtUserQueryWindow(hwnd, QUERY_WINDOW_ISHUNG) != 0);
}

/*
 * @implemented
 */
VOID WINAPI
SetLastErrorEx(DWORD dwErrCode, DWORD dwType)
{
    SetLastError(dwErrCode);
}

/*
 * @implemented
 */
HWND WINAPI
GetFocus(VOID)
{
    return (HWND)NtUserGetThreadState(THREADSTATE_FOCUSWINDOW);
}

DWORD WINAPI
GetRealWindowOwner(HWND hwnd)
{
    return NtUserQueryWindow(hwnd, QUERY_WINDOW_REAL_ID);
}

/*
 * @implemented
 */
HWND WINAPI
SetTaskmanWindow(HWND hWnd)
{
    return NtUserxSetTaskmanWindow(hWnd);
}

/*
 * @implemented
 */
HWND WINAPI
SetProgmanWindow(HWND hWnd)
{
    return NtUserxSetProgmanWindow(hWnd);
}

/*
 * @implemented
 */
HWND WINAPI
GetProgmanWindow(VOID)
{
    return (HWND)NtUserGetThreadState(THREADSTATE_PROGMANWINDOW);
}

/*
 * @implemented
 */
HWND WINAPI
GetTaskmanWindow(VOID)
{
    return (HWND)NtUserGetThreadState(THREADSTATE_TASKMANWINDOW);
}

/*
 * @implemented
 */
BOOL WINAPI
ScrollWindow(HWND hWnd,
             int dx,
             int dy,
             CONST RECT *lpRect,
             CONST RECT *prcClip)
{
    return NtUserScrollWindowEx(hWnd,
                                dx,
                                dy,
                                lpRect,
                                prcClip,
                                0,
                                NULL,
                                (lpRect ? 0 : SW_SCROLLCHILDREN) | (SW_ERASE|SW_INVALIDATE|SW_SCROLLWNDDCE)) != ERROR;
}

/* ScrollWindow uses the window DC, ScrollWindowEx doesn't */

/*
 * @implemented
 */
INT WINAPI
ScrollWindowEx(HWND hWnd,
               int dx,
               int dy,
               CONST RECT *prcScroll,
               CONST RECT *prcClip,
               HRGN hrgnUpdate,
               LPRECT prcUpdate,
               UINT flags)
{
    if (flags & SW_SMOOTHSCROLL)
    {
       FIXME("SW_SMOOTHSCROLL not supported.");
       // Fall through....
    }
    return NtUserScrollWindowEx(hWnd,
                                dx,
                                dy,
                                prcScroll,
                                prcClip,
                                hrgnUpdate,
                                prcUpdate,
                                flags);
}

/*
 * @implemented
 */
BOOL WINAPI
AnyPopup(VOID)
{
    int i;
    BOOL retvalue;
    HWND *list = WIN_ListChildren( GetDesktopWindow() );

    if (!list) return FALSE;
    for (i = 0; list[i]; i++)
    {
        if (IsWindowVisible( list[i] ) && GetWindow( list[i], GW_OWNER )) break;
    }
    retvalue = (list[i] != 0);
    HeapFree( GetProcessHeap(), 0, list );
    return retvalue;
}

/*
 * @implemented
 */
BOOL WINAPI
IsWindowInDestroy(HWND hWnd)
{
    PWND pwnd;
    pwnd = ValidateHwnd(hWnd);
    if (!pwnd)
       return FALSE;
    return ((pwnd->state2 & WNDS2_INDESTROY) == WNDS2_INDESTROY);
}

/*
 * @implemented
 */
VOID WINAPI
DisableProcessWindowsGhosting(VOID)
{
    NtUserxEnableProcessWindowGhosting(FALSE);
}

/* EOF */