39

そう。私は IE で BHO に取り組んでおり、次のようなブラウザー アクションを追加したいと考えています。

ここに画像の説明を入力

インターネットエクスプローラーでは、次のようになります

ここに画像の説明を入力

私が見つけた唯一のチュートリアルとドキュメントは、ツールバー項目の作成に関するものでした。このオプションについて言及した人はいません。クロスライダーがまさにこのことをさせてくれたので、これが可能であることを私は知っています. 方法がわかりません。

これを BHO に実装する方法に関するドキュメントが見つかりません。どんな指針も大歓迎です。

これを C# でタグ付けしたのは、C# ソリューションの方がおそらく簡単だからですが、C++ ソリューション、または機能するその他のソリューションも大歓迎です。

4

4 に答える 4

21

編集: https://github.com/somanuell/SoBrowserAction


これが私の進行中の作業のスクリーンショットです。

IE9 の新しいボタン

私がしたこと:

1. プロテクトモードからの脱出

BHO 登録はHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Low Rights\ElevationPolicyキーを更新する必要があります。保護モードの Internet Explorer の理解と操作を参照してください。

「ベストプラクティス」として言及されており、デバッグが容易であるため、プロセスの方法を選択しますがRunDll32Policy、トリックも実行できる場合があります。

rgsBHO レジストリ設定を含むファイルを見つけます。これは、レジストリ キーへの更新を含むもの'Browser Helper Object'です。そのファイルに次を追加します。

HKLM {
  NoRemove SOFTWARE {
    NoRemove Microsoft {
      NoRemove 'Internet Explorer' {
        NoRemove 'Low Rights' {
          NoRemove ElevationPolicy {
            ForceRemove '{AE6E5BFE-B965-41B5-AC70-D7069E555C76}' {
              val AppName = s 'SoBrowserActionInjector.exe'
              val AppPath = s '%MODULEPATH%'
              val Policy = d '3'
            }
          }
        }
      }
    }
  }
}

GUID は新しいものでなければなりません。私のものは使用しないでください。GUID ジェネレーターを使用してください。policyの3値は、ブローカー プロセスが中程度の整合性プロセスとして起動されることを保証します。%MODULEPATH%マクロは定義済みのものではありません。

マクロを使用する理由 MSI にレジストリの更新が含まれている場合は、RGS ファイル内のその新しいコードを避けることができます。MSI を扱うのは面倒な場合があるため、「完全な自己登録」パッケージを提供する方が簡単な場合がよくあります。ただし、マクロを使用しない場合、ユーザーがインストール ディレクトリを選択することはできません。マクロを使用すると、正しいインストール ディレクトリでレジストリを動的に更新できます。

マクロを機能させるには? BHO クラスのヘッダーでマクロを見つけてDECLARE_REGISTRY_RESOURCEID、コメントアウトします。そのヘッダーに次の関数定義を追加します。

static HRESULT WINAPI UpdateRegistry( BOOL bRegister ) throw() {
   ATL::_ATL_REGMAP_ENTRY regMapEntries[2];
   memset( &regMapEntries[1], 0, sizeof(ATL::_ATL_REGMAP_ENTRY));
   regMapEntries[0].szKey = L"MODULEPATH";
   regMapEntries[0].szData = sm_szModulePath;
   return ATL::_pAtlModule->UpdateRegistryFromResource(IDR_CSOBABHO, bRegister,
                                                       regMapEntries);
}

そのコードは、ATL 実装から借用したものですDECLARE_REGISTRY_RESOURCEID(私の場合、VS2010 に同梱されているものです。ATL のバージョンを確認し、必要に応じてコードを更新してください)。IDR_CSOBABHOマクロは、REGISTRYRC ファイルに RGS を追加するリソースのリソース ID です。

このsm_szModulePath変数には、ブローカー プロセス EXE のインストール パスが含まれている必要があります。これを BHO クラスの public static メンバー変数にすることにしました。これを設定する簡単な方法の 1 つは、DllMain関数を使用することです。regsvr32Dll をロードすると、がDllMain呼び出され、適切なパスでレジストリが更新されます。

extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) {

   if ( dwReason == DLL_PROCESS_ATTACH ) {
      DWORD dwCopied = GetModuleFileName( hInstance,
                                          CCSoBABHO::sm_szModulePath,
                                          sizeof( CCSoBABHO::sm_szModulePath ) /
                                                        sizeof( wchar_t ) );
      if ( dwCopied ) {
         wchar_t * pLastAntiSlash = wcsrchr( CCSoBABHO::sm_szModulePath, L'\\' );
         if ( pLastAntiSlash ) *( pLastAntiSlash ) = 0;
      }
   }

   return _AtlModule.DllMain(dwReason, lpReserved);

}

ムラデン・ヤンコビッチに感謝します。

ブローカー プロセスを起動する方法は?

考えられる場所の 1 つは、SetSite実装です。何度も起動されますが、プロセス自体で対処します。ホスティング IEFrame の HWND を引数として受け取ると、ブローカ プロセスにメリットがあることが後でわかります。これはIWebBrowser2::get_HWNDメソッドを使用して行うことができます。ここで、あなたはすでにIWebBrowser2*メンバーを持っていると思います。

STDMETHODIMP CCSoBABHO::SetSite( IUnknown* pUnkSite ) {

   if ( pUnkSite ) {
      HRESULT hr = pUnkSite->QueryInterface( IID_IWebBrowser2, (void**)&m_spIWebBrowser2 );
      if ( SUCCEEDED( hr ) && m_spIWebBrowser2 ) {
         SHANDLE_PTR hWndIEFrame;
         hr = m_spIWebBrowser2->get_HWND( &hWndIEFrame );
         if ( SUCCEEDED( hr ) ) {
            wchar_t szExeName[] = L"SoBrowserActionInjector.exe";
            wchar_t szFullPath[ MAX_PATH ];
            wcscpy_s( szFullPath, sm_szModulePath );
            wcscat_s( szFullPath, L"\\" );
            wcscat_s( szFullPath, szExeName );
            STARTUPINFO si;
            memset( &si, 0, sizeof( si ) );
            si.cb = sizeof( si );
            PROCESS_INFORMATION pi;
            wchar_t szCommandLine[ 64 ];
            swprintf_s( szCommandLine, L"%.48s %d", szExeName, (int)hWndIEFrame );
            BOOL bWin32Success = CreateProcess( szFullPath, szCommandLine, NULL,
                                                NULL, FALSE, 0, NULL, NULL, &si, &pi );
            if ( bWin32Success ) {
               CloseHandle( pi.hThread );
               CloseHandle( pi.hProcess );
            }
         }
      }

      [...] 

2. IEFrame スレッドの注入

これを行うには多くの方法があり、それぞれに長所と短所があるため、これが最も複雑な部分のようです。

ブローカー プロセスである「インジェクター」は、以前のインスタンスによってまだ処理されていない場合、一意の IEFrame を処理する必要がある単純な引数 (HWND または TID) を 1 つ持つ短命のプロセスである可能性があります。

むしろ、「インジェクター」は、デスクトップを継続的に監視し、新しい IEFrame が表示されるたびに処理する必要がある、長寿命で、最終的には終了しないプロセスである可能性があります。プロセスの単一性は、名前付きミューテックスによって保証される場合があります。

当面はKISS原則(Keep It Simple, Stupid)で行こうと思います。つまり、短命のインジェクターです。これにより、BHO でデスクトップにタブをドラッグ アンド ドロップした場合に特別な処理が行われることは確かですが、後で説明します。

そのルートに進むには、インジェクターの終了後も存続する Dll インジェクションが必要ですが、これを Dll 自体に委譲します。

インジェクタープロセスのコードは次のとおりです。WH_CALLWNDPROCRETIEFrame をホストするスレッドのフックをインストールし、SendMessage(特定の登録済みメッセージと共に) 使用して Dll インジェクションを直ちにトリガーし、フックを削除して終了します。BHO Dll は、CallWndRetProcという名前のコールバックをエクスポートする必要がありますHookCallWndProcRet。エラー パスは省略されます。

#include <Windows.h>
#include <stdlib.h>

typedef LRESULT (CALLBACK *PHOOKCALLWNDPROCRET)( int nCode, WPARAM wParam, LPARAM lParam );
PHOOKCALLWNDPROCRET g_pHookCallWndProcRet;
HMODULE g_hDll;
UINT g_uiRegisteredMsg;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE, char * pszCommandLine, int ) {

   HWND hWndIEFrame = (HWND)atoi( pszCommandLine );
   wchar_t szFullPath[ MAX_PATH ];
   DWORD dwCopied = GetModuleFileName( NULL, szFullPath,
                                       sizeof( szFullPath ) / sizeof( wchar_t ) );
   if ( dwCopied ) {
      wchar_t * pLastAntiSlash = wcsrchr( szFullPath, L'\\' );
      if ( pLastAntiSlash ) *( pLastAntiSlash + 1 ) = 0;
      wcscat_s( szFullPath, L"SoBrowserActionBHO.dll" );
      g_hDll = LoadLibrary( szFullPath );
      if ( g_hDll ) {
         g_pHookCallWndProcRet = (PHOOKCALLWNDPROCRET)GetProcAddress( g_hDll,
                                                                      "HookCallWndProcRet" );
         if ( g_pHookCallWndProcRet ) {
            g_uiRegisteredMsg = RegisterWindowMessage( L"SOBA_MSG" );
            if ( g_uiRegisteredMsg ) {
               DWORD dwTID = GetWindowThreadProcessId( hWndIEFrame, NULL );
               if ( dwTID ) {
                  HHOOK hHook = SetWindowsHookEx( WH_CALLWNDPROCRET,
                                                  g_pHookCallWndProcRet,
                                                  g_hDll, dwTID );
                  if ( hHook ) {
                     SendMessage( hWndIEFrame, g_uiRegisteredMsg, 0, 0 );
                     UnhookWindowsHookEx( hHook );
                  }
               }
            }
         }
      }
   }
   if ( g_hDll ) FreeLibrary( g_hDll );
   return 0;
}

3. サバイビング・インジェクション: 「私をもっと強く引っ掛けて」

ツールバーに新しいボタンを追加するには、メインの IE プロセスで Dll を一時的にロードするだけで十分です。しかし、その新しいボタンを監視できるようにするには、WM_COMMANDさらに多くのことが必要です。つまり、永続的に読み込まれた DLL と、フック プロセスが終了したにもかかわらず、フックがまだ所定の位置にあるということです。簡単な解決策は、スレッドを再度フックして、Dll インスタンス ハンドルを渡すことです。

タブを開くたびに新しい BHO のインスタンス化、つまり新しいインジェクター プロセスが発生するため、フック関数には、現在のスレッドが既にフックされているかどうかを知る方法が必要です (タブを開くたびにフックを追加したくありません。それはきれいではありません)

Thread Local Storage は次の方法です。

  1. の に TLS インデックスを割り当てDllMainますDLL_PROCESS_ATTACH
  2. 新しいHHOOKものを TLS データとして保存し、それを使用して、スレッドが既にフックされているかどうかを確認します
  3. 必要に応じてフックを外します。DLL_THREAD_DETACH
  4. TLS インデックスを解放しますDLL_PROCESS_DETACH

これは、次のコードにつながります。

// DllMain
// -------
   if ( dwReason == DLL_PROCESS_ATTACH ) {
      CCSoBABHO::sm_dwTlsIndex = TlsAlloc();

      [...]

   } else if ( dwReason == DLL_THREAD_DETACH ) {
      CCSoBABHO::UnhookIfHooked();
   } else if ( dwReason == DLL_PROCESS_DETACH ) {
      CCSoBABHO::UnhookIfHooked();
      if ( CCSoBABHO::sm_dwTlsIndex != TLS_OUT_OF_INDEXES )
         TlsFree( CCSoBABHO::sm_dwTlsIndex );
   }

// BHO Class Static functions
// --------------------------
void CCSoBABHO::HookIfNotHooked( void ) {
   if ( sm_dwTlsIndex == TLS_OUT_OF_INDEXES ) return;
   HHOOK hHook = reinterpret_cast<HHOOK>( TlsGetValue( sm_dwTlsIndex ) );
   if ( hHook ) return;
   hHook = SetWindowsHookEx( WH_CALLWNDPROCRET, HookCallWndProcRet,
                             sm_hModule, GetCurrentThreadId() );
   TlsSetValue( sm_dwTlsIndex, hHook );
   return;
}

void CCSoBABHO::UnhookIfHooked( void ) {
   if ( sm_dwTlsIndex == TLS_OUT_OF_INDEXES ) return;
   HHOOK hHook = reinterpret_cast<HHOOK>( TlsGetValue( sm_dwTlsIndex ) );
   if ( UnhookWindowsHookEx( hHook ) ) TlsSetValue( sm_dwTlsIndex, 0 );
}

これで、ほぼ完全なフック関数ができました。

LRESULT CALLBACK CCSoBABHO::HookCallWndProcRet( int nCode, WPARAM wParam,
                                                LPARAM lParam ) {
   if ( nCode == HC_ACTION ) {
      if ( sm_uiRegisteredMsg == 0 )
         sm_uiRegisteredMsg = RegisterWindowMessage( L"SOBA_MSG" );
      if ( sm_uiRegisteredMsg ) {
         PCWPRETSTRUCT pcwprets = reinterpret_cast<PCWPRETSTRUCT>( lParam );
         if ( pcwprets && ( pcwprets->message == sm_uiRegisteredMsg ) ) {
            HookIfNotHooked();
            HWND hWndTB = FindThreadToolBarForIE9( pcwprets->hwnd );
            if ( hWndTB ) {
               AddBrowserActionForIE9( pcwprets->hwnd, hWndTB );
            }
         }
      }
   }
   return CallNextHookEx( 0, nCode, wParam, lParam);
}

のコードはAddBrowserActionForIE9後で編集します。

IE9 の場合、TB を取得するのは非常に簡単です。

HWND FindThreadToolBarForIE9( HWND hWndIEFrame ) {
   HWND hWndWorker = FindWindowEx( hWndIEFrame, NULL,
                                   L"WorkerW", NULL );
   if ( hWndWorker ) {
      HWND hWndRebar= FindWindowEx( hWndWorker, NULL,
                                    L"ReBarWindow32", NULL );
      if ( hWndRebar ) {
         HWND hWndBand = FindWindowEx( hWndRebar, NULL,
                                       L"ControlBandClass", NULL );
         if ( hWndBand ) {
            return FindWindowEx( hWndBand, NULL,
                                 L"ToolbarWindow32", NULL );
         }
      }
   }
   return 0;
}

4. ツールバーの処理

その部分は大幅に改善される可能性があります。

  1. 白黒のビットマップを作成したところ、すべて問題ありませんでした。つまり、黒のピクセルが透明になりました。色やグレーレベルを追加しようとするたびに、結果はひどいものになりました。私は、これらの「ツールバーマジックのビットマップ」にまったく流暢ではありません
  2. ビットマップのサイズは、既にツールバーにある他のビットマップの現在のサイズに依存する必要があります。2 つのビットマップを使用しました (1 つは「ノーマル」、もう 1 つは「ビッグ」)。
  3. アドレスバーの幅を狭くして、ツールバーの新しい状態を IE に強制的に「再描画」させる部分を最適化できる場合があります。それは機能します.IEメインウィンドウ全体を含む迅速な「再描画」フェーズがあります。

現在、コード形式が機能している回答を編集できないため、質問に対する他の回答を参照してください。

于 2014-02-03T23:57:53.130 に答える
2

私の他の答えからの続き。

関数のコードAddBrowserActionForIE9

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<LPARAM>( &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<LPARAM>( &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. クリックのルーティング

クリックをリッスンする最も簡単な方法WM_COMMANDは、フックでメッセージをキャッチし、コマンド ID を確認することwParamです。実際の製品コードはもっと完全かもしれません (WM_COMMAND が実際にツールバーから来ていることを確認してください)。

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

NotifyActiveBhoIE9関数は次のことを行います。

a) 現在のスレッドで IEFrame を検索する
b) 見つかった IEFrame の現在アクティブなタブを
検索する c) タブをホストしているスレッドを検索する

