//////////////////////////////////////////////////////////////////////
// Spy2+ (C) 2005 - Dr_Progz - dr_progz@hotmail.com
//
// La mojorit?des id閑s sont tir閑s du programme Window Finder
// Window Finder (C) 2001 Lim Bio Liong.
//

//////////////////////////////////////////////////////////////////////
// Les ent阾es
//
#include <windows.h>
#include "resource.h"

//////////////////////////////////////////////////////////////////////
// Les definitions
//
#define APP_MUTEX_NAME            "SPY2PMUTEX"
#define    DEFEXSTYLE_COUNT        3
#define    EXSTYLE_COUNT            18
#define    STYLE_COUNT                20

//////////////////////////////////////////////////////////////////////
// Les d閏larations
//
typedef struct tagSTYLE
{
    DWORD    dwStyle;
    LPTSTR    pStyleName;
}STYLE;

STYLE DefWndExStyle[] = 
{
    {WS_EX_LEFT                , "WS_EX_LEFT" }, 
    {WS_EX_LTRREADING        , "WS_EX_LTRREADING" }, 
    {WS_EX_RIGHTSCROLLBAR    , "WS_EX_RIGHTSCROLLBAR" }
};

STYLE WndExStyle[] = 
{
    // Ce sont des combinaisons de style
    //{WS_EX_OVERLAPPEDWINDOW , "WS_EX_OVERLAPPEDWINDOW"    }, 
    //{WS_EX_PALETTEWINDOW    , "WS_EX_PALETTEWINDOW"        }, 
    {WS_EX_ACCEPTFILES        , "WS_EX_ACCEPTFILES"        }, 
    {WS_EX_APPWINDOW        , "WS_EX_APPWINDOW"            }, 
    {WS_EX_CLIENTEDGE        , "WS_EX_CLIENTEDGE"        }, 
    {WS_EX_CONTEXTHELP        , "WS_EX_CONTEXTHELP"        }, 
    {WS_EX_CONTROLPARENT    , "WS_EX_CONTROLPARENT"        },
    {WS_EX_DLGMODALFRAME    , "WS_EX_DLGMODALFRAME"        }, 
    {WS_EX_LEFTSCROLLBAR    , "WS_EX_LEFTSCROLLBAR"        }, 
    {WS_EX_LTRREADING        , "WS_EX_LTRREADING"        }, 
    {WS_EX_MDICHILD            , "WS_EX_MDICHILD"            }, 
    {WS_EX_NOPARENTNOTIFY    , "WS_EX_NOPARENTNOTIFY"    }, 
    {WS_EX_RIGHT            , "WS_EX_RIGHT"                }, 
    {WS_EX_RIGHTSCROLLBAR    , "WS_EX_RIGHTSCROLLBAR"    },
    {WS_EX_RTLREADING        , "WS_EX_RTLREADING"        }, 
    {WS_EX_STATICEDGE        , "WS_EX_STATICEDGE"        }, 
    {WS_EX_TOOLWINDOW        , "WS_EX_TOOLWINDOW"        }, 
    {WS_EX_TOPMOST            , "WS_EX_TOPMOST"            }, 
    {WS_EX_TRANSPARENT        , "WS_EX_TRANSPARENT"        }, 
    {WS_EX_WINDOWEDGE        , "WS_EX_WINDOWEDGE"        }
};

