在浏览器BHO中添加浏览器操作button
所以。 我正在IE浏览器中工作,我想添加一个浏览器这样的操作 :
在Internet Explorer中,它会看起来像
我find的唯一教程和文档是关于创build工具栏项目。 没有提到这个选项。 我知道这是可能的,因为crossrider让你做这个确切的事情。 我只是不知道如何。
我找不到任何关于如何在BHO中实现这个function的文档。 任何指针都非常受欢迎。
我用C#把它标记为C#解决scheme可能会更简单,但是C ++解决scheme或其他解决scheme也是非常受欢迎的。
编辑: https : //github.com/somanuell/SoBrowserAction
这里是我正在进行的工作的屏幕截图。
我做的事情:
1.退出保护模式
BHO注册必须更新HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Low Rights\ElevationPolicy
项。 请参阅了解并在受保护的模式下使用Internet Explorer。
我select了stream程方式,因为它被称为“最佳实践”,并且更易于debugging,但RunDll32Policy
也可能有效。
find包含您的BHOregistry设置的rgs
文件。 它是包含registry项'Browser Helper Object'
的registry项。 添加到该文件以下内容:
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%
macros不是预定义的。
为什么使用macros? 您可以避免在您的RGS文件中的新代码,只要您的MSI包含registry的更新。 由于处理MSI可能会很痛苦,所以提供一个“完全自我注册”软件包通常会更容易。 但是,如果您不使用macros,则不能允许用户select安装目录。 使用macros允许使用正确的安装目录dynamic更新registry。
如何使macros观作品? find您的BHO类的标题中的DECLARE_REGISTRY_RESOURCEID
macros,并将其注释掉。 在该标题中添加以下函数定义:
static HRESULT WINAPI UpdateRegistry( BOOL bRegister ) throw() { ATL::_ATL_REGMAP_ENTRY regMapEntries[2]; memset( ®MapEntries[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
macros是在RC文件中添加RGS的REGISTRY
资源的资源ID。
sm_szModulePath
variables必须包含代理进程EXE的安装path。 我select使它成为我的BHO类的公共静态成员variables。 一个简单的方法来设置它在DllMain
函数。 当regsvr32
加载你的Dll时, DllMain
被调用,并且registry被更新为良好的path。
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); }
非常感谢MladenJanković。
如何激励经纪过程?
一个可能的地方是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。
相反,“注入器”可能是一个长期存在的,最终永无止境的过程,它将不得不持续观看桌面,处理新的IEFrames。 这个过程的唯一性可以通过命名互斥体来保证。
就目前而言,我会尝试着用KISS的原则(保持简单,愚蠢)。 那就是:短暂的注射器。 我确实知道这会导致在BHO中对Tab拖放到桌面的情况进行特殊的处理,但我会在稍后看到。
去那条路线涉及注射器的末端生存的Dll注射,但是我将把它委派给Dll本身。
这里是注射器过程的代码。 它为托pipeIEFrame的线程安装WH_CALLWNDPROCRET
钩子,使用SendMessage
(带有特定的注册消息)立即触发Dll注入,然后删除钩子并终止。 BHO Dll必须导出名为HookCallWndProcRet
的CallWndRetProc
callback。 错误path被省略。
#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; }
生存注射:“勾住我更难”
在IE主进程中临时加载Dll足以将新button添加到工具栏。 但是能够监视WM_COMMAND
这个新的button需要更多的:一个永久加载的DLL和一个钩子仍然在位,尽pipe挂钩过程结束。 一个简单的解决scheme是再次挂接线程,传递Dll实例句柄。
由于每个标签页的打开都会导致一个新的BHO实例化,所以一个新的注入程序,钩子函数必须有一个方法来知道当前线程是否已经被钩住了(我不想为每个标签页开始添加一个钩子,那不干净)
线程本地存储是要走的路:
- 在
DllMain
中为DLL_PROCESS_ATTACH
分配一个TLS索引。 - 将新的
HHOOK
存储为TLS数据,并使用它来知道该线程是否已经被挂钩 - 当
DLL_THREAD_DETACH
时如果需要的话解除挂钩 - 释放
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 ); }
我们现在有一个几乎完整的钩子函数:
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
的代码AddBrowserActionForIE9
将被编辑。
对于IE9,获得结核病非常简单:
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.处理工具栏
这一部分可能会大大改善:
- 我只是创build了一个黑色和白色的位图,一切都很好,那就是:透明的黑色像素。 每次我试图添加一些颜色和/或灰度级,结果是可怕的。 我一点也不stream利,那些“工具栏中的位图魔法”
- 位图的大小应取决于工具栏中已有的其他位图的当前大小。 我只使用了两个位图(一个“正常”,一个“大”)
- 有可能优化的部分强制IE浏览器“重新绘制”工具栏的新状态,地址栏的宽度较小。 它工作,有一个快速的“重绘”阶段涉及整个IE主窗口。
看到我的问题的其他答案,因为我目前无法编辑代码格式工作的答案。
经过进一步的审查,我意识到,“collections夹和操作工具栏”只是一个普通的旧的公共控制工具栏(我以前认为这是某种自定义控件)。
我还没有能够调整我的代码,看看哪里需要我,但方法应该略有不同,我下面概述。
从我可以告诉,如果你想要你的工具栏button有一个图像,您必须先插入该图像到工具栏图像列表( TB_GETIMAGELIST
检索列表, TB_ADDBITMAP
添加您的图像)。
现在我们可以创build我们的TBBUTTON
实例,并使用TB_ADDBUTTONS
或TB_INSERBUTTONS
消息发送给我们的工具栏。
这应该得到在酒吧的button。 但是如何把它连接到你的代码?
单击button时,工具栏将生成一个WM_COMMAND
消息(可能与wParam
的低位字中的TBBUTTON
结构的iCommand
成员)。 所以我们只需要用WH_CALLWNDPROC
SetWindowsHookEx
并等待那个消息…
实施时即将实施;)
原始答复
正如我们前面讨论的那样,我怀疑是否有正式支持的方法在Internet Explorer UI中的该位置添加其他button(或者任何UI元素)。
但是,在IE浏览器窗口内部简单创build一个新的子窗口仍然存在“powershell”的方式。
到目前为止,我还没有能够创build一个完整的示例,主要是因为我尝试调整3个动作button所在的工具栏的大小已经失败。
无论如何,以下是我可以提出到目前为止:
internal class MyButtonFactory { public void Install() { IntPtr ieFrame = WinApi.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "IEFrame", null); IntPtr navigationBar = WinApi.FindWindowEx(ieFrame, IntPtr.Zero, "WorkerW", "Navigation Bar"); IntPtr reBar = WinApi.FindWindowEx(navigationBar, IntPtr.Zero, "ReBarWindow32", null); IntPtr controlBar = WinApi.FindWindowEx(reBar, IntPtr.Zero, "ControlBandClass", null); IntPtr toolsBar = WinApi.FindWindowEx(controlBar, IntPtr.Zero, "ToolbarWindow32", "Favorites and Tools Bar"); IntPtr myButton = WinApi.CreateWindowEx(0, "Button", "MySpecialButtonName", WinApi.WindowStyles.WS_CHILD | WinApi.WindowStyles.WS_VISIBLE, 0, 0, 16, 16, toolsBar, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); if (IntPtr.Zero == myButton) { Debug.WriteLine(new Win32Exception(Marshal.GetLastWin32Error()).Message); } IntPtr buttonWndProc = Marshal.GetFunctionPointerForDelegate(new WinApi.WndProc(WndProc)); WinApi.SetWindowLongPtr(new HandleRef(this, myButton), -4, buttonWndProc); // -4 = GWLP_WNDPROC } [AllowReversePInvokeCalls] public IntPtr WndProc(IntPtr hWnd, WinApi.WM msg, IntPtr wParam, IntPtr lParam) { switch (msg) { case WinApi.WM.LBUTTONUP: MessageBox.Show("Hello World"); break; default: return WinApi.DefWindowProc(hWnd, msg, wParam, lParam); } return IntPtr.Zero; } }
这需要几个Windows API调用,导致从pinvoke.net复制一个1600行兽,所以我会从这篇文章中省略。
除了事实上,我不能让button很好地适应工具栏,只要我设置我自己的窗口消息处理程序,该button不再绘制。
所以显然还需要做很多工作才能使这种方法奏效,但是我认为我会分享这个到目前为止。
想到的另一个想法是忽略整个工具栏,只需将button放在旁边。 也许这更容易处理。
在大量search网页上的Windows API相关术语的同时,我也遇到了CodeProject文章“ 将控制权添加到另一个应用程序上” ,这似乎在这方面可能非常相关。
从我的其他答案继续。
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
消息,并检查wParam
的命令ID。 真正的生产代码可能会更完整(确认WM_COMMAND确实来自工具栏)。
if ( pcwprets && ( pcwprets->message == WM_COMMAND ) ) { if ( LOWORD( pcwprets->wParam ) == 4242 ) { NotifyActiveBhoIE9( pcwprets->hwnd ); } }
NotifyActiveBhoIE9
函数将会:
a)在当前线程中查找IEFrame
b)findfind的IEFrame当前激活的标签
c)find托pipe标签的线程
每个BHO实例都有一个不可见的窗口,它的窗口文本中有线程标识符。 一个简单的FindWindow
调用会给我们这个窗口,BHO将会收到一条消息通知。
创build私人窗口:
// 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; }
呼叫站点:
当PROCESS_ATTACH
时, RegisterPrivateClass
在DllMain
中DllMain
在PROCESS_DETACH
时,在DllMain
中DllMain
UnregisterPrivateClass
当pUnkSite != NULL
时,在SetSite
调用CreatePrivateWindow
在SetSite
DestroyPrivateWindow
,当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 ); }
隐藏的窗口连接到一个经典的BHO:将this
指针存储在Windows Words中。
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会在源iexplore.exe进程中的新线程中创build一个新的IEFrame主窗口,并托pipe该选项卡。
为了检测,一个简单的解决scheme是侦听DISPID_WINDOWSTATECHANGED事件:使用IWebBrowser2::get_HWND
方法来检索当前的IE主窗口。 如果该窗口与先前保存的窗口不同,则该选项卡已被重新设置。 然后,启动代理程序:如果新的父框架还没有button,它将被添加。
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项目已经更新。
伙计,注射是答案。
在这里,你去 。
编辑:
当然。 看来你不需要做DLL注入,BHO就可以从IE进程里面访问。 那么这就容易多了
基本上,你需要先find窗口。 所以通过修改function来适应您的需求,它会看起来像这样:
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; }
其余的逻辑与我链接的文章相同。 你inheritance了拦截消息循环,并为你自己的button添加自己的事件处理程序。
另一种方法是创build一个button作为popup窗口,将IE窗口设置为父窗口,find“collections夹和工具栏”的位置,并将button放置在与其相邻的位置。 当然更容易,但不那么优雅。
编辑2:对不起,我刚刚看到我回应了一些奥利弗的答案。 但是,如果你按照我在BHO里面写的内容,那么button的行为就像IE自己的任何button一样,你完全可以控制它。