各 BHO インスタンスには、そのウィンドウ テキストにスレッド識別子を使用して作成された非表示のウィンドウがあります。簡単なFindWindow電話でそのウィンドウが表示され、BHO にメッセージで通知されます。

プライベート ウィンドウの作成:

// 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;
}

コール サイト:
RegisterPrivateClassで呼び出されたDllMain場合、でPROCESS_ATTACH
UnregisterPrivateClass呼び出されDllMainた場合、で呼び出された場合、でPROCESS_DETACH
CreatePrivateWindow呼び出されたSetSite場合、でpUnkSite != NULL
DestroyPrivateWindow呼び出されSetSiteた場合pUnkSite == NULL

NotifyActiveBhoIE9 実装:

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<HWND*>( lParam ) = hWnd;
               return FALSE;
            }
         }
         return TRUE;
      }
   };

   HWND hWndFirstVisibleTab = 0;
   EnumChildWindows( hwndIEFrame, ew::ewp,
                     reinterpret_cast<LPARAM>( &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 );
}

目に見えないウィンドウはthis、Windows Word にポインターを格納する従来の方法で BHO に接続されます。

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

6. 「TAB DRAG & DROP」ケースの処理

タブをデスクトップに「ドラッグ アンド ドロップ」すると、IE9 は新しい IEFrame メイン ウィンドウをソース iexplore.exe プロセスの新しいスレッドに作成し、タブをホストします。