STYLE WndStyle[] = 
{
    // Ce sont des combinaisons de style
    //{WS_OVERLAPPEDWINDOW , "WS_OVERLAPPEDWINDOW" }, 
    //{WS_POPUPWINDOW         , "WS_POPUPWINDOW"         }, 
    {WS_CAPTION             , "WS_CAPTION"             }, 
    {WS_BORDER             , "WS_BORDER"             }, 
    {WS_CLIPCHILDREN     , "WS_CLIPCHILDREN"     },
    {WS_CLIPSIBLINGS     , "WS_CLIPSIBLINGS"     }, 
    {WS_CHILDWINDOW         , "WS_CHILDWINDOW"         }, // meme que WS_CHILD
    {WS_DISABLED         , "WS_DISABLED"         }, 
    {WS_DLGFRAME         , "WS_DLGFRAME"         }, 
    {WS_GROUP             , "WS_GROUP"             }, 
    {WS_HSCROLL             , "WS_HSCROLL"             }, 
    {WS_ICONIC             , "WS_ICONIC"             }, 
    {WS_MAXIMIZE         , "WS_MAXIMIZE"         }, 
    {WS_MAXIMIZEBOX         , "WS_MAXIMIZEBOX"         }, 
    {WS_MINIMIZE         , "WS_MINIMIZE"         }, 
    {WS_MINIMIZEBOX         , "WS_MINIMIZEBOX"         },
    {WS_POPUP             , "WS_POPUP"             }, 
    {WS_SYSMENU             , "WS_SYSMENU"             }, 
    {WS_TABSTOP             , "WS_TABSTOP"             }, 
    {WS_THICKFRAME         , "WS_THICKFRAME"         }, 
    {WS_VISIBLE             , "WS_VISIBLE"             }, 
    {WS_VSCROLL             , "WS_VSCROLL"             }
};

HINSTANCE        m_hInstance           = NULL;
HWND            m_hWndFoundWindow     = NULL;
HANDLE            m_hApplicationMutex   = NULL;
BOOL            m_bStartSearchWindow  = FALSE;
HCURSOR            m_hCursorSearchWindow = NULL;
HCURSOR            m_hCursorPrevious     = NULL;
HBITMAP            m_hBitmapFinderToolFilled;
HBITMAP            m_hBitmapFinderToolEmpty;

//////////////////////////////////////////////////////////////////////
// InitializeApplication: Intialise l'application
//
BOOL InitializeApplication(HINSTANCE hThisInst, HINSTANCE hPrevInst, 
                           LPTSTR lpszArgs, int nWinMode)
{ 
    BOOL        bRet        = FALSE;
    DWORD       dwLastError = 0;

    // Creation du mutex de l'application, cela permet de ne pas 
    // executer a la fois deux instances de cet application.
    m_hApplicationMutex = CreateMutex((LPSECURITY_ATTRIBUTES)NULL, 
        (BOOL)TRUE, (LPCTSTR)APP_MUTEX_NAME);

    dwLastError = GetLastError();

    // Si on a pas pus cr閑r le mutex, on quite.
    if (m_hApplicationMutex == NULL)
    {
        bRet = FALSE;
        goto InitializeApplication_0;
    }

    // Si le mutex existe deja, on quite.
    if (dwLastError == ERROR_ALREADY_EXISTS)
    {
        CloseHandle(m_hApplicationMutex);
        m_hApplicationMutex = NULL;
        bRet = FALSE;
        goto InitializeApplication_0;
    }

    // Tout est bien pass?
    bRet = TRUE;

InitializeApplication_0:
    return bRet; 
}

