gpt4 book ai didi

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

转载 作者:IT王子 更新时间:2023-10-29 04:01:28 29 4
gpt4 key购买 nike

所以。我正在 IE 中处理 BHO,我想添加一个 browser action像这样:

enter image description here

在 Internet Explorer 中,它看起来像

enter image description here

我发现的唯一教程和文档是关于创建工具栏项目的。没有人提到这个选项。我知道这是可能的,因为 crossrider 让你做这件事。我只是不知道如何。

我找不到任何关于如何在 BHO 中实现这一点的文档。任何指针都非常受欢迎。

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

最佳答案

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

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

New button in IE9

我做的事情:

1.退出保护模式

BHO 注册必须更新 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Low Rights\ElevationPolicy key 。 See Understanding and Working in Protected Mode Internet Explorer.

我选择流程方式是因为它被称为“最佳实践”并且更容易调试,但 RunDll32Policy也可以解决问题。

找到 rgs包含您的 BHO 注册表设置的文件。它是包含更新注册表项 '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%宏不是预定义的。

为什么要使用宏?
您可以避免在 RGS 文件中使用新代码,前提是您的 MSI 包含对注册表的更新。由于处理 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);
}

该代码是从 DECLARE_REGISTRY_RESOURCEID 的 ATL 实现中借用的。 (就我而言,它是 VS2010 附带的,请检查您的 ATL 版本并在必要时更新代码)。 IDR_CSOBABHO宏是 REGISTRY 的资源 ID在 RC 文件中添加 RGS 的资源。
sm_szModulePath变量必须包含代理进程 EXE 的安装路径。我选择使它成为我的 BHO 类的公共(public)静态成员变量。设置它的一种简单方法是在 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);

}

非常感谢姆拉登·扬科维奇。

如何启动 Broker 进程?

一个可能的地方是在 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.注入(inject)IEFrame线程

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

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

相反,“注入(inject)器”可能是一个长期存在的,最终永无止境的过程,它必须不断地观察桌面,在新的 IEFrames 出现时进行处理。进程的唯一性可以通过命名互斥体来保证。

目前,我将尝试遵循 KISS 原则(Keep It Simple, Stupid)。那就是:一个短命的注入(inject)器。我确信这将导致在 BHO 中对选项卡拖放到桌面的情况进行特殊处理,但我稍后会看到。

走这条路涉及到在注入(inject)器结束后仍然存在的 Dll 注入(inject),但我会将其委托(delegate)给 Dll 本身。

这是注入(inject)器过程的代码。它安装了一个 WH_CALLWNDPROCRET用于承载 IEFrame 的线程的钩子(Hook),使用 SendMessage (使用特定的注册消息)立即触发 Dll 注入(inject),然后移除钩子(Hook)并终止。 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. 幸存注入(inject):“用力钩住我”

在主IE进程中临时加载Dll足以向工具栏添加一个新按钮。但是能够监控 WM_COMMAND对于那个新按钮需要更多:一个永久加载的 Dll 和一个钩子(Hook),尽管钩子(Hook)过程结束了。一个简单的解决方案是再次 Hook 线程,传递 Dll 实例句柄。

由于每个选项卡打开都会导致一个新的 BHO 实例化,因此一个新的注入(inject)器进程,钩子(Hook)函数必须有办法知道当前线程是否已经被钩住(我不想只为每个选项卡打开添加一个钩子(Hook),那不干净)

线程本地存储是要走的路:
  • DllMain 中分配 TLS 索引, 为 DLL_PROCESS_ATTACH .
  • 新店HHOOK作为 TLS 数据,并使用它来了解
    线程已被钩住
  • 如有必要,在 DLL_THREAD_DETACH 时解开钩子(Hook)
  • 释放 DLL_PROCESS_DETACH 中的 TLS 索引

  • 这导致以下代码:
    // 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 );
    }

    我们现在有一个几乎完整的钩子(Hook)函数:
    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. 处理工具栏

    这部分可能会有很大的改进:
  • 我刚刚创建了一个黑白位图,一切都很好,那就是:透明的黑色像素。每次我尝试添加一些颜色和/或灰度级时,结果都很糟糕。我对那些“工具栏魔术中的位图”一点也不流利
  • 位图的大小应取决于工具栏中已有的其他位图的当前大小。我只用了两张位图(一张“普通”,一张“大”)
  • 可以优化强制 IE“重绘”工具栏新状态的部分,地址栏的宽度较小。它有效,有一个涉及整个 IE 主窗口的快速“重绘”阶段。

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

    关于c# - 在 Internet Explorer BHO 中添加浏览器操作按钮,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21364178/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com