Grüße StackOverflowianer,
Wie entdeckt ici Windows 7 weist einen Fehler auf, bei dem das Ereignis DISPID_BEFORENAVIGATE2 für Windows Explorer-Instanzen nicht ausgelöst wird. Dieses Ereignis ermöglicht es Shell-Erweiterungen, benachrichtigt zu werden, wenn eine Navigation stattfinden soll, und (was für mich am wichtigsten ist) die Möglichkeit zu haben, die Navigation abzubrechen. Ich habe lange nach einem Workaround gesucht, und ich glaube, ich habe einen gefunden. Aber ich würde gerne ein paar Meinungen darüber einholen, wie sicher sie ist.
Ich habe in letzter Zeit viel mit API-Hooking gespielt, und ich benutze es bereits, um ein paar Funktionen für meine Erweiterung zu haken. Ich habe bemerkt, dass es eine Funktion im IShellBrowser die die Navigation steuert. Zuerst dachte ich, dass man so etwas nicht einhängen kann, aber als ich über die Layout eines COM-Objekts Ich erkannte, dass es möglich sein sollte, indem man einfach den richtigen Funktionszeiger aus der vtable jeder aktiven Instanz abgreift. Und tatsächlich, es funktioniert wie ein Traum. Nachdem der Hook gesetzt ist, laufen alle Navigationen in allen Explorer-Fenstern direkt durch meine Umleitungsfunktion, und ich kann entscheiden, ob sie auf der Grundlage ihrer Ziel-Pidl zurückgewiesen werden sollen.
Meine Frage ist also: Gibt es einen Grund, warum ich das nicht tun sollte? Ich habe noch nie gehört, dass API-Hooking verwendet wird, um COM-Objektfunktionen zu haken. Gibt es Umstände, unter denen es nicht funktionieren würde? Ist es gefährlich? (Zumindest gefährlicher als normales API-Hooking)
Der entsprechende Code folgt. Ich verwende MinHook ist eine minimalistische Hooking-Bibliothek, die die bewährte Methode der Trampolinfunktionen verwendet.
typedef HRESULT (WINAPI *BROWSEOBJECT)(IShellBrowser*, PCUIDLIST_RELATIVE, UINT);
HRESULT WINAPI DetourBrowseObject(IShellBrowser* _this, PCUIDLIST_RELATIVE pidl, UINT wFlags);
BROWSEOBJECT fpBrowseObject = NULL;
BROWSEOBJECT ShellBrowser_BrowseObject = NULL;
bool Initialize() {
if(MH_Initialize() != MH_OK) {
return false;
}
// Get a reference to an existing IShellBrowser. Any instance will do.
// ShellBrowser enum code taken from The Old New Thing
IShellWindows *psw;
BOOL fFound = FALSE;
if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_IShellWindows, (void**)&psw))) {
VARIANT v;
V_VT(&v) = VT_I4;
IDispatch *pdisp;
for (V_I4(&v) = 0; !fFound && psw->Item(v, &pdisp) == S_OK; V_I4(&v)++) {
IWebBrowserApp *pwba;
if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba))) {
IServiceProvider *psp;
if (SUCCEEDED(pwba->QueryInterface(IID_IServiceProvider, (void**)&psp))) {
IShellBrowser *psb;
if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser,IID_IShellBrowser, (void**)&psb))) {
fFound = true;
// Grab the 11th entry in the VTable, which is BrowseObject
void** vtable = (*(void***)(psb));
ShellBrowser_BrowseObject = (BROWSEOBJECT)(vtable[11]);
psb->Release();
}
psp->Release();
}
pwba->Release();
}
pdisp->Release();
}
psw->Release();
}
if(fFound) {
if(MH_CreateHook(ShellBrowser_BrowseObject, &DetourBrowseObject, reinterpret_cast<void**>(&fpBrowseObject)) != MH_OK) {
return false;
}
if(MH_EnableHook(ShellBrowser_BrowseObject) != MH_OK) {
return false;
}
}
return true;
}
HRESULT WINAPI DetourBrowseObject(IShellBrowser* _this, PCUIDLIST_RELATIVE pidl, UINT wFlags) {
if(NavigateIsOkay(pidl, wFlags)) {
return fpBrowseObject(_this, pidl, wFlags);
}
else {
return S_FALSE;
}
}