HOOK函数(一)——进程内HOOK

  什么是HOOK呢?其实很简单,HOOK就是对Windows消息进行拦截检查处理的一个函数。在Windows的消息机制中,当用户产生消息时,应用程序通过调用GetMessage函数取出消息,然后把消息放入到消息队列,再使用消息调度函数DispatchMessage函数讲消息调度给系统,Windows系统会调用创建窗口类时制定的窗口过程中进行次消息的处理。而HOOK函数的话,就可以对此消息进行拦截。经过此HOOK函数的处理后,再决定是屏蔽掉此消息,还是继续往下传递。至于为什么叫“钩子”,可能就是因为它可以像钩子一样把消息给钩住。话说四川话里面的“钩子”却是屁股的意思...哈哈,四川的同学读钩子肯定多了几份趣味

  钩子函数的实现分为以下步骤:

  1 安装钩子函数

  安装钩子函数是通过函数SetWindowHookEx来实现的:

  

HHOOK WINAPI SetWindowsHookExW(__in int idHook,  __in HOOKPROC lpfn,__in_opt HINSTANCE hmod,  __in DWORD dwThreadId);

  此函数的第一个参数idHook指定将要安装的钩子过程的类型,如果我们安装键盘钩子,则设为WH_KEYBOARD,鼠标钩子则设为WH_MOUSElpfn指向了相应的钩子函数,如果dwThreadId为0,或者指定了一个其他进程创建的线程之标识符,则lpfn必须指向一个位于某动态链接库中的钩子函数。如果为进程外钩子,hMod指向的是钩子函数所在的DLL的句柄,如果为进程内钩子,则设为NULLdwThreadId指定与钩子过程相关的线程表示,为0表示与所有线程相关。函数成功返回所安装的钩子函数的句柄,否则返回NULL。PS:最后安装的钩子函数总是排在钩子链的最前面。

  

  2 添加全局变量

HHOOK g_hMouse = NULL;
HHOOK g_hKeyboard = NULL;
HWND g_hWnd = NULL;

     全局句柄g_hMouse、g_hKeyboard分别代表我们所要安装的鼠标钩子和键盘钩子过程的句柄,g_hWnd用来保存当前的窗口句柄。

  3 声明钩子函数

  如果想要监视鼠标消息,需要定义相应的鼠标钩子过程。同样,要监视键盘消息,也需要定义相应的键盘钩子过程。该钩子函数的形式必须如下:

LRESULT CALLBACK  HookProc(int nCode, WPARAM wParam, LPARAM lParam);

  在当前钩子函数中处理完信息后,如果想把信息继续传递给下一个钩子函数,可以调用CallNextHookEx函数来实现:

