/*****************************************************************************\
*
* Module: hook.c
*
*   Contains the message hooking functions for the Windows debugging Spy
*   SDK applet.
*
* History:
*
\*****************************************************************************/

#include "spy.h"


PRIVATE BOOL gfProcessHooks = TRUE;



/*****************************************************************************\
* CreateHookThread
*
*
*
* Arguments:
*
*
*
* Returns:
*
*
\*****************************************************************************/

BOOL
CreateHookThread(
    VOID
    )
{
    WNDCLASS wc;
    DWORD Id;

    //
    // Register a class for the hook stuff to forward its messages to.
    //
    wc.hCursor        = NULL;    // this window never shown, so no
    wc.hIcon          = NULL;    // cursor or icon are necessary
    wc.lpszMenuName   = NULL;
    wc.lpszClassName  = HOOKWINDOWCLASS;
    wc.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hInstance      = ghInst;
    wc.style          = 0;
    wc.lpfnWndProc    = HookWndProc;
    wc.cbWndExtra     = sizeof(HWND) + sizeof(HWND);
    wc.cbClsExtra     = 0;

    if (!RegisterClass(&wc))
        return FALSE;

    //
    // Now create another thread to handle the new queue
    //
    if (!(ghHookThread = CreateThread(NULL, 0, HookMain,
        0L, STANDARD_RIGHTS_REQUIRED, &Id)))
        return FALSE;

    return TRUE;
}



/*****************************************************************************\
* HookMain
*
*
*
* Arguments:
*
*
*
* Returns:
*
*
\*****************************************************************************/

DWORD
HookMain(
    LPVOID lpv
    )
{
    MSG msg;

    //
    // Create a hidden window for all to find, but not to see
    //
    ghwndSpyHook = CreateWindow(HOOKWINDOWCLASS, HOOKWINDOWNAME,
        WS_OVERLAPPEDWINDOW,
        0, 0, 0, 0,
        (HWND) NULL,        /* no parent */
        (HMENU) NULL,       /* use class menu */
        (HANDLE) ghInst,    /* handle to window instance */
        (LPSTR) NULL        /* no params to pass on */
        );

    if (!ghwndSpyHook)
    {
        ExitThread(0);
    }

    SetWindowToSpyOn(HWND_ALL);

    //
    // Polling forwarded messages from hook's event queue
    //
    while (IsWindow(ghwndSpyHook) && GetMessage(&msg, ghwndSpyHook, 0, 0))
    {
        if (gfProcessHooks)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    ghwndSpyHook = NULL;

    return 0; // not reached
}



/*****************************************************************************\
* HookWndProc
*
*
*
* Arguments:
*
*
*
* Returns:
*
*
\*****************************************************************************/

LRESULT CALLBACK
HookWndProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    switch (msg)
    {
        case WM_COPYDATA:
            {
                MSG msgT;

                msgT.hwnd = (HWND)wParam;
                msgT.message = ((PCOPYDATASTRUCT)lParam)->dwData;
                msgT.wParam = ((PSPYMSGDATA)((PCOPYDATASTRUCT)lParam)->lpData)->wParam;
                msgT.lParam = ((PSPYMSGDATA)((PCOPYDATASTRUCT)lParam)->lpData)->lParam;
//DbgPrintf("S Received Message hwnd:%8.8x msg:%d", msgT.hwnd, msgT.message);
                PrintMsg(&msgT);
//DbgPrintf("S Printed Message hwnd:%8.8x msg:%d", msgT.hwnd, msgT.message);
            }

            return TRUE;

        case WM_CREATE:
            //
            // Initialize the second HWND in the window words to be the
            // window handle of the spy app.  This will be queried by
            // the hook DLL.
            //
            SetWindowLong(hwnd, sizeof(HWND), (LONG)ghwndSpyApp);
            return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        case WM_NCDESTROY:
            gfProcessHooks = FALSE;
            break;
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}



/*****************************************************************************\
* SetSpyHook
*
* Sets the windows hooks used to trap the messages.  After this
* is called with a TRUE for fSet, the messages will start flowing
* through the hook DLL.
*
* Arguments:
*   BOOL fSet - TRUE to hook, FALSE to unhook.
*
* Returns:
*   TRUE if successful.
*
\*****************************************************************************/

BOOL
SetSpyHook(
    BOOL fSet
    )
{
    static HHOOK hhkGetMessage = NULL;
    static HHOOK hhkCallWndProc = NULL;
    static HANDLE hmodHook;

    if (fSet)
    {
        if (!hmodHook)
        {
            if (!(hmodHook = LoadLibrary("hook")))
            {
                Message(MB_OK | MB_ICONEXCLAMATION, 
                        LoadResourceString(IDS_ERROR_CANT_LOAD_DLL));
                return FALSE;
            }
        }

        if (!hhkGetMessage)
        {
            if (!(hhkGetMessage = SetWindowsHookEx(WH_GETMESSAGE,
                (HOOKPROC)GetProcAddress(hmodHook, "SpyGetMsgProc"), hmodHook, 0)))
            {
                return FALSE;
            }
        }

        if (!hhkCallWndProc)
        {
            if (!(hhkCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC,
                (HOOKPROC)GetProcAddress(hmodHook, "SpyCallWndProc"), hmodHook, 0)))
            {
                UnhookWindowsHookEx(hhkGetMessage);
                return FALSE;
            }
        }
    }
    else
    {
        if (hhkGetMessage)
        {
            UnhookWindowsHookEx(hhkGetMessage);
            hhkGetMessage = NULL;
        }

        if (hhkCallWndProc)
        {
            UnhookWindowsHookEx(hhkCallWndProc);
            hhkCallWndProc = NULL;
        }
    }

    return TRUE;
}



/*****************************************************************************\
* SetWindowToSpyOn
*
* Sets the current window to spy on to the specified hwnd.  This hwnd can
* also be the special value HWND_ALL to specify that all windows should
* be spy'd upon.
*
* Arguments:
*   HWND hwndSpyingOn - Window handle to spy on, or HWND_ALL for all windows.
*
\*****************************************************************************/

VOID
SetWindowToSpyOn(
    HWND hwndSpyingOn
    )
{
    ghwndSpyingOn = hwndSpyingOn;
    gfSpyAll = (ghwndSpyingOn == HWND_ALL) ? TRUE : FALSE;
    SetWindowLong(ghwndSpyHook, 0, (LONG)ghwndSpyingOn);
    SetSpyCaption();
}



#ifdef DBG
/****************************************************************************
* DBGprintf
*
* This debugging function prints out a string to the debug output.
* An optional set of substitutional parameters can be specified,
* and the final output will be the processed result of these combined
* with the format string, just like printf.  A newline is always
* output after every call to this function.
*
* Arguments:
*   LPTSTR fmt - Format string (printf style).
*   ...        - Variable number of arguments.
*
* History:
*  28-Aug-1990  Byron Dazey - Created
****************************************************************************/

VOID DbgPrintf(
    LPTSTR fmt,
    ...
    )
{
    va_list marker;
    TCHAR szBuf[256];

    va_start(marker, fmt);
    wvsprintf(szBuf, fmt, marker);
    va_end(marker);

    OutputDebugString(szBuf);
    OutputDebugString(TEXT("\r\n"));
}
#endif