如何拦截任务栏按钮调用?现有Hook方案问题排查
问题描述
- 我正在尝试拦截任务栏按钮的调用,目前通过Hook
CoCreateInstance函数检测ITaskbarList4的创建,但这种方式需要以挂起状态启动explorer,希望找到其他Hook方案。 - 当前
CoCreateInstance_H已被调用,我基于ITaskbarList4指针创建了MyTaskbarList子类,其HrInit()返回S_OK,但AddTab、RemoveTab、RegisterTab等其他函数未被触发,任务栏运行正常未崩溃,需要排查问题原因。
拦截逻辑代码
inline HRESULT(WINAPI* CoCreateInstance_O)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv) = CoCreateInstance; class MyTaskbarList : public ITaskbarList4 { public: MyTaskbarList(ITaskbarList4* original) : m_original(original) {} // IUnknown methods HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override { if (riid == __uuidof(IUnknown) || riid == __uuidof(ITaskbarList4)) { *ppvObject = static_cast<ITaskbarList4*>(this); AddRef(); return S_OK; } *ppvObject = nullptr; return E_NOINTERFACE; } ULONG STDMETHODCALLTYPE AddRef() override { return InterlockedIncrement(&m_refCount); } ULONG STDMETHODCALLTYPE Release() override { ULONG refCount = InterlockedDecrement(&m_refCount); if (refCount == 0) { delete this; } return refCount; } HRESULT STDMETHODCALLTYPE HrInit() override { HRESULT hr = m_original->HrInit(); return hr; } HRESULT STDMETHODCALLTYPE AddTab(HWND hwnd) override { return m_original->AddTab(hwnd); } HRESULT STDMETHODCALLTYPE DeleteTab(HWND hwnd) override { return m_original->DeleteTab(hwnd); } HRESULT STDMETHODCALLTYPE ActivateTab(HWND hwnd) override { return m_original->ActivateTab(hwnd); } HRESULT STDMETHODCALLTYPE SetActiveAlt(HWND hwnd) override { return m_original->SetActiveAlt(hwnd); } HRESULT STDMETHODCALLTYPE MarkFullscreenWindow(HWND hwnd, BOOL fFullscreen) override { return m_original->MarkFullscreenWindow(hwnd, fFullscreen); } HRESULT STDMETHODCALLTYPE SetProgressValue(HWND hwnd, ULONGLONG ullCompleted, ULONGLONG ullTotal) override { return m_original->SetProgressValue(hwnd, ullCompleted, ullTotal); } HRESULT STDMETHODCALLTYPE SetProgressState(HWND hwnd, TBPFLAG tbpFlags) override { return m_original->SetProgressState(hwnd, tbpFlags); } HRESULT STDMETHODCALLTYPE RegisterTab(HWND hwndTab, HWND hwndMDI) override { OutputDebugStringW(L"RegisterTab"); return m_original->RegisterTab(hwndTab, hwndMDI); } HRESULT STDMETHODCALLTYPE UnregisterTab(HWND hwndTab) override { return m_original->UnregisterTab(hwndTab); } HRESULT STDMETHODCALLTYPE SetTabOrder(HWND hwndTab, HWND hwndInsertBefore) override { return m_original->SetTabOrder(hwndTab, hwndInsertBefore); } HRESULT STDMETHODCALLTYPE SetTabActive(HWND hwndTab, HWND hwndMDI, DWORD dwReserved) override { return m_original->SetTabActive(hwndTab, hwndMDI, dwReserved); } HRESULT STDMETHODCALLTYPE ThumbBarAddButtons(HWND hwnd, UINT cButtons, LPTHUMBBUTTON pButton) override { return m_original->ThumbBarAddButtons(hwnd, cButtons, pButton); } HRESULT STDMETHODCALLTYPE ThumbBarUpdateButtons(HWND hwnd, UINT cButtons, LPTHUMBBUTTON pButton) override { return m_original->ThumbBarUpdateButtons(hwnd, cButtons, pButton); } HRESULT STDMETHODCALLTYPE ThumbBarSetImageList(HWND hwnd, HIMAGELIST himl) override { return m_original->ThumbBarSetImageList(hwnd, himl); } HRESULT STDMETHODCALLTYPE SetOverlayIcon(HWND hwnd, HICON hIcon, LPCWSTR pszDescription) override { return m_original->SetOverlayIcon(hwnd, hIcon, pszDescription); } HRESULT STDMETHODCALLTYPE SetThumbnailTooltip(HWND hwnd, LPCWSTR pszTip) override { return m_original->SetThumbnailTooltip(hwnd, pszTip); } HRESULT STDMETHODCALLTYPE SetThumbnailClip(HWND hwnd, RECT* prcClip) override { return m_original->SetThumbnailClip(hwnd, prcClip); } HRESULT STDMETHODCALLTYPE SetTabProperties(HWND hwndTab, STPFLAG stpFlags) override { return m_original->SetTabProperties(hwndTab, stpFlags); } private: ITaskbarList4* m_original; LONG m_refCount; }; MyTaskbarList* myTaskbarList = nullptr; HRESULT WINAPI CoCreateInstance_H(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv) { if (rclsid == CLSID_TaskbarList) { if (riid == IID_ITaskbarList4) { ITaskbarList4* original; HRESULT hr = CoCreateInstance_O(rclsid, pUnkOuter, dwClsContext, riid, (void**)&original); OutputDebugStringW(L"CoCreateInstance_H"); if (SUCCEEDED(hr)) { myTaskbarList = new MyTaskbarList(original); *ppv = myTaskbarList; OutputDebugStringW(L"success"); } return hr; } else OutputDebugStringW(L"!IID"); } return CoCreateInstance_O(rclsid, pUnkOuter, dwClsContext, riid, ppv); }
DLL入口代码
#include "stdafx.h" #include "Detours.h" #pragma comment(lib, "detours.lib") BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)CoCreateInstance_O, CoCreateInstance_H); DetourTransactionCommit(); } } return TRUE; }
问题排查与解决方案
一、子类函数未触发的原因分析
- QueryInterface实现不完整:你的
QueryInterface只处理了IUnknown和ITaskbarList4的IID,但任务栏内部可能会查询ITaskbarList、ITaskbarList2、ITaskbarList3等父接口。当调用方查询这些接口时,你的实现返回E_NOINTERFACE,导致调用方无法获取正确的接口指针,转而直接使用原始对象。- 修复方式:补充父接口IID的判断,或委托给原始对象处理其他接口查询:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override { if (riid == __uuidof(IUnknown) || riid == __uuidof(ITaskbarList) || riid == __uuidof(ITaskbarList2) || riid == __uuidof(ITaskbarList3) || riid == __uuidof(ITaskbarList4)) { *ppvObject = static_cast<ITaskbarList4*>(this); AddRef(); return S_OK; } return m_original->QueryInterface(riid, ppvObject); }
- 修复方式:补充父接口IID的判断,或委托给原始对象处理其他接口查询:
- 引用计数未初始化:
m_refCount默认是随机值,可能导致Release时提前销毁对象,后续调用无法触发。- 修复方式:在构造函数中初始化
m_refCount为0:MyTaskbarList(ITaskbarList4* original) : m_original(original), m_refCount(0) {}
- 修复方式:在构造函数中初始化
- Hook时机过晚:如果explorer已创建
ITaskbarList4实例后才注入DLL,Hook不会拦截已有实例,后续调用直接使用原始对象。- 验证方式:检查
CoCreateInstance_H的输出日志,确认是否在explorer初始化阶段被调用。
- 验证方式:检查
- 调用方使用父接口:任务栏内部可能通过
ITaskbarList3或更早的接口调用方法,子类仅覆盖ITaskbarList4方法,导致调用无法触发。- 修复方式:确保子类正确实现所有父接口的方法,或在
QueryInterface中处理所有相关IID。
- 修复方式:确保子类正确实现所有父接口的方法,或在
二、替代Hook方案(无需挂起启动explorer)
- Hook
CoGetClassObject:这是更底层的类对象获取函数,explorer创建ITaskbarList4时必然调用。Hook该函数拦截CLSID_TaskbarList的类对象,包装IClassFactory后在CreateInstance时返回子类实例,无需挂起启动explorer,运行时注入即可生效。 - 替换虚函数表:找到explorer中已有的
ITaskbarList4实例,替换其虚表中的方法指针为钩子函数。需注意多线程同步和虚表保护问题。 - Windows消息钩子:任务栏按钮操作会发送特定消息(如
WM_TASKBARBUTTONCREATED),通过全局消息钩子拦截这些消息,间接实现对任务栏按钮调用的拦截。该方式仅能拦截消息层面,无法直接拦截COM方法调用。
内容的提问来源于stack exchange,提问作者Katt
相关产品推荐
相关产品推荐

