在Internet Explorer BHO中添加浏览器操作按钮

本杰明·格伦鲍姆(Benjamin Gruenbaum)

所以。我正在IE中处理BHO,我想添加一个浏览器操作,如下所示:

在此处输入图片说明

在Internet Explorer中,它看起来像

在此处输入图片说明

我发现的唯一教程和文档都是关于创建工具栏项的。没有人提到此选项。我知道这是可能的,因为crossrider可以让您做这件事。我就是不知道

我找不到任何有关如何在BHO中实施此方法的文档。任何指针都非常欢迎。

我用C#进行了标记,因为C#解决方案可能会更简单,但是C ++解决方案或任何其他可行的解决方案也非常受欢迎。

手动地

编辑:https//github.com/somanuell/SoBrowserAction


这是我正在进行的工作的屏幕截图。

IE9中的新按钮

我所做的事情:

1.退出保护模式

BHO注册必须更新HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Low Rights\ElevationPolicy密钥。请参阅了解和在保护模式下工作Internet Explorer。

我选择处理方式是因为它被称为“最佳实践”,并且易于调试,但是RunDll32Policy也可以做到这一点。

找到rgs包含您的BHO注册表设置文件。它是包含注册表项upadte的版本'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生成器。3策略价值可确保将代理程序作为中等完整性程序启动。%MODULEPATH%宏不是预定义宏。

为什么要使用宏?如果您的MSI包含对注册表的更新,则可以避免在RGS文件中使用该新代码。由于与MSI打交道可能很痛苦,因此提供“完全自注册”程序包通常会更容易。但是,如果您不使用宏,则无法允许用户选择安装目录。使用宏允许使用正确的安装目录动态更新注册表。

如何使宏工作?DECLARE_REGISTRY_RESOURCEID在您的BHO类的标题中找到该宏并将其注释掉。在该标头中添加以下函数定义:

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宏是的资源IDREGISTRY资源添加RGS在你的RC文件。

sm_szModulePath变量必须包含代理进程EXE的安装路径。我选择使其成为BHO类的公共静态成员变量。一种简单的设置方法是在DllMain函数中。regsvr32加载您的Dll时,将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);

}

非常感谢Mladen Jankovic。

如何处理经纪人流程?

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线程

看来这可能是最复杂的部分,因为这样做的方法很多,每种方法各有利弊。

代理进程,即“注入器”,可能是短暂的,只有一个简单的参数(HWND或TID),如果以前的实例尚未处理过,则必须处理唯一的IEFrame。

而是,“注入器”可能是一个长期存在的过程,最终永远不会结束,该过程将不得不不断监视桌面,并在出现新的IEFrame时对其进行处理。该过程的统一性可以通过命名互斥体来保证。

目前,我将尝试遵循KISS原则(保持简单,愚蠢)。也就是说:喷油器寿命短。我可以肯定的是,对于将Tab拖放到桌面的情况,这将在BHO中导致特殊处理,但是稍后我会看到。

沿着这条路线进行的是Dll注入,该注入在注入器的末端都可以幸存,但是我将其委托给Dll本身。

这是喷射器过程的代码。WH_CALLWNDPROCRET为托管IEFrame的线程安装了一个挂钩,使用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实例化,从而导致新的注入器过程,因此钩子函数必须有一种方法来知道当前线程是否已经被钩住(我不想为每个凸舌开口添加一个钩子,那不干净)

线程本地存储是必经之路:

  1. DllMain中为分配TLS索引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. 位图的大小应取决于工具栏中已经存在的其他位图的当前大小。我只使用了两个位图(一个“正常”和一个“大”)
  3. 可以优化迫使IE重新绘制工具栏的新状态的部分,而地址栏的宽度较小。它的工作原理是,有一个涉及整个IE主窗口的快速“重绘”阶段。

请参阅我对该问题的其他答案,因为我当前无法使用代码格式工作来编辑答案。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Internet Explorer缓存Ajax ...在其他浏览器中工作正常

来自分类Dev

浏览器问题搜索代码在Firefox中工作,而不在Internet Explorer中工作