//////////////////////////////////////////////////////////////////////
// UninitializeApplication
//
BOOL UninitializeApplication()
{
    if(m_hApplicationMutex)
    {
        ReleaseMutex(m_hApplicationMutex);
        CloseHandle(m_hApplicationMutex);
        m_hApplicationMutex = NULL;
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////
// InitialiseResources: Initialise les resources
//
BOOL InitialiseResources()
{
    BOOL bRet = FALSE;

    m_hCursorSearchWindow = LoadCursor(m_hInstance, 
        MAKEINTRESOURCE(IDC_FIND_WINDOW));
    if(m_hCursorSearchWindow == NULL)
    {
        bRet = FALSE;
        goto InitialiseResources_0;
    }

    m_hBitmapFinderToolFilled = LoadBitmap(m_hInstance, 
        MAKEINTRESOURCE(IDB_FINDER_FILLED));
    if(m_hBitmapFinderToolFilled == NULL)
    {
        bRet = FALSE;
        goto InitialiseResources_0;
    }

    m_hBitmapFinderToolEmpty = LoadBitmap(m_hInstance, 
        MAKEINTRESOURCE(IDB_FINDER_EMPTY));
    if(m_hBitmapFinderToolEmpty == NULL)
    {
        bRet = FALSE;
        goto InitialiseResources_0;
    }

    bRet = TRUE;

InitialiseResources_0:
    return bRet;
}

//////////////////////////////////////////////////////////////////////
// UninitialiseResources: Lib鑢e les resources
//
BOOL UninitialiseResources()
{
    BOOL bRet = TRUE;

    if(m_hBitmapFinderToolFilled)
    {
        DeleteObject(m_hBitmapFinderToolFilled);
        m_hBitmapFinderToolFilled = NULL;
    }

    if(m_hBitmapFinderToolEmpty)
    {
        DeleteObject(m_hBitmapFinderToolEmpty);
        m_hBitmapFinderToolEmpty = NULL;
    }
    return bRet;
}

//////////////////////////////////////////////////////////////////////
// StartSearchWindow: D閙arre la recherche
//
int StartSearchWindow(HWND hDlg)
{
    int   nRet = 0;
    HWND  hWndFT = NULL;
    RECT  rcClient;
    POINT ptScreen;

    // On a commencer la recherche
    m_bStartSearchWindow = TRUE;

    SendDlgItemMessage(hDlg, IDC_FINDER_TOOL, STM_SETIMAGE, 
        (WPARAM)IMAGE_BITMAP, (LPARAM)m_hBitmapFinderToolEmpty);

    hWndFT = GetDlgItem(hDlg, IDC_FINDER_TOOL);
    if(hWndFT)
    {
        // Positionne le curseur sur le centre de 
        // IDC_FINDER_TOOL: c.?d mettre le curseur
        // sur l'image du centre
        GetWindowRect(hWndFT, &rcClient);
        ptScreen.x = rcClient.left + 15; // regarder le point du centre de 
        ptScreen.y = rcClient.top + 18;  // l'image "IDB_FINDER_FILLED"
        SetCursorPos(ptScreen.x, ptScreen.y);
    }

    // Modification du curseur
    if(m_hCursorSearchWindow)
        m_hCursorPrevious = SetCursor(m_hCursorSearchWindow);
    else
        m_hCursorPrevious = NULL;

    // NB : Capture de tous les messages de la souris
    SetCapture(hDlg);
  
    return nRet;
}

//////////////////////////////////////////////////////////////////////
//
//
BOOL CheckWindowValidity(HWND hDlg, HWND hWndToCheck)
{
    BOOL bRet     = TRUE;

    // la fen阾re ne doit pas 阾re NULL.
    if (hWndToCheck == NULL)
    {
        bRet = FALSE;
        goto CheckWindowValidity_0;
    }

    // la fen阾re ne doit pas 阾re valide.
    if (IsWindow(hWndToCheck) == FALSE)
    {
        bRet = FALSE;
        goto CheckWindowValidity_0;
    }

    // la fen阾re ne doit pas l'ancienne fen阾re.
    if(hWndToCheck == m_hWndFoundWindow)
    {
        bRet = FALSE;
        goto CheckWindowValidity_0;
    }
  
    // la fen阾re ne doit pas 阾re la fen阾re de notre application.
    if(hWndToCheck == hDlg)
    {
        bRet = FALSE;
        goto CheckWindowValidity_0;
    }

    // Aussi elle ne doit pas 阾re l'une des fen阾res filles
    // de notre application.
    if(GetParent(hWndToCheck) == hDlg)
    {
        bRet = FALSE;
        goto CheckWindowValidity_0;
    }

CheckWindowValidity_0:
    return bRet;
}

//////////////////////////////////////////////////////////////////////
// DisplayInfoOnFoundWindow: Affiche les informations
//
void DisplayInfoOnFoundWindow(HWND hDlg, HWND hWndFoundWindow)
{
    char    szClassName[100];
    int        nMaxCount, i;
    char*    pCaption = NULL;
    RECT    rcWindow;
    DWORD    dwWndParent, dwWndProc, dwThreadID, dwProcessID;
    DWORD    dwStyle = 0, dwExStyle = 0;
    char    szText[256];

    // Le non de la class.
    GetClassName(hWndFoundWindow, szClassName, 
        99); // 99 = sizeof(szClassName) - 1

    // Le texte
    nMaxCount = GetWindowTextLength(hWndFoundWindow) + 1;
    if(nMaxCount)
    {
        pCaption = new char[nMaxCount];
        GetWindowText(hWndFoundWindow, pCaption, nMaxCount);
    }

    // Le rectange (Coordonn閑s ecran).
    GetWindowRect(hWndFoundWindow, &rcWindow);

    // fen阾re parent, adresse de la fonction de rappel
    // et l'identificateur du processus et du thread
    dwWndParent = GetWindowLong(hWndFoundWindow, GWL_HWNDPARENT);
    dwWndProc = GetWindowLong(hWndFoundWindow, GWL_WNDPROC);
    dwThreadID = GetWindowThreadProcessId(hWndFoundWindow, 
        &dwProcessID);

    // Style est ExStyle
    dwStyle = GetWindowLong(hWndFoundWindow, GWL_STYLE);
    dwExStyle = GetWindowLong(hWndFoundWindow, GWL_EXSTYLE);

    // Affichage des informations
    wsprintf(szText, "%08X", hWndFoundWindow);
    SetDlgItemText(hDlg, IDC_HANDLE, szText);

    // Nom de la class
    SetDlgItemText(hDlg, IDC_CLASS, szClassName);

    // Texte
    if(pCaption)
    {
        SetDlgItemText(hDlg, IDC_CAPTION, pCaption);
        delete [] pCaption;
    }

    // Rectangle
    wsprintf(szText, "{%d, %d, %d, %d} - %dx%d", 
        rcWindow.left, rcWindow.top, rcWindow.right, rcWindow.bottom 
        , rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top);
    SetDlgItemText(hDlg, IDC_RECT, szText);

    // fen阾re parent
    wsprintf(szText, "%08X", dwWndParent);
    SetDlgItemText(hDlg, IDC_PARENT, szText);
    // Fonction de rappel
    wsprintf(szText, "%08X", dwWndProc);
    SetDlgItemText(hDlg, IDC_WNDPROC, szText);
    // l'identificateur du processu
    wsprintf(szText, "%08X", dwProcessID);
    SetDlgItemText(hDlg, IDC_PROCESSID, szText);
    // l'identificateur du thread
    wsprintf(szText, "%08X", dwThreadID);
    SetDlgItemText(hDlg, IDC_THREADID, szText);

    // Netoyage des 2 list box
    SendDlgItemMessage(hDlg, IDC_STYLE_LIST, LB_RESETCONTENT, 0, 0);
    SendDlgItemMessage(hDlg, IDC_EXSTYLE_LIST, LB_RESETCONTENT, 0, 0);

    // Le style
    wsprintf(szText, "%08X", dwStyle);
    SetDlgItemText(hDlg, IDC_STYLE, szText);
    // Ajout des valeurs par defaut aa la list box
    SendDlgItemMessage(hDlg, IDC_STYLE_LIST, LB_ADDSTRING, 
        0, (LPARAM)(LPCTSTR)"WS_OVERLAPPED");
    // Detection des styles
    for(i = 0; i < STYLE_COUNT; i++)
    {
        if(dwStyle & WndStyle[i].dwStyle)
        {
            SendDlgItemMessage(hDlg, IDC_STYLE_LIST, LB_ADDSTRING, 
                0, (LPARAM)(LPCTSTR)WndStyle[i].pStyleName);
            dwStyle &= ~WndStyle[i].dwStyle;
        }
    }
    // Si il rest quelque chose (style personalis?
    if(dwStyle)
    {
        wsprintf(szText, "%08X", dwStyle);
        SendDlgItemMessage(hDlg, IDC_STYLE_LIST, LB_ADDSTRING, 
        0, (LPARAM)(LPCTSTR)szText);
    }
    
    // ExStyle
    wsprintf(szText, "%08X", dwExStyle);
    SetDlgItemText(hDlg, IDC_EXSTYLE, szText);
    // Ajout des valeurs par defaut aa la list box
    for(i = 0; i < DEFEXSTYLE_COUNT; i++)
        SendDlgItemMessage(hDlg, IDC_EXSTYLE_LIST, LB_ADDSTRING, 
        0, (LPARAM)(LPCTSTR)DefWndExStyle[i].pStyleName);

    // Detection des styles
    for(i = 0; i < EXSTYLE_COUNT; i++)
    {
        if(dwExStyle & WndExStyle[i].dwStyle)
        {
            SendDlgItemMessage(hDlg, IDC_EXSTYLE_LIST, LB_ADDSTRING, 
                0, (LPARAM)(LPCTSTR)WndExStyle[i].pStyleName);
            dwExStyle &= ~WndExStyle[i].dwStyle;
        }
    }
    // Si il rest quelque chose (style personalis?
    if(dwExStyle)
    {
        wsprintf(szText, "%08X", dwExStyle);
        SendDlgItemMessage(hDlg, IDC_EXSTYLE_LIST, LB_ADDSTRING, 
        0, (LPARAM)(LPCTSTR)szText);
    }
}

//////////////////////////////////////////////////////////////////////
// HighlightRect: trace un rectangle sur une fen阾re
//
void HighlightRect(HDC hDC, LPCRECT pRect)
{
    HBITMAP hBitmap = NULL;
    HBRUSH  hBrush = NULL, hOldBrush = NULL;

    static WORD _dotPatternBmp[8] = 
    { 
        0xff, 0xff, 0xff, 0xff, 
        0xff, 0xff, 0xff, 0xff
    };

    hBitmap = CreateBitmap(8, 8, 1, 1, _dotPatternBmp);
    hBrush = CreatePatternBrush(hBitmap);

    SetBrushOrgEx(hDC, pRect->left, pRect->top, 0);
    hOldBrush = (HBRUSH)SelectObject(hDC, hBrush);

    PatBlt(hDC, pRect->left, pRect->top, 
        pRect->right - pRect->left, 3, PATINVERT);

    PatBlt(hDC, pRect->left, pRect->top + 3, 
        3, pRect->bottom - pRect->top - 6, PATINVERT);

    PatBlt(hDC, pRect->left, pRect->bottom - 3, 
        pRect->right - pRect->left, 3, PATINVERT);

    PatBlt(hDC, pRect->right - 3, pRect->top + 3, 
        3, pRect->bottom - pRect->top - 6, PATINVERT);

    SelectObject(hDC, hOldBrush);
    
    DeleteObject(hBrush);
    DeleteObject(hBitmap);
}

//////////////////////////////////////////////////////////////////////
// RefreshWindow: supprime le rectangle dessin?sur hWndToBeRefreshed
//
void RefreshWindow(HWND hWndToBeRefreshed)
{
    HDC            hWindowDC  = NULL;
    RECT        rcWindow;

    hWindowDC = GetWindowDC(hWndToBeRefreshed);

    if(hWindowDC)
    {
        GetWindowRect(hWndToBeRefreshed, &rcWindow);
        OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);

        HighlightRect(hWindowDC, &rcWindow);

        ReleaseDC(hWndToBeRefreshed, hWindowDC);
    }
}

//////////////////////////////////////////////////////////////////////
// HighlightFoundWindow: Encadre la fenetre trouv閑
//
void HighlightFoundWindow(HWND hDlg, HWND hWndFoundWindow)
{
    HDC            hWindowDC  = NULL;
    RECT        rcWindow;

    hWindowDC = GetWindowDC(hWndFoundWindow);

    if(hWindowDC)
    {
        GetWindowRect(hWndFoundWindow, &rcWindow);
        OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);

        HighlightRect(hWindowDC, &rcWindow);

        ReleaseDC(hWndFoundWindow, hWindowDC);
    }
}

