StackOverflowians の皆さん、こんにちは。
ここで発見されたように、Windows 7 には、Windows Explorer インスタンスに対して DISPID_BEFORENAVIGATE2 イベントが発生しないというバグがあります。このイベントにより、ナビゲーションが行われようとしているときにシェル拡張に通知でき、(私にとって最も重要なことに) ナビゲーションをキャンセルする機会があります。私はかなり長い間回避策を探していましたが、見つけたと思います。ただ、安全性については意見を頂きたいです。
私は最近 API フックで遊んでおり、拡張機能のいくつかの関数をフックするために既に使用しています。ナビゲーションを制御する関数が IShellBrowserにあることに気付きました。最初はそのようなものをフックすることはできないと思っていましたが、COM オブジェクトのレイアウトについて読んだときに、アクティブなインスタンスの vtable から適切な関数ポインターを取得するだけで可能であることに気付きました。案の定、それは夢のように機能します。フックが設定されると、すべての Explorer ウィンドウのすべてのナビゲーションが迂回機能を介して実行され、ターゲット pidl に基づいてそれらを拒否するかどうかを決定できます。
だから私の質問は、これをしてはいけない理由はありますか? COM オブジェクト関数をフックするために使用される API フックについては聞いたことがありません。それが機能しない状況はありますか?それは危険ですか?(少なくとも、通常の API フック以上のもの)
関連するコードは次のとおりです。私はMinHookを使用しています。これは、トランポリン関数の実証済みの方法を使用する最小限のフック ライブラリです。
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;
}
}