zoukankan      html  css  js  c++  java
  • 【windows核心编程】一个HOOK的例子

     

    一、应用场景

    封装一个OCX控件,该控件的作用是来播放一个视频文件,需要在一个进程中放置四个控件实例。 由于控件是提供给别人用的,因此需要考虑很多东西。

     

    二、考虑因素

    1、控件的父窗口resize时需要控件也随之resize

        子窗体不能知道父窗口的resize情况,因为父窗口不会主动把这一情况通知子窗口。 因此需要放一个钩子来主动得知父窗口的resize事件,然后告诉控件窗口做出适当的改变。

        这里用了一个局部钩子,即线程钩子。 被放置钩子的线程是控件的父窗口所在的线程(这也是考虑第3个因素的原因)。

     

    2、这四个控件是否为同一个父窗口

        在该场景中考虑到了多个控件在同一个父窗口的情况:这种情况下,当父窗口resize的时候,就需要通知每一个控件(子窗口)。 又因为多个控件的父窗口可能不是同一个,因此钩子就要被放置到每一个父窗口所在的线程。

        

        

    3、执行四个控件UI相关代码的线程是否为同一个线程

         由于控件的所在场景相对无法控制,因此控件的父窗口的代码有可能是在不同的线程中执行的(虽然99.999%可能性是同一个线程),这就需要在每个不同的线程中放置一个钩子(这基于一个前提,那就是  放置钩子的线程 ==  执行钩子回调函数的线程)。

        

    综合3个因素,可以得出这样一个 对应关系

    一个线程Thread:    被放置钩子的线程 == 控件的父窗口所在的线程(可能这样说不准确,但权且这样说) == 执行钩子回调函数的线程

    一个Hook:            每放置一个钩子就产生一个HOOK, 对于同一个线程只放置一次,如果四个控件的父窗口不是同一个线程,那么就要放置多个。还因为要在代码中使用CallNextHook将当前线程对应的钩子作为参数传给该函数,因此要保存线程和钩子的对应关系。

    一个父窗口句柄:     即相同父窗口的控件的父窗口

    若干个子窗口(控件):同一个父窗口有多个子窗口 

     

     对应关系如下

     

     

     用代码描述对应关系

    //key为父窗口, value为若干个子窗口
    typedef map<HWND, vector<HWND> > MYMAP;    
       
    
    typedef struct _tagHOOKInfo
    {
        HHOOK hHook;              //钩子、线程、父窗口 三者一一对应
        MYMAP parentAndOcx;       //该父窗口和它的若干个子窗口(控件)
    
    }HOOKInfo, *PHOOKInfo;
    
     
    //key为线程ID , value为该线程对应的{钩子、父窗口、若干个子窗口 }
    map<DWORD, HOOKInfo>                g_ThreadParentOcxMap2;

     

     

     

    三、关于钩子相关函数

       

     1、WH_CALLWNDPROC钩子

         这个钩子专门来截获SendMessage到窗口过程的消息,在消息被传递给窗口过程之前先被其截获。但是不能更改和丢弃消息。

     

     2、 API 和 数据结构

    //放置钩子 
    HHOOK SetWindowsHookEx(
      __in  int idHook,          //钩子类型
      __in  HOOKPROC lpfn,       //回调函数
      __in  HINSTANCE hMod,      //模块句柄,如果是全局钩子则为DLL句柄,dwThreadID为0;  如果是局部钩子则为NULL,dwThreadID为某一线程ID
      __in  DWORD dwThreadId     //全局钩子为0, 句柄钩子为线程ID
    );

     

     

    SetWindowsHookEx失败返回NULL,否则成功

     

    WH_WNDWNDPROC对应的回调函数以及其他函数

    //回调函数原型
    LRESULT CALLBACK CallWndProc(
    
      __in  int nCode,        /*标识是否钩子回调函数必须处理这个消息。如果该值是HC_ACTION,回调函数必须处理。  如果该值大于等于0,强烈建议调用CallNextHookEx并且返回他的返回值。
    如果该值小于0,钩子回调必须调用CallNextHookEx函数并且不能处理这个消息,然后必须返回CallNextHookEx的返回值。
    如果钩子回调没有调用CallNextHookEx,则该函数必须返回0. */
    
      __in  WPARAM wParam,  //标识消息是否由当前线程发送,非0是,0不是。
      __in  LPARAM lParam //一个指向CWPSTRUCT结构的指针,包含了该消息的许多细节
    ); 

    typedef
    struct { LPARAM lParam; //附加信息 WPARAM wParam; //附加信息 UINT message; //消息值 HWND hwnd; //消息的目标窗口 } CWPSTRUCT, *PCWPSTRUCT;


     

     

    撤销钩子

    BOOL UnhookWindowsHookEx(
      __in  HHOOK hhk       //SetWindowsHookEx返回值
    );

     

     在控件的代码里获取其父窗口所在的线程ID,将钩子放置在该线程里

    //根据窗口句柄获取他所在的进程和线程ID
    DWORD GetWindowThreadProcessId(
      __in   HWND hWnd,                         //窗口句柄
      __out  LPDWORD lpdwProcessId     //带回进程ID
    );
    
    //该函数返回线程ID, 参数带回进程ID

     

     四、上代码 

    /************************************************************************
    四个视频控件,假如分别放在四个父窗口上
    则每个父窗口对应的vecor子窗口中只有一个元素
    
    假如一个父窗口上放了多个视频控件,则每个父窗口对应的vector子窗口中有多个元素
    
    另:回调函数的主调线程 应该就是 钩子被放置的线程
    
    【放置钩子的线程】很有可能不是【钩子被放置到的线程】
    因此要保存一个 线程--钩子--父窗口(及其子窗口)  的对应关系
    
    /************************************************************************/
    
    typedef map<HWND, vector<HWND> > MYMAP;       
    
    typedef struct _tagHOOKInfo
    {
        HHOOK hHook;
        MYMAP parentAndOcx;
    }HOOKInfo, *PHOOKInfo;
    
     
    map<DWORD, HOOKInfo>                  g_ThreadParentOcxMap2;
    
     
    
    LRESULT CALLBACK CallWndProc(
                                 __in  int nCode,
                                 __in  WPARAM wParam,
                                 __in  LPARAM lParam
                                 )
    { 
        //当前线程ID
        DWORD dwCurrentID = GetCurrentThreadId(); 
    
        PCWPSTRUCT pMsg = (PCWPSTRUCT)lParam; 
        map<DWORD, HOOKInfo>::iterator iter;
        MYMAP::const_iterator iter2;
    
        //取得其 钩子句柄 父窗口 子窗口  
        PHOOKInfo pHookInfo = NULL;
        HHOOK hHook = NULL;
        MYMAP *pMyMap = NULL;
    
        //查找map中主键为当前线程ID的元素(认为  钩子回调函数的主调线程 == 钩子被设置的线程) 
        //当前接受消息的窗口为某个控件的父窗口

    //经过Andy指点:进入此回调函数时就已经说明该线程对应的此钩子起作用了,那么就一定会找到当前线程对应的钩子,
    //那么,又因为控件的父窗口所在UI线程是不变的,其对应的窗口句柄也不会变,钩子也一定能找到;
    //下面if判断永远成立! else不会被执行到! 可以将此段if...else改造为单纯取map中value的操作,
    //而不必担心主键线程ID 和 主键父窗口句柄 不存在的情况!
    if ((iter = g_ThreadParentOcxMap2.find(dwCurrentID)) != g_ThreadParentOcxMap2.end() && (iter2 = (iter->second).parentAndOcx.find(pMsg->hwnd)) != iter->second.parentAndOcx.end() ) { pHookInfo = &(iter->second); hHook = pHookInfo->hHook; pMyMap = &(pHookInfo->parentAndOcx); } else {
         //else永远不会被执行到!
    return 0; } if (nCode == HC_ACTION ) { if (NULL != pMsg && pMsg->message == WM_SIZE) { #if defined _DEBUG OutputDebugString(TEXT(" ----------WM_SIZE happened: ")); OutputDebugString(TEXT("----- ")); #endif for (vector<HWND>::const_iterator iter3 = iter2->second.begin(); iter3 != iter2->second.end(); ++ iter3) { ::PostMessage(*iter3, WM_USER + 255, 0, 0); } } return ::CallNextHookEx(hHook, nCode, wParam, lParam); } else if (nCode < 0 && NULL != hHook) { return ::CallNextHookEx(hHook, nCode, wParam, lParam); } return 0; } extern "C" __declspec(dllexport) BOOL __stdcall StartHook(HWND hParentWnd, HWND hOCXWnd, DWORD dwThreadID) { DWORD dwCurrentID = GetCurrentThreadId(); BOOL bRet = FALSE; if(! ::IsWindow(hParentWnd) || ! ::IsWindow(hOCXWnd)) return FALSE; vector<HWND> hOCXWndVector; hOCXWndVector.push_back(hOCXWnd); HOOKInfo hookInfo; map<DWORD, HOOKInfo>::iterator iterBig; //不存在主键为dwThreadID的元素,则为该线程放置钩子 if ((iterBig = g_ThreadParentOcxMap2.find(dwThreadID)) == g_ThreadParentOcxMap2.end()) { HHOOK hHook = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)CallWndProc, NULL, dwThreadID ); if(NULL == hHook) return FALSE; hookInfo.parentAndOcx.insert(map<HWND, vector<HWND> >::value_type(hParentWnd, hOCXWndVector)); hookInfo.hHook = hHook; g_ThreadParentOcxMap2.insert(map<DWORD, HOOKInfo>::value_type(dwThreadID, hookInfo)); } else { //存在主键为dwThreadID的元素,则不必再设置钩子,只需要在map值字段(HOOKInfo结构体)中的map插入或追加 PHOOKInfo pHookInfo = &(iterBig->second); //判断HOOKInfo的map字段中是否存在主键为hParent的元素 MYMAP *pMap = &(pHookInfo->parentAndOcx); if (NULL == pMap) return FALSE; MYMAP::iterator iterSmall; //如果没有 则将map<hParent, hOCXWndVector>插入 if((iterSmall = pMap->find(hParentWnd)) == pMap->end()) { pMap->insert(MYMAP::value_type(hParentWnd, hOCXWndVector)); } //如果有 则将hOCXWnd追加到其值vector中 else { (iterSmall->second).push_back(hOCXWnd); } } return TRUE; } extern "C" __declspec(dllexport) BOOL __stdcall StopHook() { for (map<DWORD, HOOKInfo>::const_iterator iter = g_ThreadParentOcxMap2.begin(); iter != g_ThreadParentOcxMap2.end(); ++ iter) { HHOOK hHook = (iter->second).hHook; if(NULL != hHook) UnhookWindowsHookEx(hHook); } return TRUE; }

     

     

      

  • 相关阅读:
    AngularJs跨域请求Java的实现---博客园老牛大讲堂
    JavaWeb的json包装以及跨域问题--博客园老牛大讲堂
    无网络联机打单机游戏---博客园老牛大讲堂
    H5移动开发AUI框架入门---博客园老牛大讲堂
    MySql链表语句--博客园老牛大讲堂
    Jquery的基本用法--博客园老牛大讲堂
    Java的日期字符串的转换---博客园老牛大讲堂
    实现访问电脑百度,访问到自己的工程方法---博客园老牛大讲堂
    Jquery的闭包理解--匿名函数--博客园老牛大讲堂
    xampp的安装和配置与HBuilder的配置--博客园老牛大讲堂
  • 原文地址:https://www.cnblogs.com/cuish/p/3793394.html
Copyright © 2011-2022 走看看