来自分类Dev

在Internet Explorer BHO中添加浏览器操作按钮

来自分类Dev

在Internet Explorer中对齐

来自分类Dev

Internet Explorer中的动画路径

来自分类Dev

Selenium:Internet Explorer中的NoSuchElementException

来自分类Dev

Internet Explorer作为CasperJS的默认浏览器

来自分类Dev

从Internet Explorer启动Chrome浏览器

来自分类Dev

Selenium Internet Explorer单击按钮

来自分类Dev

如何获取Internet Explorer浏览器帮助器对象中呈现网页的窗口的HWND

来自分类Dev

@支持Internet Explorer浏览器的CSS替代

来自分类Dev

Internet Explorer的特定于浏览器的CSS

来自分类Dev

浏览器问题搜索代码在Firefox中工作,而不在Internet Explorer中工作

来自分类Dev

WinRT:无论默认浏览器如何,都可以在Internet Explorer中打开链接

来自分类Dev

Internet Explorer中的不同颜色

来自分类Dev

JavaScript如何在Internet Explorer 8和Internet Explorer 9(浏览器端)中获取文件大小?

来自分类Dev

通过Internet Explorer中的错误

来自分类Dev

Internet Explorer 11中的formatBlock

来自分类Dev

浏览器更新消息Internet Explorer 8

来自分类Dev

Selenium:Internet Explorer中的NoSuchElementException

来自分类Dev

Internet Explorer中的CORS请求

来自分类Dev

Internet Explorer中的定位问题

来自分类Dev

jQuery平滑滚动浏览器不在Internet Explorer中

来自分类Dev

Internet Explorer中的LeafletJS 0.8

来自分类Dev

Internet Explorer中的动画口吃

来自分类Dev

单击Internet Explorer中的链接

来自分类Dev

Internet Explorer中的Highchart导出

来自分类Dev

Internet Explorer 中的问题,定位?

来自分类Dev

在 Internet Explorer 中获取?

Related 相关文章

  1. 1

    Internet Explorer缓存Ajax ...在其他浏览器中工作正常

  2. 2

    浏览器问题搜索代码在Firefox中工作,而不在Internet Explorer中工作

  3. 3

    在Internet Explorer BHO中添加浏览器操作按钮

  4. 4

    在Internet Explorer中对齐

  5. 5

    Internet Explorer中的动画路径

  6. 6

    Selenium:Internet Explorer中的NoSuchElementException

  7. 7

    Internet Explorer作为CasperJS的默认浏览器

  8. 8

    从Internet Explorer启动Chrome浏览器

  9. 9

    Selenium Internet Explorer单击按钮

  10. 10

    如何获取Internet Explorer浏览器帮助器对象中呈现网页的窗口的HWND

  11. 11

    @支持Internet Explorer浏览器的CSS替代

  12. 12

    Internet Explorer的特定于浏览器的CSS

  13. 13

    浏览器问题搜索代码在Firefox中工作,而不在Internet Explorer中工作

  14. 14

    WinRT:无论默认浏览器如何,都可以在Internet Explorer中打开链接

  15. 15

    Internet Explorer中的不同颜色

  16. 16

    JavaScript如何在Internet Explorer 8和Internet Explorer 9(浏览器端)中获取文件大小?

  17. 17

    通过Internet Explorer中的错误

  18. 18

    Internet Explorer 11中的formatBlock

  19. 19

    浏览器更新消息Internet Explorer 8

  20. 20

    Selenium:Internet Explorer中的NoSuchElementException

  21. 21

    Internet Explorer中的CORS请求

  22. 22

    Internet Explorer中的定位问题

  23. 23

    jQuery平滑滚动浏览器不在Internet Explorer中

  24. 24

    Internet Explorer中的LeafletJS 0.8

  25. 25

    Internet Explorer中的动画口吃

  26. 26

    单击Internet Explorer中的链接

  27. 27

    Internet Explorer中的Highchart导出

  28. 28

    Internet Explorer 中的问题,定位?

  29. 29

    在 Internet Explorer 中获取?

热门标签

归档