/*
 * Copyright 2004-2020 Sandboxie Holdings, LLC 
 *
 * This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

//---------------------------------------------------------------------------
// GUI Services
//---------------------------------------------------------------------------

#include "dll.h"

#include "gui_p.h"
#include "core/svc/GuiWire.h"
#include "common/my_version.h"
#include <stdio.h>

#pragma auto_inline(off)


//---------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------


static void Gui_InitPropAtoms(void);

static const void *Gui_ReplaceAtom(const void *lpString);

HANDLE Gui_GetPropCommon(
    HWND hWnd, const void *lpString, BOOLEAN unicode, ULONG LastError);

static HANDLE Gui_GetPropW(HWND hWnd, const WCHAR *lpString);

static HANDLE Gui_GetPropA(HWND hWnd, const UCHAR *lpString);

static BOOL Gui_SetPropW(HWND hWnd, const WCHAR *lpString, HANDLE hData);

static BOOL Gui_SetPropA(HWND hWnd, const UCHAR *lpString, HANDLE hData);

static HANDLE Gui_RemovePropW(HWND hWnd, const WCHAR *lpString);

static HANDLE Gui_RemovePropA(HWND hWnd, const UCHAR *lpString);

static ULONG_PTR Gui_GetLongCommon(HWND hWnd, int nIndex, ULONG which);

static ULONG_PTR Gui_GetWindowLong_DragDrop(
    HWND hWnd, int nIndex, ULONG_PTR value);

static ULONG_PTR Gui_SetWindowLong_DragDrop(
    HWND hWnd, int nIndex, ULONG_PTR dwNew, ULONG_PTR value);

static ULONG Gui_GetWindowLongW(HWND hWnd, int nIndex);

static ULONG Gui_GetWindowLongA(HWND hWnd, int nIndex);

static ULONG Gui_SetWindowLongW(HWND hWnd, int nIndex, ULONG dwNew);

static ULONG Gui_SetWindowLongA(HWND hWnd, int nIndex, ULONG dwNew);

static ULONG_PTR Gui_SetWindowLong8(
    HWND hWnd, int nIndex, ULONG_PTR dwNew, ULONG IsAscii);

#ifndef _M_ARM64
static BOOLEAN Gui_Hook_SetWindowLong8(HMODULE module);
#endif

static ULONG Gui_GetClassLongW(HWND hWnd, int nIndex);

static ULONG Gui_GetClassLongA(HWND hWnd, int nIndex);

#ifdef _WIN64

static ULONG_PTR Gui_GetWindowLongPtrW(HWND hWnd, int nIndex);

static ULONG_PTR Gui_GetWindowLongPtrA(HWND hWnd, int nIndex);

static ULONG_PTR Gui_SetWindowLongPtrW(
    HWND hWnd, int nIndex, ULONG_PTR dwNew);

static ULONG_PTR Gui_SetWindowLongPtrA(
    HWND hWnd, int nIndex, ULONG_PTR dwNew);

static ULONG_PTR Gui_SetWindowLongPtr8(
    HWND hWnd, int nIndex, ULONG_PTR dwNew, ULONG IsAscii);

#ifndef _M_ARM64
static BOOLEAN Gui_Hook_SetWindowLongPtr8(HMODULE module);
#endif

static ULONG_PTR Gui_GetClassLongPtrW(HWND hWnd, int nIndex);

static ULONG_PTR Gui_GetClassLongPtrA(HWND hWnd, int nIndex);

#endif _WIN64


//---------------------------------------------------------------------------
// Variables
//---------------------------------------------------------------------------

       ATOM Gui_DropTargetProp_Atom = 0;

       ATOM Gui_WindowProcOldW_Atom = 0;
       ATOM Gui_WindowProcOldA_Atom = 0;
static ATOM Gui_WindowProcNewW_Atom = 0;
static ATOM Gui_WindowProcNewA_Atom = 0;

static ATOM Gui_OleDropTargetInterface_Atom           = 0;
static ATOM Gui_OleDropTargetMarshalHwnd_Atom         = 0;
static ATOM Gui_OleEndPointID_Atom                    = 0;
static ATOM Gui_Sandbox_OleDropTargetInterface_Atom   = 0;
static ATOM Gui_Sandbox_OleDropTargetMarshalHwnd_Atom = 0;
static ATOM Gui_Sandbox_OleEndPointID_Atom            = 0;


#ifdef _WIN64

static P_SetWindowLong8            __sys_SetWindowLong8         = 0;
static P_SetWindowLongPtr8         __sys_SetWindowLongPtr8      = 0;

#endif _WIN64


//---------------------------------------------------------------------------
// Gui_InitProp
//---------------------------------------------------------------------------


_FX BOOLEAN Gui_InitProp(HMODULE module)
{
    //
    // initialize our Drag-n-Drop atoms
    //

    Gui_InitPropAtoms();

    //
    // hook functions
    //

    // DisableComProxy BEGIN
    if (!SbieApi_QueryConfBool(NULL, L"DisableComProxy", FALSE))
    // DisableComProxy END
    if (! SbieDll_IsOpenCOM()) {

        //
        // if there is access to the real COM epmapper, then don't
        // hook Prop functions that are used to hide drag/drop props
        //

        SBIEDLL_HOOK_GUI(GetPropA);
        SBIEDLL_HOOK_GUI(GetPropW);
        SBIEDLL_HOOK_GUI(SetPropA);
        SBIEDLL_HOOK_GUI(SetPropW);
        SBIEDLL_HOOK_GUI(RemovePropA);
        SBIEDLL_HOOK_GUI(RemovePropW);
    }

    if (! Gui_OpenAllWinClasses) {

        //
        // if not hooking window classes, don't install custom wndproc
        //

        SBIEDLL_HOOK_GUI(GetWindowLongA);
        SBIEDLL_HOOK_GUI(GetWindowLongW);

#ifdef _WIN64

        if (Dll_OsBuild >= 14942) { // Windows 10 1703 preview #7
            HMODULE hWin32u = GetModuleHandleA("win32u.dll");
            __sys_SetWindowLong8 = (P_SetWindowLong8) GetProcAddress(hWin32u, "NtUserSetWindowLong");
            SBIEDLL_HOOK_GUI(SetWindowLong8);
        }
        else if (Dll_OsBuild < 9600) {
            SBIEDLL_HOOK_GUI(SetWindowLongA);
            SBIEDLL_HOOK_GUI(SetWindowLongW);

        } 
        else
#ifndef _M_ARM64
        // new style hook on SetWindowLong on 64-bit Windows 8.1 and later
        if (! Gui_Hook_SetWindowLong8(module))
#endif
        {
            SbieApi_Log(2205, L"SetWindowLong8");
            return FALSE;
        }

#else ! _WIN64

        // otherwise old style hooks on SetWindowLongA and SetWindowLongW
        SBIEDLL_HOOK_GUI(SetWindowLongA);
        SBIEDLL_HOOK_GUI(SetWindowLongW);

#endif _WIN64

        SBIEDLL_HOOK_GUI(GetClassLongA);
        SBIEDLL_HOOK_GUI(GetClassLongW);

#ifdef _WIN64

        SBIEDLL_HOOK_GUI(GetWindowLongPtrA);
        SBIEDLL_HOOK_GUI(GetWindowLongPtrW);

        if (Dll_OsBuild >= 14942) { // Windows 10 1703 preview #7
            HMODULE hWin32u = GetModuleHandleA("win32u.dll");
            __sys_SetWindowLongPtr8 = (P_SetWindowLongPtr8) GetProcAddress(hWin32u,"NtUserSetWindowLongPtr");
            SBIEDLL_HOOK_GUI(SetWindowLongPtr8);
        }
        else if (Dll_OsBuild < 8400) {
            SBIEDLL_HOOK_GUI(SetWindowLongPtrA);
            SBIEDLL_HOOK_GUI(SetWindowLongPtrW);

        } 
        else 
#ifndef _M_ARM64
        // special hook on SetWindowLongPtr on 64-bit Windows 8 and later
        if (! Gui_Hook_SetWindowLongPtr8(module))
#endif
        {
            SbieApi_Log(2205, L"SetWindowLongPtr8");
            return FALSE;
        }

        SBIEDLL_HOOK_GUI(GetClassLongPtrA);
        SBIEDLL_HOOK_GUI(GetClassLongPtrW);

#endif _WIN64

    }

    return TRUE;
}


//---------------------------------------------------------------------------
// Gui_InitPropAtoms
//---------------------------------------------------------------------------


_FX void Gui_InitPropAtoms(void)
{
    //
    // drag drop props
    //

    static const WCHAR *_OleDropTargetInterface = L"OleDropTargetInterface";
    static const WCHAR *_OleDropTargetMarshalHwnd =
        L"OleDropTargetMarshalHwnd";
    static const WCHAR *_OleEndPointID =
        L"OleEndPointID";
    static const WCHAR *_format = L"Sandbox:%s:%s";
    WCHAR name[128];

    if (! Gui_OleDropTargetInterface_Atom) {

        Gui_OleDropTargetInterface_Atom =
            GlobalAddAtomW(_OleDropTargetInterface);

        if (Gui_OleDropTargetInterface_Atom) {
            Sbie_snwprintf(name, 128, _format, Dll_BoxName, _OleDropTargetInterface);
            Gui_Sandbox_OleDropTargetInterface_Atom = GlobalAddAtomW(name);
        }
    }

    if (! Gui_OleDropTargetMarshalHwnd_Atom) {

        Gui_OleDropTargetMarshalHwnd_Atom =
            GlobalAddAtomW(_OleDropTargetMarshalHwnd);

        if (Gui_OleDropTargetMarshalHwnd_Atom) {
			Sbie_snwprintf(name, 128, _format, Dll_BoxName, _OleDropTargetMarshalHwnd);
            Gui_Sandbox_OleDropTargetMarshalHwnd_Atom = GlobalAddAtomW(name);
        }
    }

    if (! Gui_OleEndPointID_Atom) {

        Gui_OleEndPointID_Atom =
            GlobalAddAtomW(_OleEndPointID);

        if (Gui_OleEndPointID_Atom) {
			Sbie_snwprintf(name, 128, _format, Dll_BoxName, _OleEndPointID);
            Gui_Sandbox_OleEndPointID_Atom = GlobalAddAtomW(name);
        }
    }

    //
    // window/dialog procedure props
    //

    if (! Gui_DropTargetProp_Atom)
        Gui_DropTargetProp_Atom = GlobalAddAtomW(SBIE L"_DropTarget");

    if (! Gui_WindowProcOldW_Atom)
        Gui_WindowProcOldW_Atom = GlobalAddAtomW(SBIE L"_WindowProcOldW");

    if (! Gui_WindowProcOldA_Atom)
        Gui_WindowProcOldA_Atom = GlobalAddAtomW(SBIE L"_WindowProcOldA");

    if (! Gui_WindowProcNewW_Atom)
        Gui_WindowProcNewW_Atom = GlobalAddAtomW(SBIE L"_WindowProcNewW");

    if (! Gui_WindowProcNewA_Atom)
        Gui_WindowProcNewA_Atom = GlobalAddAtomW(SBIE L"_WindowProcNewA");
}


//---------------------------------------------------------------------------
// Gui_ReplaceAtom
//---------------------------------------------------------------------------


_FX const void *Gui_ReplaceAtom(const void *lpString)
{
    Gui_InitPropAtoms();

    if (((LONG_PTR)lpString & ~0xFFFF) == 0) {
        LONG_PTR atom = (LONG_PTR)lpString & 0xFFFF;
        if (atom == Gui_OleDropTargetInterface_Atom)
            (ATOM)lpString = Gui_Sandbox_OleDropTargetInterface_Atom;
        if (atom == Gui_OleDropTargetMarshalHwnd_Atom)
            (ATOM)lpString = Gui_Sandbox_OleDropTargetMarshalHwnd_Atom;
        if (atom == Gui_OleEndPointID_Atom)
            (ATOM)lpString = Gui_Sandbox_OleEndPointID_Atom;
    }

    return lpString;
}


//---------------------------------------------------------------------------
// Gui_GetPropCommon
//---------------------------------------------------------------------------


_FX HANDLE Gui_GetPropCommon(
    HWND hWnd, const void *lpString, BOOLEAN unicode, ULONG LastError)
{
    GUI_GET_WINDOW_PROP_REQ req;
    GUI_GET_WINDOW_PROP_RPL *rpl;
    HANDLE handle;
    ULONG error;
    ULONG len;

    memzero(&req, sizeof(req));

    req.msgid = GUI_GET_WINDOW_PROP;
    req.error = LastError;
    req.hwnd = (ULONG)(ULONG_PTR)hWnd;
    req.unicode = unicode;

    if (((ULONG_PTR)lpString & 0xFFFF0000) == 0) {
        req.prop_atom = ((ULONG)(ULONG_PTR)lpString) & 0xFFFF;

    } else if (unicode) {
        len = wcslen((WCHAR *)lpString);
        if (len >= RTL_NUMBER_OF_V1(req.prop_text) - 1)
            return NULL;
        wmemcpy(req.prop_text, lpString, len);
        req.prop_is_text = TRUE;

    } else {
        len = strlen((char *)lpString);
        if (len >= RTL_NUMBER_OF_V1(req.prop_text) - 1)
            return NULL;
        memcpy(req.prop_text, lpString, len);
        req.prop_is_text = TRUE;
    }

    rpl = Gui_CallProxy(&req, sizeof(req), sizeof(GUI_GET_WINDOW_PROP_RPL));
    if (! rpl)
        return FALSE;

    handle = (HANDLE)(ULONG_PTR)rpl->result;
    error = rpl->error;
    Dll_Free(rpl);

    SetLastError(error);
    return handle;
}


//---------------------------------------------------------------------------
// Gui_GetPropW
//---------------------------------------------------------------------------


_FX HANDLE Gui_GetPropW(HWND hWnd, const WCHAR *lpString)
{
    if (! Gui_OpenAllWinClasses) {

        //
        // normal mode
        //

        ULONG LastError = GetLastError();

        BOOLEAN IsDesktop = (hWnd == __sys_GetDesktopWindow());

        if (! Gui_IsWindowAccessible(hWnd) && !IsDesktop) {
            SetLastError(ERROR_INVALID_WINDOW_HANDLE);
            return NULL;
        }

        lpString = Gui_ReplaceAtom(lpString);

        if (!Gui_UseProxyService || Gui_IsSameBox(hWnd, NULL, NULL) || IsDesktop)
            return __sys_GetPropW(hWnd, lpString);
        else
            return Gui_GetPropCommon(hWnd, lpString, TRUE, LastError);

    } else {

        //
        // OpenWinClass=* mode
        //

        lpString = Gui_ReplaceAtom(lpString);
        return __sys_GetPropW(hWnd, lpString);
    }
}


//---------------------------------------------------------------------------
// Gui_GetPropA
//---------------------------------------------------------------------------


_FX HANDLE Gui_GetPropA(HWND hWnd, const UCHAR *lpString)
{
    if (! Gui_OpenAllWinClasses) {

        //
        // normal mode
        //

        ULONG LastError = GetLastError();

        BOOLEAN IsDesktop = (hWnd == __sys_GetDesktopWindow());

        if (! Gui_IsWindowAccessible(hWnd) && !IsDesktop) {
            SetLastError(ERROR_INVALID_WINDOW_HANDLE);
            return NULL;
        }

        lpString = Gui_ReplaceAtom(lpString);

        if (!Gui_UseProxyService || Gui_IsSameBox(hWnd, NULL, NULL) || IsDesktop)
            return __sys_GetPropA(hWnd, lpString);
        else
            return Gui_GetPropCommon(hWnd, lpString, TRUE, LastError);

    } else {

        //
        // OpenWinClass=* mode
        //

        lpString = Gui_ReplaceAtom(lpString);
        return __sys_GetPropA(hWnd, lpString);
    }
}


//---------------------------------------------------------------------------
// Gui_SetPropW
//---------------------------------------------------------------------------


_FX BOOL Gui_SetPropW(HWND hWnd, const WCHAR *lpString, HANDLE hData)
{
    if (! Gui_OpenAllWinClasses) {

        if (! Gui_IsWindowAccessible(hWnd)) {
            SetLastError(ERROR_INVALID_WINDOW_HANDLE);
            return FALSE;
        }
    }

    lpString = Gui_ReplaceAtom(lpString);

    return __sys_SetPropW(hWnd, lpString, hData);
}


//---------------------------------------------------------------------------
// Gui_SetPropA
//---------------------------------------------------------------------------


_FX BOOL Gui_SetPropA(HWND hWnd, const UCHAR *lpString, HANDLE hData)
{
    if (! Gui_OpenAllWinClasses) {

        if (! Gui_IsWindowAccessible(hWnd)) {
            SetLastError(ERROR_INVALID_WINDOW_HANDLE);
            return FALSE;
        }
    }

    lpString = Gui_ReplaceAtom(lpString);

    return __sys_SetPropA(hWnd, lpString, hData);
}


//---------------------------------------------------------------------------
// Gui_RemovePropW
//---------------------------------------------------------------------------


_FX HANDLE Gui_RemovePropW(HWND hWnd, const WCHAR *lpString)
{
    if (! Gui_OpenAllWinClasses) {

        if (! Gui_IsWindowAccessible(hWnd)) {
            SetLastError(ERROR_INVALID_WINDOW_HANDLE);
            return NULL;
        }
    }

    lpString = Gui_ReplaceAtom(lpString);

    return __sys_RemovePropW(hWnd, lpString);
}


//---------------------------------------------------------------------------
// Gui_RemovePropA
//---------------------------------------------------------------------------


_FX HANDLE Gui_RemovePropA(HWND hWnd, const UCHAR *lpString)
{
    if (! Gui_OpenAllWinClasses) {

        if (! Gui_IsWindowAccessible(hWnd)) {
            SetLastError(ERROR_INVALID_WINDOW_HANDLE);
            return NULL;
        }
    }

    lpString = Gui_ReplaceAtom(lpString);

    return __sys_RemovePropA(hWnd, lpString);
}


//---------------------------------------------------------------------------
// Gui_GetLongCommon
//---------------------------------------------------------------------------


_FX ULONG_PTR Gui_GetLongCommon(HWND hWnd, int nIndex, ULONG which)
{
    ULONG_PTR result;
    ULONG LastError = GetLastError();

    if (!Gui_UseProxyService || Gui_IsSameBox(hWnd, NULL, NULL)) {

        //
        // if target window is in the same sandbox (i.e. within the
        // boundary of the job object) then issue a direct call
        //

        if (which == 'cl w')
            result = __sys_GetClassLongW(hWnd, nIndex);
        else if (which == 'cl a')
            result = __sys_GetClassLongA(hWnd, nIndex);

        else if (which == 'wl w')
            result = __sys_GetWindowLongW(hWnd, nIndex);
        else if (which == 'wl a')
            result = __sys_GetWindowLongA(hWnd, nIndex);

#ifdef _WIN64

        else if (which == 'clpw')
            result = __sys_GetClassLongPtrW(hWnd, nIndex);
        else if (which == 'clpa')
            result = __sys_GetClassLongPtrA(hWnd, nIndex);

        else if (which == 'wlpw')
            result = __sys_GetWindowLongPtrW(hWnd, nIndex);
        else if (which == 'wlpa')
            result = __sys_GetWindowLongPtrA(hWnd, nIndex);

#endif _WIN64

        else {
            result = 0;
            SetLastError(ERROR_INVALID_PARAMETER);
        }

    } else {

        //
        // if target window is outside the job, issue an indirect request
        //

        GUI_GET_WINDOW_LONG_REQ req;
        GUI_GET_WINDOW_LONG_RPL *rpl;
        ULONG error;

        req.msgid = GUI_GET_WINDOW_LONG;
        req.error = LastError;
        req.hwnd = (ULONG)(ULONG_PTR)hWnd;
        req.index = nIndex;
        req.which = which;
        rpl = Gui_CallProxy(
                &req, sizeof(req), sizeof(GUI_GET_WINDOW_LONG_RPL));
        if (! rpl)
            return FALSE;

        result = (ULONG_PTR)rpl->result;
        error = rpl->error;
        Dll_Free(rpl);
        SetLastError(error);
    }

    return result;
}


//---------------------------------------------------------------------------
// Gui_GetWindowLong_DragDrop
//---------------------------------------------------------------------------


_FX ULONG_PTR Gui_GetWindowLong_DragDrop(
    HWND hWnd, int nIndex, ULONG_PTR value)
{
    if (nIndex == GWL_EXSTYLE && (value & WS_EX_ACCEPTFILES)) {

        //
        // if the style WS_EX_ACCEPTFILES is there because we put it
        // there (see Ole_RegisterDragDrop) to make it possible for
        // unsandboxed windows to drop files in sandboxed windows,
        // then hide it from sandboxed processes (because they can
        // find and use the sandboxed IDropTarget)
        //

        void *pDropTarget =
                    __sys_GetPropW(hWnd, (LPCWSTR)Gui_DropTargetProp_Atom);
        if (pDropTarget)
            value &= ~WS_EX_ACCEPTFILES;
    }

    return value;
}


//---------------------------------------------------------------------------
// Gui_SetWindowLong_DragDrop
//---------------------------------------------------------------------------


_FX ULONG_PTR Gui_SetWindowLong_DragDrop(
    HWND hWnd, int nIndex, ULONG_PTR dwNew, ULONG_PTR value)
{
    if (nIndex == GWL_EXSTYLE) {

        if (dwNew & WS_EX_ACCEPTFILES) {

            //
            // if the app is explicitly setting the WS_EX_ACCEPTFILES flag
            // then make sure we are not trying to handle WM_DROPFILES on
            // its behalf
            //
            // Note:  Updated to not remove our property.  With Win10 Creator's
            //        update and Win8.1, drag-n-drop was not working because
            //        we were removing it below and thus the IDropTarget was
            //        not found in Ole_DoDragDrop().
            //

            if (Gui_DropTargetProp_Atom)
            {
                // __sys_RemovePropW(hWnd, (LPCWSTR)Gui_DropTargetProp_Atom);
            }

        } else {

            //
            // if the app does a SetWindowLong(GetWindowLong(), ...)
            // sequence on the window, it will clear our WS_EX_ACCEPTFILES
            // flag (per Gui_GetWindowLong_DragDrop) so apply it again
            //

            void *pDropTarget =
                    __sys_GetPropW(hWnd, (LPCWSTR)Gui_DropTargetProp_Atom);
            if (pDropTarget) {
               dwNew |= WS_EX_ACCEPTFILES;
                __sys_SetWindowLongW(hWnd, nIndex, (ULONG)dwNew);
            }
        }
    }

    return value;
}


//---------------------------------------------------------------------------
// Gui_GetWindowLongW
//---------------------------------------------------------------------------


_FX ULONG Gui_GetWindowLongW(HWND hWnd, int nIndex)
{
    ULONG value;
    Gui_InitPropAtoms();
    //value = __sys_GetWindowLongW(hWnd, nIndex);
    value = (ULONG)Gui_GetLongCommon(hWnd, nIndex, 'wl w');

#ifndef _WIN64

    if (Gui_RenameClasses && (nIndex == GWLP_WNDPROC)) {

        //
        // prevent caller from seeing our window procedure
        //

        WNDPROC w = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewW_Atom);
        WNDPROC a = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewA_Atom);

        if ((w && value == (ULONG_PTR)w) || (a && value == (ULONG_PTR)a)) {

            value = (ULONG_PTR)__sys_GetPropW(
                                    hWnd, (LPCWSTR)Gui_WindowProcOldW_Atom);
        }
    }

#endif ! _WIN64

    return (ULONG)Gui_GetWindowLong_DragDrop(hWnd, nIndex, value);
}


//---------------------------------------------------------------------------
// Gui_GetWindowLongA
//---------------------------------------------------------------------------


_FX ULONG Gui_GetWindowLongA(HWND hWnd, int nIndex)
{
    ULONG value;
    Gui_InitPropAtoms();
    //value = __sys_GetWindowLongA(hWnd, nIndex);
    value = (ULONG)Gui_GetLongCommon(hWnd, nIndex, 'wl a');

#ifndef _WIN64

    if (Gui_RenameClasses && (nIndex == GWLP_WNDPROC)) {

        //
        // prevent caller from seeing our window procedure
        //

        WNDPROC w = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewW_Atom);
        WNDPROC a = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewA_Atom);

        if ((w && value == (ULONG_PTR)w) || (a && value == (ULONG_PTR)a)) {

            value = (ULONG_PTR)__sys_GetPropW(
                                    hWnd, (LPCWSTR)Gui_WindowProcOldA_Atom);
        }
    }

#endif ! _WIN64

    return (ULONG)Gui_GetWindowLong_DragDrop(hWnd, nIndex, value);
}


//---------------------------------------------------------------------------
// Gui_SetWindowLongW
//---------------------------------------------------------------------------


_FX ULONG Gui_SetWindowLongW(HWND hWnd, int nIndex, ULONG dwNew)
{
    ULONG value;
    Gui_InitPropAtoms();
    value = __sys_SetWindowLongW(hWnd, nIndex, dwNew);

#ifndef _WIN64

    if (Gui_RenameClasses && (nIndex == GWLP_WNDPROC)) {

        //
        // replace window procedure, then override the new procedure
        //

        WNDPROC w = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewW_Atom);
        WNDPROC a = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewA_Atom);

        if ((w && value == (ULONG_PTR)w) || (a && value == (ULONG_PTR)a)) {

            value = (ULONG_PTR)__sys_GetPropW(
                                    hWnd, (LPCWSTR)Gui_WindowProcOldW_Atom);

            Gui_SetWindowProc(hWnd, TRUE);
        }
    }

#endif ! _WIN64

    return (ULONG)Gui_SetWindowLong_DragDrop(hWnd, nIndex, dwNew, value);
}


//---------------------------------------------------------------------------
// Gui_SetWindowLongA
//---------------------------------------------------------------------------


_FX ULONG Gui_SetWindowLongA(HWND hWnd, int nIndex, ULONG dwNew)
{
    ULONG value;
    Gui_InitPropAtoms();
    value = __sys_SetWindowLongA(hWnd, nIndex, dwNew);

#ifndef _WIN64

    if (Gui_RenameClasses && (nIndex == GWLP_WNDPROC)) {

        //
        // replace window procedure, then override the new procedure
        //

        WNDPROC w = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewW_Atom);
        WNDPROC a = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewA_Atom);

        if ((w && value == (ULONG_PTR)w) || (a && value == (ULONG_PTR)a)) {

            value = (ULONG_PTR)__sys_GetPropW(
                                    hWnd, (LPCWSTR)Gui_WindowProcOldA_Atom);

            Gui_SetWindowProc(hWnd, TRUE);
        }
    }

#endif ! _WIN64

    return (ULONG)Gui_SetWindowLong_DragDrop(hWnd, nIndex, dwNew, value);
}


//---------------------------------------------------------------------------
// Gui_GetClassLongW
//---------------------------------------------------------------------------


_FX ULONG Gui_GetClassLongW(HWND hWnd, int nIndex)
{
    return (ULONG)Gui_GetLongCommon(hWnd, nIndex, 'cl w');
}


//---------------------------------------------------------------------------
// Gui_GetClassLongA
//---------------------------------------------------------------------------


_FX ULONG Gui_GetClassLongA(HWND hWnd, int nIndex)
{
    return (ULONG)Gui_GetLongCommon(hWnd, nIndex, 'cl a');
}


//---------------------------------------------------------------------------
// Begin 64-bit Get/SetWindowLongPtr functions
//---------------------------------------------------------------------------


#ifdef _WIN64


//---------------------------------------------------------------------------
// Gui_GetWindowLongPtrW
//---------------------------------------------------------------------------


_FX ULONG_PTR Gui_GetWindowLongPtrW(HWND hWnd, int nIndex)
{
    ULONG_PTR value;
    Gui_InitPropAtoms();
    //value = (ULONG_PTR)__sys_GetWindowLongPtrW(hWnd, nIndex);
    value = (ULONG_PTR)Gui_GetLongCommon(hWnd, nIndex, 'wlpw');

    if (Gui_RenameClasses && (nIndex == GWLP_WNDPROC)) {

        //
        // prevent caller from seeing our window procedure
        //

        WNDPROC w = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewW_Atom);
        WNDPROC a = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewA_Atom);

        if ((w && value == (ULONG_PTR)w) || (a && value == (ULONG_PTR)a)) {

            value = (ULONG_PTR)__sys_GetPropW(
                                    hWnd, (LPCWSTR)Gui_WindowProcOldW_Atom);
        }
    }

    return Gui_GetWindowLong_DragDrop(hWnd, nIndex, value);
}


//---------------------------------------------------------------------------
// Gui_GetWindowLongPtrA
//---------------------------------------------------------------------------


_FX ULONG_PTR Gui_GetWindowLongPtrA(HWND hWnd, int nIndex)
{
    ULONG_PTR value;
    Gui_InitPropAtoms();
    //value = (ULONG_PTR)__sys_GetWindowLongPtrA(hWnd, nIndex);
    value = (ULONG_PTR)Gui_GetLongCommon(hWnd, nIndex, 'wlpa');

    if (Gui_RenameClasses && (nIndex == GWLP_WNDPROC)) {

        //
        // prevent caller from seeing our window procedure
        //

        WNDPROC w = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewW_Atom);
        WNDPROC a = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewA_Atom);

        if ((w && value == (ULONG_PTR)w) || (a && value == (ULONG_PTR)a)) {

            value = (ULONG_PTR)__sys_GetPropW(
                                    hWnd, (LPCWSTR)Gui_WindowProcOldA_Atom);
        }
    }

    return Gui_GetWindowLong_DragDrop(hWnd, nIndex, value);
}


//---------------------------------------------------------------------------
// Gui_SetWindowLongPtrW
//---------------------------------------------------------------------------


_FX ULONG_PTR Gui_SetWindowLongPtrW(HWND hWnd, int nIndex, ULONG_PTR dwNew)
{
    ULONG_PTR value;
    Gui_InitPropAtoms();
    value = __sys_SetWindowLongPtrW(hWnd, nIndex, dwNew);

    if (Gui_RenameClasses && (nIndex == GWLP_WNDPROC)) {

        //
        // replace window procedure, then override the new procedure
        //

        WNDPROC w = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewW_Atom);
        WNDPROC a = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewA_Atom);

        if ((w && value == (ULONG_PTR)w) || (a && value == (ULONG_PTR)a)) {

            value = (ULONG_PTR)__sys_GetPropW(
                                    hWnd, (LPCWSTR)Gui_WindowProcOldW_Atom);

            Gui_SetWindowProc(hWnd, TRUE);
        }
    }

    return Gui_SetWindowLong_DragDrop(hWnd, nIndex, dwNew, value);
}


//---------------------------------------------------------------------------
// Gui_SetWindowLongPtrA
//---------------------------------------------------------------------------


_FX ULONG_PTR Gui_SetWindowLongPtrA(HWND hWnd, int nIndex, ULONG_PTR dwNew)
{
    ULONG_PTR value;
    Gui_InitPropAtoms();
    value = __sys_SetWindowLongPtrA(hWnd, nIndex, dwNew);

    if (Gui_RenameClasses && (nIndex == GWLP_WNDPROC)) {

        //
        // replace window procedure, then override the new procedure
        //

        WNDPROC w = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewW_Atom);
        WNDPROC a = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewA_Atom);

        if ((w && value == (ULONG_PTR)w) || (a && value == (ULONG_PTR)a)) {

            value = (ULONG_PTR)__sys_GetPropW(
                                    hWnd, (LPCWSTR)Gui_WindowProcOldA_Atom);

            Gui_SetWindowProc(hWnd, TRUE);
        }
    }

    return Gui_SetWindowLong_DragDrop(hWnd, nIndex, dwNew, value);
}


//---------------------------------------------------------------------------
// Gui_GetClassLongPtrW
//---------------------------------------------------------------------------


_FX ULONG_PTR Gui_GetClassLongPtrW(HWND hWnd, int nIndex)
{
    return Gui_GetLongCommon(hWnd, nIndex, 'clpw');
}


//---------------------------------------------------------------------------
// Gui_GetClassLongPtrA
//---------------------------------------------------------------------------


_FX ULONG_PTR Gui_GetClassLongPtrA(HWND hWnd, int nIndex)
{
    return Gui_GetLongCommon(hWnd, nIndex, 'clpa');
}


//---------------------------------------------------------------------------
// Gui_SetWindowLong8
//---------------------------------------------------------------------------


_FX ULONG_PTR Gui_SetWindowLong8(
    HWND hWnd, int nIndex, ULONG_PTR dwNew, ULONG IsAscii)
{
    ULONG_PTR value;
    Gui_InitPropAtoms();
    value = __sys_SetWindowLong8(hWnd, nIndex, dwNew, IsAscii);

    if (Gui_RenameClasses && (nIndex == GWLP_WNDPROC)) {

        //
        // replace window procedure, then override the new procedure
        //

        WNDPROC w = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewW_Atom);
        WNDPROC a = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewA_Atom);

        if ((w && value == (ULONG_PTR)w) || (a && value == (ULONG_PTR)a)) {

            ATOM atom = IsAscii ? Gui_WindowProcOldA_Atom
                                : Gui_WindowProcOldW_Atom;
            value = (ULONG_PTR)__sys_GetPropW(hWnd, (LPCWSTR)atom);

            Gui_SetWindowProc(hWnd, TRUE);
        }
    }

    return Gui_SetWindowLong_DragDrop(hWnd, nIndex, dwNew, value);
}


//---------------------------------------------------------------------------
// Gui_Hook_SetWindowLong8
//---------------------------------------------------------------------------

#ifndef _M_ARM64
// $HookHack$ - Custom, not automated, Hook
_FX BOOLEAN Gui_Hook_SetWindowLong8(HMODULE module)
{
    //
    // on Windows 8.1, the SetWindowLongA and SetWindowLongW functions
    // are very short stubs that invoke user32!SetWindowLong.
    // the hook trampoline may override past the end of the short function,
    // and corrupt surrounding code.  to work around this, we analyze the
    // short functions in order to hook user32!SetWindowLong instead
    //
    // SetWindowLongA       mov r9d,1           41 B9 01 00 00 00
    //      (11 bytes)      jmp xxx             E9 xx xx xx xx
    // SetWindowLongW       xor r9d,r9d         45 33 C9
    //      (8 bytes)       jmp xxx             E9 xx xx xx xx
    //

    UCHAR *a = (UCHAR *)__sys_SetWindowLongA;
    UCHAR *w = (UCHAR *)__sys_SetWindowLongW;

    if (*(ULONG *)a == 0x0001B941 && *(ULONG *)w == 0xE9C93345) {

        LONG a_offset = *(LONG *)(a + 7);
        LONG w_offset = *(LONG *)(w + 4);

        if ((a + 11 + a_offset) == (w + 8 + w_offset)) {

            __sys_SetWindowLong8 = (P_SetWindowLong8)(w + 8 + w_offset);

            SBIEDLL_HOOK_GUI(SetWindowLong8);

            return TRUE;
        }
    }

    //windows 10 build 10147 no longer has the wrapper for SetWindowLongW.
    //SetWindowLongA remains the same as the previous version but points to a
    //separate function 
    else if (*(ULONG *)a == 0x0001B941) {
        LONG a_offset = *(LONG *)(a + 7);
        __sys_SetWindowLong8 = (P_SetWindowLong8)w;
        if ((a + 11 + a_offset) !=  w) {
            __sys_SetWindowLongA =  (P_SetWindowLong)(a + 11 + a_offset);
        }
        return TRUE;
    }
    
    return FALSE;
}
#endif

//---------------------------------------------------------------------------
// Gui_SetWindowLongPtr8
//---------------------------------------------------------------------------


_FX ULONG_PTR Gui_SetWindowLongPtr8(
    HWND hWnd, int nIndex, ULONG_PTR dwNew, ULONG IsAscii)
{
    ULONG_PTR value;
    Gui_InitPropAtoms();
    value = __sys_SetWindowLongPtr8(hWnd, nIndex, dwNew, IsAscii);

    if (Gui_RenameClasses && (nIndex == GWLP_WNDPROC)) {

        //
        // replace window procedure, then override the new procedure
        //

        WNDPROC w = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewW_Atom);
        WNDPROC a = __sys_GetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewA_Atom);

        if ((w && value == (ULONG_PTR)w) || (a && value == (ULONG_PTR)a)) {

            ATOM atom = IsAscii ? Gui_WindowProcOldA_Atom
                                : Gui_WindowProcOldW_Atom;
            value = (ULONG_PTR)__sys_GetPropW(hWnd, (LPCWSTR)atom);

            Gui_SetWindowProc(hWnd, TRUE);
        }
    }

    return Gui_SetWindowLong_DragDrop(hWnd, nIndex, dwNew, value);
}


//---------------------------------------------------------------------------
// Gui_Hook_SetWindowLongPtr8
//---------------------------------------------------------------------------

#ifndef _M_ARM64
// $HookHack$ - Custom, not automated, Hook
_FX BOOLEAN Gui_Hook_SetWindowLongPtr8(HMODULE module)
{
    //
    // on Windows 8, the SetWindowLongPtrA and SetWindowLongPtrW functions
    // are very short stubs that invoke user32!SetWindowLongPtr.
    // the hook trampoline may override past the end of the short function,
    // and corrupt surrounding code.  to work around this, we analyze the
    // short functions in order to hook user32!SetWindowLongPtr instead
    //
    // SetWindowLongPtrA    mov r9d,1           41 B9 01 00 00 00
    //      (11 bytes)      jmp xxx             E9 xx xx xx xx
    // SetWindowLongPtrW    xor r9d,r9d         45 33 C9
    //      (8 bytes)       jmp xxx             E9 xx xx xx xx
    //

    UCHAR *a = (UCHAR *)__sys_SetWindowLongPtrA;
    UCHAR *w = (UCHAR *)__sys_SetWindowLongPtrW;

    if (*(ULONG *)a == 0x0001B941 && *(ULONG *)w == 0xE9C93345) {

        LONG a_offset = *(LONG *)(a + 7);
        LONG w_offset = *(LONG *)(w + 4);

        if ((a + 11 + a_offset) == (w + 8 + w_offset)) {

            __sys_SetWindowLongPtr8 =
                                (P_SetWindowLongPtr8)(w + 8 + w_offset);

            SBIEDLL_HOOK_GUI(SetWindowLongPtr8);

            return TRUE;
        }
    }

    return FALSE;
}
#endif

//---------------------------------------------------------------------------
// End 64-bit Get/SetWindowLongPtr functions
//---------------------------------------------------------------------------


#endif _WIN64


//---------------------------------------------------------------------------
// Gui_SetWindowProc
//---------------------------------------------------------------------------


_FX void Gui_SetWindowProc(HWND hWnd, BOOLEAN force)
{
    void *pWindowProcW;
    void *pWindowProcA;

    //
    // ignore windows that are not top level windows, unless forced
    //
    // note that SetWindowLongXxx functions specify the force parameter
    // when they re-apply our wndproc after the caller program requested
    // to replace our wndproc
    //

    if ((! force) && __sys_GetParent(hWnd))
        return;

    Gui_InitPropAtoms();

    //
    // replace window procedure while recording the results of
    // GetWindowLong A+W before and after the replace
    //

    pWindowProcW = (void *)__sys_GetWindowLongPtrW(hWnd, GWLP_WNDPROC);
    pWindowProcA = (void *)__sys_GetWindowLongPtrA(hWnd, GWLP_WNDPROC);

    if (pWindowProcW == Gui_WindowProcW || pWindowProcA == Gui_WindowProcA)
        return;

    __sys_SetPropW(hWnd, (LPCWSTR)Gui_WindowProcOldW_Atom, pWindowProcW);
    __sys_SetPropW(hWnd, (LPCWSTR)Gui_WindowProcOldA_Atom, pWindowProcA);

    if (__sys_IsWindowUnicode(hWnd)) {
        __sys_SetWindowLongPtrW(
                        hWnd, GWLP_WNDPROC, (ULONG_PTR)Gui_WindowProcW);
    } else {
        __sys_SetWindowLongPtrA(
                        hWnd, GWLP_WNDPROC, (ULONG_PTR)Gui_WindowProcA);
    }

    pWindowProcW = (void *)__sys_GetWindowLongPtrW(hWnd, GWLP_WNDPROC);
    pWindowProcA = (void *)__sys_GetWindowLongPtrA(hWnd, GWLP_WNDPROC);

    __sys_SetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewW_Atom, pWindowProcW);
    __sys_SetPropW(hWnd, (LPCWSTR)Gui_WindowProcNewA_Atom, pWindowProcA);
}
