EDIT: https://github.com/somanuell/SoBrowserAction
Voici une capture d'écran de mon travail en cours.
Les choses que j'ai fait:
1. S'échapper de la mode protégé
Le BHO Inscription doit mettre à jour l' HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Low Rights\ElevationPolicy
- clés. Voir la Compréhension et le Travail en Mode Protégé d'Internet Explorer.
- Je choisir le processus, car il est noté comme "meilleure pratique" et est plus facile à déboguer, mais l' RunDll32Policy
peut faire l'affaire aussi.
Localiser l' rgs
le fichier contenant votre BHO les paramètres du registre. C'est l'une contenant le upadte à la Clé de Registre 'Browser Helper Object'
. Ajoutez à cela le fichier suivant:
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'
}
}
}
}
}
}
}
Le GUID doit être nouvelle, n'utilisez pas de mine, l'utilisation d'un Générateur de GUID.
L' 3
de la valeur de la politique garantit que le processus de courtier sera lancé en tant que moyen d'un processus d'intégrité. L' %MODULEPATH%
macro n'est PAS prédéfini.
Pourquoi utiliser une macro?
Vous pouvez éviter que le nouveau code dans votre fichier RGS, à condition que votre fichier MSI contient cette mise à jour du registre. En traitant avec MSI peuvent être douloureuses, il est souvent plus facile de fournir une "auto-inscription" paquet. Mais si vous n'avez pas utiliser une macro, vous ne pouvez pas permettre à l'utilisateur de choisir le répertoire d'installation. À l'aide d'une macro permet de mettre à jour dynamiquement le registre avec le bon répertoire d'installation.
Comment faire de la macro fonctionne?
Localiser l' DECLARE_REGISTRY_RESOURCEID
macro dans l'en-tête de votre BHO classe et de la commenter. Ajoutez la fonction suivante de la définition qui en-tête:
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);
}
Ce code est emprunté à partir de l'ATL de mise en œuvre pour l' DECLARE_REGISTRY_RESOURCEID
(dans mon cas, c'est celui fourni avec VS2010, vérifiez votre version de l'ATL et le code de mise à jour si nécessaire). L' IDR_CSOBABHO
macro est l'ID de la ressource de l' REGISTRY
des ressources de l'ajout de la RGS dans votre fichier RC.
L' sm_szModulePath
variable doit contenir le chemin d'installation du processus de courtier EXE. J'ai choisi de faire un public statique de la variable de membre de mon BHO classe. Un moyen simple, il est dans l' DllMain
fonction. Lors de l' regsvr32
chargement de votre fichier Dll, DllMain
est appelé, et le registre est mis à jour avec le bon chemin.
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);
}
Un grand merci à Mladen Janković.
Comment faire pour lancer le processus de Courtier?
Une place est dans l' SetSite
mise en œuvre. Il va être lancé plusieurs fois, mais nous traiterons que du processus lui-même. Nous verrons plus loin que le processus de courtier peut recevoir comme argument le HWND de l'hébergement IEFrame. Cela peut être fait avec l' IWebBrowser2::get_HWND
méthode. Je suppose ici que votre déjà un IWebBrowser2*
membre.
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. L'injection de la IEFrame fils
Il semble que ce peut être la partie la plus complexe, car il existe de nombreuses façons de le faire, chacun présentant des avantages et des inconvénients.
Le processus de courtier, le "injecteur", peut-être une courte durée, avec un argument simple (un HWND ou un TID), qui devra composer avec un unique IEFrame, si ce n'est déjà traitées par l'instance précédente.
Plutôt, la "injecteur" peut-être une longue durée de vie, finalement, ne s'arrêtant jamais, processus qui devront continuer à regarder le Bureau, le traitement de nouvelles IEFrames tels qu'ils apparaissent. L'unicité du processus peut être garanti par un Mutex Nommé.
Pour le moment, je vais essayer d'aller avec un principe KISS (Keep It Simple, Stupid). Que est: un court vécu de l'injecteur. Je sais pour sûr que cela conduira à un traitement spécial, dans le BHO, pour le cas d'un Onglet de Glisser Et Déposer des ed sur le Bureau, mais je vais voir ça plus tard.
Va que la route fait une injection de Dll qui survit à la fin de l'injecteur, mais je vais déléguer cela à la Dll elle-même.
Voici le code de l'injecteur processus. Il installe un WH_CALLWNDPROCRET
crochet pour le fil d'hébergement de la IEFrame, utilisez SendMessage
(avec un message enregistré) pour immédiatement déclencher l'injection de Dll, puis supprime le crochet et se termine. Le BHO Dll devez exporter CallWndRetProc
rappel nommé HookCallWndProcRet
. Erreur chemins sont omis.
#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. Survivant de l'Injection: "hook me harder"
La temporaire de chargement de la Dll dans le principal IE processus est suffisant pour ajouter un nouveau bouton à la Barre d'outils. Mais le fait d'être en mesure de surveiller l' WM_COMMAND
pour que le bouton nouveau nécessite plus: un chargé en permanence de Dll et d'un crochet toujours en place malgré la fin de l'accrochage processus. Une solution simple est de connecter le fil à nouveau, passant de la Dll handle d'instance.
Comme chaque onglet de l'ouverture d'un nouveau BHO instanciation, un nouveau injecteur processus, la fonction de raccordement doit avoir un moyen de savoir si le thread actuel est déjà accro (je ne veux pas simplement ajouter un crochet pour chaque onglet d'ouverture, qui n'est pas propre)
Thread Local Storage est le chemin à parcourir:
- Attribuer un index TLS en
DllMain
, pour DLL_PROCESS_ATTACH
.
- Magasin de la nouvelle -
HHOOK
TLS de données, et l'utiliser pour savoir si l'
thread est déjà accro
- Décrocher, si nécessaire, lors de l'
DLL_THREAD_DETACH
- Gratuit le TLS indice en
DLL_PROCESS_DETACH
Qui mène le code suivant:
// 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 );
}
Nous avons maintenant presque au complet crochet de la fonction:
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);
}
Le code pour AddBrowserActionForIE9
sera édité plus tard.
Pour IE9, l'obtention de la TUBERCULOSE est assez simple:
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. Le traitement de la Barre d'outils
Cette partie peut être largement améliorée:
- Je viens de créer un bitmap en noir et blanc, et tout était bien, c'est: les pixels noirs où transparent. Chaque fois que j'ai essayé d'ajouter un peu de couleurs et/ou de niveaux de gris, les résultats ont été horribles. Je ne suis pas à l'aise, à tous, avec ces "bitmap dans la barre d'outils de la magie"
- La taille de l'image bitmap doit dépend de la taille de l'autre bitmaps déjà dans la barre d'outils. J'ai juste utilisé deux images (une "normale", et un "gros")
- Il peut être possible d'optimiser la part de la force, c'est à dire "redessiner" l'état nouveau de la barre d'outils, avec une moindre largeur de la barre d'adresse. Il fonctionne, il est un moyen rapide de "redessiner" phase impliquant l'ensemble de l'IE de la Fenêtre Principale.
Voir mon autre réponse à la question, comme je suis actuellement dans l'impossibilité de modifier la réponse avec le format de code de travail.