//////////////////////////////////////////////////////////////////////
//  OnMouseMove: Evenement WM_MOUSEMOVE
//
LRESULT OnMouseMove(HWND hDlg)
{
    POINT        ptScreen;
    HWND        hWndFoundWindow = NULL;

    // Nous devons utuliser GetCursorPos() ?la place
    // des valeurs de lParam
    GetCursorPos(&ptScreen);  

    // On determine la fen阾re qui est au dessous du curseur.
    hWndFoundWindow = WindowFromPoint(ptScreen);

    // Est-elle une fen阾re valide.
    if(CheckWindowValidity(hDlg, hWndFoundWindow))
    {
        // On a trouv?une fen阾re.

        // On affiche quelques informations
        DisplayInfoOnFoundWindow(hDlg, hWndFoundWindow);

        // Si une ancienne fen阾re est encadr? nous devons
        // supprim?le rectangle qui l'ancadre
        if(m_hWndFoundWindow)
            RefreshWindow(m_hWndFoundWindow);

         m_hWndFoundWindow = hWndFoundWindow;

        // Encadrer la fen阾re trouv
        HighlightFoundWindow(hDlg, m_hWndFoundWindow);
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////
// OnMouseMove: Evenement WM_LBUTTONUP
//
LRESULT OnMouseUp(HWND hDlg)
{
    if (m_hCursorPrevious)
        SetCursor(m_hCursorPrevious);

    // S'il y a une ancienne fen阾re, nous devons
    // supprim?le rectangle qui l'ancadre
    if (m_hWndFoundWindow)
        RefreshWindow(m_hWndFoundWindow);

    SendDlgItemMessage(hDlg, IDC_FINDER_TOOL, STM_SETIMAGE, 
        (WPARAM)IMAGE_BITMAP, (LPARAM)m_hBitmapFinderToolFilled);

    // NB : liberer la souris.
    ReleaseCapture();

    m_bStartSearchWindow = FALSE;

    return 0;
}

//////////////////////////////////////////////////////////////////////
// DialogProc: Fonction de rappel pour la boite de dialog
//
LRESULT CALLBACK DialogProc(HWND hDlg, UINT uMsg, 
                            WPARAM wParam, LPARAM lParam)
{
    BOOL bRet = FALSE;
    switch(uMsg)
    {
        case WM_COMMAND:
        {
            WORD wID = LOWORD(wParam);

            if((wID == IDC_OK) || (wID == IDC_CANCEL))
            {
                bRet = TRUE;
                EndDialog(hDlg, wID);
            }

            if(wID == IDC_FINDER_TOOL)
            {
                // Le controle static IDC_FINDER_TOOL ?le style 
                // SS_NOTIFY, donc il envoi le message WM_COMMAND
                // lors du clique au dessus
                bRet = TRUE;
                // Lancement de la recherche des fen阾res par 
                // la foction StartSearchWindow(HWND hDlg)
                StartSearchWindow(hDlg);
            }

            break;
        }

        case WM_MOUSEMOVE:
        {
            bRet = TRUE;
            if(m_bStartSearchWindow)
                OnMouseMove(hDlg);
            break;
        }

        case WM_LBUTTONUP:
        {
            bRet = TRUE;
            if(m_bStartSearchWindow)
                OnMouseUp(hDlg);
            break;
        }

        case WM_CLOSE:
        {
            EndDialog(hDlg, 0);
            break;
        }
    }
    return bRet;
}

//////////////////////////////////////////////////////////////////////
// WinMain: La fonction principale de l'application
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                     LPSTR lpCmdLine, int nCmdShow)
{
    BOOL    bRet    = FALSE;
    int        nRet    = 0;

    m_hInstance = hInstance;

    bRet = InitializeApplication(hInstance, hPrevInstance, 
        lpCmdLine, nCmdShow);

    if(bRet == FALSE)
    {
        // Echec d'initialisation de l'application.
        // On quite.
        nRet = 0;
        goto WinMain_0;
    }

    bRet = InitialiseResources();
    if(bRet == FALSE)
    {
        // Echec d'initialisation des resources.
        // On quite.
        nRet = 0;
        goto WinMain_0;
    }

    nRet = DialogBox((HINSTANCE)m_hInstance, 
        (LPCTSTR)MAKEINTRESOURCE(IDD_FINDWINDOW_DIALOG), 
        NULL, (DLGPROC)DialogProc);

WinMain_0:
    UninitializeApplication();
    UninitialiseResources();
    return nRet;
}