Add browser action button in internet explorer BHO

后端 未结 4 1895
深忆病人
深忆病人 2020-12-04 19:34

So. I\'m working on a BHO in IE and I want to add a browser action like this:

\"enter<

4条回答
  •  粉色の甜心
    2020-12-04 20:03

    Continuation from my other answer.

    Code for the AddBrowserActionForIE9 function.

    void AddBrowserActionForIE9( HWND hWndIEFrame, HWND hWndToolBar ) {
    
       // do nothing if already done
       LRESULT lr = SendMessage( hWndToolBar, TB_BUTTONCOUNT, 0, 0 );
       UINT ButtonCount = (UINT)lr;
       for ( WPARAM index = 0; index < ButtonCount; ++index ) {
          TBBUTTON tbb;
          LRESULT lr = SendMessage( hWndToolBar, TB_GETBUTTON, index, reinterpret_cast( &tbb ) );
          if ( lr == TRUE ) {
             if ( tbb.idCommand == 4242 ) return;
          }
       }
       HIMAGELIST hImgList = (HIMAGELIST)SendMessage( hWndToolBar, TB_GETIMAGELIST, 0, 0 );
       HIMAGELIST hImgListHot = (HIMAGELIST)SendMessage( hWndToolBar, TB_GETHOTIMAGELIST, 0, 0 );
       HIMAGELIST hImgListPressed = (HIMAGELIST)SendMessage( hWndToolBar, TB_GETPRESSEDIMAGELIST, 0, 0 );
       // load little or big bitmap
       int cx, cy;
       BOOL bRetVal = ImageList_GetIconSize( hImgList, &cx, &cy );
    
       HBITMAP hBitMap = LoadBitmap( CCSoBABHO::sm_hModule,
                                     MAKEINTRESOURCE( cx <= 17 ? IDB_BITMAP_SO_LITTLE : IDB_BITMAP_SO_BIG ) );
       int iImage = -1;
       if ( hImgList ) {
          iImage = ImageList_Add( hImgList, hBitMap, NULL );
       }
       if ( hImgListHot ) {
          ImageList_Add( hImgListHot, hBitMap, NULL );
       }
       if ( hImgListPressed ) {
          ImageList_Add( hImgListPressed, hBitMap, NULL );
       }
       TBBUTTON tbb;
       memset( &tbb, 0, sizeof( TBBUTTON ) );
       tbb.idCommand = 4242;
       tbb.iBitmap = iImage;
       tbb.fsState = TBSTATE_ENABLED;
       tbb.fsStyle = BTNS_BUTTON;
       lr = SendMessage( hWndToolBar, TB_INSERTBUTTON, 0, reinterpret_cast( &tbb ) );
       if ( lr == TRUE ) {
          // force TB container to expand
          HWND hWndBand = GetParent( hWndToolBar );
          RECT rectBand;
          GetWindowRect( hWndBand, &rectBand );
          HWND hWndReBar = GetParent( hWndBand );
          POINT ptNew = { rectBand.left - cx, rectBand.top };
          ScreenToClient( hWndReBar, &ptNew );
          MoveWindow( hWndBand, ptNew.x, ptNew.y, rectBand.right - rectBand.left + cx,
                      rectBand.bottom - rectBand.top, FALSE );
          // force IE to resize address bar
          RECT rect;
          GetWindowRect( hWndIEFrame, &rect );
          SetWindowPos( hWndIEFrame, NULL, rect.left, rect.top, rect.right - rect.left + 1,
                        rect.bottom - rect.top, SWP_NOZORDER );
          SetWindowPos( hWndIEFrame, NULL, rect.left, rect.top, rect.right - rect.left,
                        rect.bottom - rect.top, SWP_NOZORDER );
       }
       if ( hBitMap ) DeleteObject( hBitMap );
       return;
    }
    

    5. Routing the Click

    The simplest way to listen to the click is just to catch WM_COMMAND messages in the hook and check the command Id in wParam. Real production code may be more complete (verify that the WM_COMMAND is indeed coming from the Toolbar).

    if ( pcwprets && ( pcwprets->message == WM_COMMAND ) ) {
       if ( LOWORD( pcwprets->wParam ) == 4242 ) {
          NotifyActiveBhoIE9( pcwprets->hwnd );
       }
    }
    

    The NotifyActiveBhoIE9 function will:

    a) Find the IEFrame in the current thread
    b) Find the current activated tab for the found IEFrame
    c) Find the thread hosting the tab

    Each BHO instance will have an invisible window created with the Thread Identifier in it's Window Text. A simple FindWindow call will give us that window and the BHO will be notified with a message.

    Creating the private window:

    // New Members in CCSoBABHO
    static wchar_t * sm_pszPrivateClassName
    static void RegisterPrivateClass( void );
    static void UnregisterPrivateClass( void );
    HWND m_hWndPrivate;
    static LRESULT CALLBACK wpPrivate( HWND hWnd, UINT uiMsg,
                                       WPARAM wParam, LPARAM lParam );
    static wchar_t * MakeWindowText( wchar_t * pszBuffer, size_t cbBuffer,
                                     DWORD dwTID );
    bool CreatePrivateWindow( void );
    bool DestroyPrivateWindow( void ) {
       if ( m_hWndPrivate ) DestroyWindow( m_hWndPrivate );
    };
    
    // implementation
    wchar_t * CCSoBABHO::sm_pszPrivateClassName = L"SoBrowserActionClassName";
    
    void CCSoBABHO::RegisterPrivateClass( void ) {
       WNDCLASS wndclass;
       memset( &wndclass, 0, sizeof( wndclass ) );
       wndclass.hInstance = sm_hInstance;
       wndclass.lpszClassName = sm_pszPrivateClassName;
       wndclass.lpfnWndProc = wpPrivate;
       RegisterClass( &wndclass );
       return;
    }
    
    void CCSoBABHO::UnregisterPrivateClass( void ) {
       UnregisterClass( sm_pszPrivateClassName, sm_hInstance );
       return;
    }
    
    wchar_t * CCSoBABHO::MakeWindowText( wchar_t * pszBuffer, size_t cbBuffer,
                                         DWORD dwTID ) {
       swprintf( pszBuffer, cbBuffer / sizeof( wchar_t ),
                 L"TID_%.04I32x", dwTID );
       return pszBuffer;
    }
    
    bool CCSoBABHO::CreatePrivateWindow( void ) {
       wchar_t szWindowText[ 64 ];
       m_hWndPrivate = CreateWindow( sm_pszPrivateClassName,
                                     MakeWindowText( szWindowText,
                                                     sizeof( szWindowText ),
                                                     GetCurrentThreadId() ),
                                     0, 0, 0,0 ,0 ,NULL, 0, sm_hInstance, this );
       return m_hWndPrivate ? true : false;
    }
    

    Call sites:
    RegisterPrivateClass called in DllMain, when PROCESS_ATTACH
    UnregisterPrivateClass called in DllMain, when PROCESS_DETACH
    CreatePrivateWindow called in SetSite, when pUnkSite != NULL
    DestroyPrivateWindow called in SetSite, when pUnkSite == NULL

    The NotifyActiveBhoIE9 implementation:

    void CCSoBABHO::NotifyActiveBhoIE9( HWND hWndFromIEMainProcess ) {
       // Up to Main Frame
       HWND hWndChild = hWndFromIEMainProcess;
       while ( HWND hWndParent = GetParent( hWndChild ) ) {
          hWndChild = hWndParent;
       }
       HWND hwndIEFrame = hWndChild;
    
       // down to first "visible" FrameTab"
       struct ew {
          static BOOL CALLBACK ewp( HWND hWnd, LPARAM lParam ) {
          if ( ( GetWindowLongPtr( hWnd, GWL_STYLE ) & WS_VISIBLE ) == 0 ) return TRUE;
             wchar_t szClassName[ 32 ];
             if ( GetClassName( hWnd, szClassName, _countof( szClassName ) ) ) {
                if ( wcscmp( szClassName, L"Frame Tab" ) == 0 ) {
                   *reinterpret_cast( lParam ) = hWnd;
                   return FALSE;
                }
             }
             return TRUE;
          }
       };
    
       HWND hWndFirstVisibleTab = 0;
       EnumChildWindows( hwndIEFrame, ew::ewp,
                         reinterpret_cast( &hWndFirstVisibleTab ) );
       if ( hWndFirstVisibleTab == 0 ) return;
    
       // down to first child, (in another process) 
       HWND hWndThreaded = GetWindow( hWndFirstVisibleTab, GW_CHILD );
       if ( hWndThreaded == 0 ) return;
       DWORD dwTID = GetWindowThreadProcessId( hWndThreaded, NULL );
       wchar_t szWindowText[ 64 ];
       HWND hWndPrivate = FindWindow( sm_pszPrivateClassName,
                                      MakeWindowText( szWindowText,
                                                      sizeof( szWindowText ), dwTID ) );
       if ( hWndPrivate ) SendMessage( hWndPrivate, WM_USER, 0, 0 );
    }
    

    The invisible window is connected to the BHO with a classic one: storing a this pointer in Windows Words.

    LRESULT CALLBACK CCSoBABHO::wpPrivate( HWND hWnd, UINT uMsg,
                                           WPARAM wParam, LPARAM lParam ) {
       switch( uMsg ) {
          case WM_CREATE: {
             CREATESTRUCT * pCS = reinterpret_cast( lParam );
             SetWindowLongPtr( hWnd, GWLP_USERDATA,
                               reinterpret_cast( pCS->lpCreateParams ) );
             return 0;
          }
          case WM_USER: {
             CCSoBABHO * pThis =
                reinterpret_cast( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
             if ( pThis ) pThis->OnActionClick( wParam, lParam );
             break;
          }
          default: return DefWindowProc( hWnd, uMsg, wParam, lParam );
       }
       return 0;
    }
    

    6. Processing the "TAB DRAG & DROP" case

    When you "Drag and Drop" a tab to the Desktop, IE9 creates a new IEFrame Main window, in a new thread in the source iexplore.exe process, hosting the tab.

    To detect that, a simple solution is to listen to the DISPID_WINDOWSTATECHANGED event: use the IWebBrowser2::get_HWND method to retrieve the current IE main window. If that window is not the same as the previously save one, then the tab has been reparented. Then, just launch the broker process: if the new parent frame has not yet the button, it will be added.

    case DISPID_WINDOWSTATECHANGED: {
       LONG lFlags = pDispParams->rgvarg[ 1 ].lVal;
       LONG lValidFlagsMask = pDispParams->rgvarg[ 0 ].lVal;
       LONG lEnabledUserVisible = OLECMDIDF_WINDOWSTATE_USERVISIBLE |
                                  OLECMDIDF_WINDOWSTATE_ENABLED;
       if ( ( lValidFlagsMask & lEnabledUserVisible ) == lEnabledUserVisible ) {
          SHANDLE_PTR hWndIEFrame = 0;
          HRESULT hr = m_spIWebBrowser2->get_HWND( &hWndIEFrame );
          if ( SUCCEEDED( hr ) && hWndIEFrame ) {
             if ( reinterpret_cast( hWndIEFrame ) != m_hWndIEFrame ) {
                m_hWndIEFrame = reinterpret_cast( hWndIEFrame );
                LaunchMediumProcess();
             }
          }
       }
       break;
    }
    

    The github project has been updated.

提交回复
热议问题