LRESULT
WINAPI CallNextHookEx( __in_opt HHOOK hhk,   __in int nCode, __in WPARAM wParam,  __in LPARAM lParam

  CallNextHookEx函数的第一个参数是钩子的句柄,就是我们调用SetWindowHookEx函数返回的句柄,其他几个参数和HookProc中是一样的。

  4 卸载钩子函数

  当我们不需要再使用钩子函数的时候,可以将其卸载,调用函数:

  

BOOL WINAPI UnhookWindowsHookEx(  __in HHOOK hhk);

  函数的唯一参数便为钩子函数的句柄。

  说一千道一万,不如一个程序来的实在。下面便是一个实现进程内钩子函数的MFC例子。步骤如下:

  1 新建一个名字CInnerHookDlg的MFC工程。

  2 在CInnerHookDlgDlg.cpp中添加钩子的全局变量和当前句柄

  

HHOOK g_hMouse = NULL;
HHOOK g_hKeyboard = NULL;
HWND g_hWnd = NULL;

  3 然后再分别实现键盘和鼠标钩子函数,我们在键盘钩子函数中显示出当前按下去的键盘。而在鼠标钩子函数中,当我们双击右键时候,会显示出对话框。

//鼠标钩子函数
LRESULT CALLBACK MouseProc( int nCode, WPARAM wParam, LPARAM lParam )
{
...
}
    


//键盘钩子函数
LRESULT CALLBACK KeyBoardProc( int nCode, WPARAM wParam, LPARAM lParam )
{
...
}

  4 在CCInnerHookDlgDlg的OnInitDialog()中设置钩子,添加如下代码

  

//设置鼠标钩子
    g_hMouse = SetWindowsHookEx(WH_MOUSE, MouseProc, NULL,GetCurrentThreadId());

    //设置键盘钩子
    g_hKeyboard = SetWindowsHookEx(WH_KEYBOARD, KeyBoardProc, NULL, GetCurrentThreadId());
    
    //获取当前句柄
    g_hWnd = m_hWnd;

  5 最后在CCInnerHookDlgDlg的OnSysCommand()中卸载钩子,添加如下代码:

UnhookWindowsHookEx(g_hMouse);
    UnhookWindowsHookEx(g_hKeyboard);

  下面是所有的代码啦!

// CInnerHookDlgDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "CInnerHookDlg.h"
#include "CInnerHookDlgDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

HHOOK g_hMouse = NULL;
HHOOK g_hKeyboard = NULL;
HWND g_hWnd = NULL;


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialog
{
public:
    CAboutDlg();

// 对话框数据
    enum { IDD = IDD_ABOUTBOX };

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CCInnerHookDlgDlg 对话框
CCInnerHookDlgDlg::CCInnerHookDlgDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CCInnerHookDlgDlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CCInnerHookDlgDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CCInnerHookDlgDlg, CDialog)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

//鼠标钩子函数
LRESULT CALLBACK MouseProc( int nCode, WPARAM wParam, LPARAM lParam )
{
    LPMOUSEHOOKSTRUCT pMouseHook = (MOUSEHOOKSTRUCT*)(lParam);

    if (nCode >= 0)
    {
        //双击右键弹出当前窗口的窗口名
        if (wParam == WM_RBUTTONDBLCLK)
        {
            HWND hWnd = pMouseHook->hwnd;  //鼠标所在的窗口
            HWND hParent;

            //获取顶层窗口
            while(hWnd != NULL)
            {
                hParent = GetParent(hWnd);
                if (hParent == NULL)
                    break;
                hWnd = hParent;
            }

            if (hWnd != NULL)
            {
                //得到窗口名
                TCHAR strDlgName[MAX_PATH] = {0};
                GetWindowText(hWnd,strDlgName, MAX_PATH);
                AfxMessageBox( strDlgName );
            }
        }
    }

    return CallNextHookEx(g_hMouse,nCode, wParam, lParam);
}

//键盘钩子函数
LRESULT CALLBACK KeyBoardProc( int nCode, WPARAM wParam, LPARAM lParam )
{
    LRESULT lReturnValue;
    lReturnValue = CallNextHookEx(g_hKeyboard, nCode, wParam, lParam);

    //键盘按键是否已经松动
    if ( (lParam & 0x80000000) && (HC_ACTION == nCode))
    {
        char c = (char) lParam;
        CString strText(_T("按下了键"));
        strText += c;
        AfxMessageBox(strText);  //显示当前的按键
    }

    //如果按下F4,则退出程序
    if (wParam == VK_F4)
    {
        SendMessage(g_hWnd, WM_CLOSE,0,0);  
    }

    return lReturnValue;
}


// CCInnerHookDlgDlg 消息处理程序

BOOL CCInnerHookDlgDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // 将“关于...”菜单项添加到系统菜单中。

    // IDM_ABOUTBOX 必须在系统命令范围内。
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        CString strAboutMenu;
        strAboutMenu.LoadString(IDS_ABOUTBOX);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
    //  执行此操作
    SetIcon(m_hIcon, TRUE);            // 设置大图标
    SetIcon(m_hIcon, FALSE);        // 设置小图标

    // TODO: 在此添加额外的初始化代码

    //设置鼠标钩子
    g_hMouse = SetWindowsHookEx(WH_MOUSE, MouseProc, NULL,GetCurrentThreadId());

    //设置键盘钩子
    g_hKeyboard = SetWindowsHookEx(WH_KEYBOARD, KeyBoardProc, NULL, GetCurrentThreadId());
    
    //获取当前句柄
    g_hWnd = m_hWnd;

    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}
void CCInnerHookDlgDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialog::OnSysCommand(nID, lParam);
    }

    UnhookWindowsHookEx(g_hMouse);
    UnhookWindowsHookEx(g_hKeyboard);
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CCInnerHookDlgDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // 用于绘制的设备上下文

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // 使图标在工作区矩形中居中
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // 绘制图标
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialog::OnPaint();
    }
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CCInnerHookDlgDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}

 

  

 

 

 

    

 

posted @ 2013-12-14 18:40  数据世界  阅读(2044)  评论(0编辑  收藏  举报