それを検出するための簡単な解決策は、DISPID_WINDOWSTATECHANGEDイベントをリッスンすることですIWebBrowser2::get_HWND。メソッドを使用して、現在の IE メイン ウィンドウを取得します。そのウィンドウが以前に保存したものと同じでない場合、タブは再親化されています。次に、ブローカー プロセスを起動します。新しい親フレームにボタンがまだない場合は、追加されます。

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<HWND>( hWndIEFrame ) != m_hWndIEFrame ) {
            m_hWndIEFrame = reinterpret_cast<HWND>( hWndIEFrame );
            LaunchMediumProcess();
         }
      }
   }
   break;
}

github プロジェクトが更新されました。

于 2014-02-18T15:27:50.130 に答える
1

DLL注射が答えです、相棒。

どうぞ

編集:

もちろん。DLL インジェクションを行う必要はないようです。BHO は IE プロセスの内部からアクセスできます。それで、それはずっと簡単です。

基本的に、最初にウィンドウを見つける必要があります。したがって、ニーズに合わせて関数を変更すると、次のようになります。

BOOL FindFavoritesAndToolsBar(HWND mainWnd, HWND* addressBarWnd, HWND* cmdTargetWnd)
{
  mainWnd = ::FindWindowEx( mainWnd, NULL, TEXT( "WorkerW" ), NULL );
  mainWnd = ::FindWindowEx( mainWnd, NULL, TEXT( "ReBarWindow32" ), NULL );

  *cmdTargetWnd = ::FindWindowEx
  mainWnd, NULL, TEXT( "ControlBandClass" ), NULL );

  if( *cmdTargetWnd  )
      *addressBarWnd = ::FindWindowEx(
      *cmdTargetWnd, NULL, TEXT( "ToolbarWindow32" ), L"Favorites and Tools Bar" );

  return cmdTargetWnd != NULL;
}

Spy++ を使用して見つけました。

ロジックの残りの部分は、リンクした記事と同じです。サブクラス化してメッセージ ループをインターセプトし、独自のボタンに独自のイベント ハンドラーを追加します。

もう 1 つの方法は、ボタンをポップアップ ウィンドウとして作成し、IE ウィンドウを親として設定し、「お気に入りとツールバー」の位置を見つけて、ボタンをそれに隣接して配置することです。さらに簡単ですが、もちろんエレガントではありません。

編集 2: 申し訳ありませんが、オリバーの回答の一部をエコーし​​たところを見ました。ただし、上記のように BHO 内で実行すると、ボタンは IE 独自のボタンのように動作し、完全に制御できます。

于 2014-02-03T18:23:22.963 に答える