- 浏览: 427878 次
文章分类
最新评论
一种清除windows通知区域“僵尸”图标的方案——XP系统解决方案
XP下“僵尸”图标的解决方案
从《一种清除windows通知区域“僵尸”图标的方案——问题分析》(以后简称《问题分析》)一文中分析的通知区域结构可以看出,XP的通知区域结构是相对简单的。如果我们解决了XP下的问题,那么Win7上的问题至少解决了一半——只有那个隐藏系统通知区域需要研究下。所以,我们先选择XP作为研究对象。(转载请指明出于breaksoftware的csdn博客)
从SPY++抓到的结构可以看出来,通知区域是一个ToolbarWindow32窗口类对象。ToolbarWindow32是windows一个标准的窗口类,虽然我们不能直接调用该类的一些方法来操作元素,但是该类对象响应Toolbar的标准消息。这也是我最开始的解决思路。
获取图标信息
在尝试去掉“僵尸”图标之前,有几个问题摆在我们面前
- 如何获取图标的总数
- 如何枚举到每个图标
- 如何获取图标的信息
- 如何找到我们创建的图标
查阅MSDN后,我发现Toolbar消息中TB_BUTTONCOUNT可以获取到通知区域图标总数。如此,我们便可以一个For循环枚举到所有图标。问题1和2便迎刃而解。那如何找到我们创建的图标呢?在《问题分析》一文中,我们在介绍初始化图标时,特别提出,我给图标Tip取了一个晦涩的名字——“中A英1文”。如此设计,也是因为我试图通过这个特征来识别图标(虽然这种方案存在不严谨性,但是图标的识别不是本文的主要的探讨课题)。
那么我们如何去获取图标的文字呢?查阅MSDN,发现TB_GETBUTTONTEXT这个消息可以获取图标的文字。其参数说明是
wParam Command identifier of the button whose text is to be retrieved. lParam Pointer to a buffer that receives the button text.这儿要注意几个问题:
- 什么是Command identfier?它和我们For循环传递的递增参数是一致的么?
- lParam指向的地址到底在哪个进程中?因为通知区域的进程载体是Explorer,而Explorer自然不可以访问到我们进程中的空间。如果这段空间在Explorer进程中,我们进程又如何才能读取到Explorer进程中的空间呢?
1 Command identifier of the button whose text is to be retrieved. 2 Zero-based index of the button for which to retrieve information.可以见得两者是不可以混用的。这两者存在一个推导关系,即可以通过2推导出1。实现这个过程的是TB_GETBUTTON消息。
wParam Zero-based index of the button for which to retrieve information. lParam Pointer to the TBBUTTON structure that receives the button information.lParam指向一个获取的结构体TBBUTTON信息
typedef struct { int iBitmap; int idCommand; BYTE fsState; BYTE fsStyle; #ifdef _WIN64 BYTE bReserved[6]; #else #if defined(_WIN32) BYTE bReserved[2]; #endif #endif DWORD_PTR dwData; INT_PTR iString; } TBBUTTON, *PTBBUTTON, *LPTBBUTTON;其中idCommand就是我们之前提到的Command identifier。
问题1我们解决了,那么问题2呢?我们不仅在发送TB_GETBUTTONTEXT消息时遇到这个问题,在发送TB_GETBUTTON消息时也会遇到。这个问题说到底就是跨进程的数据通信,那使用内存映射?No!管道?No!Socket?No!……
#define SENDMSGTIME 100 CSendMessageToProcess::CSendMessageToProcess(void) { } CSendMessageToProcess::CSendMessageToProcess( HWND hwnd, ENUMWINOWSENDPROC lpFunc ) { m_hWnd = hwnd; m_lpFun = lpFunc; m_stRemoteBuffer.lpBuffer = NULL; m_stRemoteBuffer.dwBufferSize = BUFFERSIZE; m_hProcess = NULL; InitializeRemoteBuffer(); } CSendMessageToProcess::~CSendMessageToProcess(void) { UnitializeRemoteBuffer(); } BOOL CSendMessageToProcess::InitializeRemoteBuffer() { if ( NULL != m_stRemoteBuffer.lpBuffer ) { return TRUE; } BOOL bSuc = FALSE; do { if ( 0 == m_stRemoteBuffer.dwBufferSize ) { ::SetLastError(ERROR_INVALID_PARAMETER); break; } DWORD dwProcessID = 0; GetWindowThreadProcessId(m_hWnd, &dwProcessID); m_hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ|PROCESS_VM_WRITE , FALSE, dwProcessID); if ( NULL == m_hProcess ) { break;; } m_stRemoteBuffer.lpBuffer = (LPBYTE)VirtualAllocEx(m_hProcess, NULL, m_stRemoteBuffer.dwBufferSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); if ( NULL == m_stRemoteBuffer.lpBuffer ) { break; } bSuc = TRUE; }while (0); return bSuc; } VOID CSendMessageToProcess::UnitializeRemoteBuffer() { if ( NULL != m_hProcess ) { VirtualFreeEx(m_hProcess, m_stRemoteBuffer.lpBuffer, 0, MEM_RELEASE); m_stRemoteBuffer.lpBuffer = NULL; m_stRemoteBuffer.dwBufferSize = 0; CloseHandle(m_hProcess); m_hProcess = NULL; } } BOOL CSendMessageToProcess::SendRemoteMessage( DWORD dwMsgID, DWORD dwIndexOrCmdId, PStBufferInfo lpstLocalBuffer, DWORD& dwRet ) { BOOL bSuc = FALSE; do { if ( NULL == lpstLocalBuffer ) { if ( 0 == ::SendMessageTimeout( m_hWnd, dwMsgID, dwIndexOrCmdId, 0, SMTO_ABORTIFHUNG, SENDMSGTIME, &dwRet ) ) { break; } bSuc = TRUE; break; } if ( NULL == lpstLocalBuffer || NULL == m_stRemoteBuffer.lpBuffer ) { ::SetLastError(ERROR_INVALID_PARAMETER); break; } if ( NULL == m_stRemoteBuffer.lpBuffer || 0 == m_stRemoteBuffer.dwBufferSize ) { ::SetLastError(ERROR_INVALID_PARAMETER); break; } if ( lpstLocalBuffer->dwBufferSize > m_stRemoteBuffer.dwBufferSize ) { ::SetLastError(ERROR_MORE_DATA); break; } DWORD dwWrite = 0; if ( NULL != lpstLocalBuffer->lpBuffer && 0 != lpstLocalBuffer->dwBufferSize ) { // 使用传入的数据初始化数据空间 if ( FALSE == WriteProcessMemory(m_hProcess, m_stRemoteBuffer.lpBuffer, lpstLocalBuffer->lpBuffer, lpstLocalBuffer->dwBufferSize , &dwWrite) ) { break; } } if ( 0 == ::SendMessageTimeout( m_hWnd, dwMsgID, dwIndexOrCmdId, (LPARAM)m_stRemoteBuffer.lpBuffer, SMTO_ABORTIFHUNG, SENDMSGTIME, &dwRet ) ) { break; } DWORD dwRead = 0; if ( NULL != lpstLocalBuffer->lpBuffer && 0 != lpstLocalBuffer->dwBufferSize ) { if ( FALSE == ReadProcessMemory( m_hProcess, m_stRemoteBuffer.lpBuffer, lpstLocalBuffer->lpBuffer, lpstLocalBuffer->dwBufferSize, &dwRead)) { break; } } bSuc = TRUE; } while (0); return bSuc; }这儿有个特别需要说明的:我使用的是SendMessageTimeout发送消息,而不是SendMessage。因为我们对其他进程发送消息时,我们无法保证其他进程在处理消息时是否会非常费时,或者压根就不返回,从而导致我们发起消息的线程被堵塞。一般来说,比较好的方式是采用PostMessage向其他进程发送消息。但是由于我们的流程需要同步执行,所以在保证同步的情况下,同时兼顾SendMessage执行超时,采用了SendMessageTimeout方式。
BOOL CSendMessageToProcess::EnumChild() { BOOL bSuc = FALSE; do { DWORD dwChildCount = 0; if ( 0 == ::SendMessageTimeout(m_hWnd, TB_BUTTONCOUNT, 0, 0, SMTO_ABORTIFHUNG, SENDMSGTIME, &dwChildCount) ) { break; } BOOL bContinue = TRUE; for ( DWORD dwIndex = 0; dwIndex < dwChildCount && bContinue; dwIndex++ ) { bContinue = m_lpFun(this, dwIndex); } bSuc = TRUE; } while (0); return bSuc; }这儿m_lpFun是一个回调函数,其用来判定和执行清除“僵尸”图标的目的。其回调函数是
static BOOL CALLBACK DealChildComp(CSendMessageToProcess* lpThis, DWORD dwIndex) { BOOL bContinue = TRUE; do { BYTE byLocalBuffer[BUFFERSIZE] = {0}; WCHAR wchBuffer[BUFFERSIZE] = {0}; memset(byLocalBuffer, 0, sizeof(byLocalBuffer)); TBBUTTON TBButton; memset(&TBButton, 0, sizeof(TBButton)); StBufferInfo stLocalBuffer; stLocalBuffer.lpBuffer = &TBButton; stLocalBuffer.dwBufferSize = sizeof(TBBUTTON); DWORD dwSucGetButton = 0; if ( FALSE == lpThis->SendRemoteMessage(TB_GETBUTTON, dwIndex, &stLocalBuffer, dwSucGetButton ) ) { continue; } memset(byLocalBuffer, 0, sizeof(byLocalBuffer)); stLocalBuffer.lpBuffer = byLocalBuffer; stLocalBuffer.dwBufferSize = sizeof(byLocalBuffer); DWORD dwRetLenth = 0xFFFFFFFF; if ( FALSE == lpThis->SendRemoteMessage(TB_GETBUTTONTEXTW, TBButton.idCommand, &stLocalBuffer, dwRetLenth ) ) { continue; } if ( 0xFFFFFFFF == dwRetLenth ) { continue; } memset(wchBuffer, 0, sizeof(wchBuffer)); memcpy_s( wchBuffer, sizeof(wchBuffer), byLocalBuffer, dwRetLenth * sizeof(WCHAR)); std::cout<<wchBuffer<<std::endl; if ( 0 != lstrcmp( L"中A英1文", wchBuffer)) { continue; } g_bFind = TRUE; //bContinue = lpThis->ClearIcon(TBButton.idCommand, FALSE); bContinue = lpThis->ClearIcon(dwIndex, TRUE); } while (0); return bContinue; }下一步,我们将要将重心放在如何清除“僵尸”图标上。注意一下ClearIcon这个函数,从我上面列出的代码可以看出,貌似是存在两种方法。是的,的确是两种。之后我将详细介绍这两种方法。
直接删除“僵尸”图标
MSDN上给出了Toolbar消息的所有名称,其中最开始吸引我的是TB_DELETEBUTTON这个消息。在经过上面一系列努力后,我们只要发送这个消息给通知区域便可以干净利索优雅的清除“僵尸”图标。其参数说明是
wParam Zero-based index of the button to delete. lParam Must be zero.我们的代码是
BOOL CSendMessageToProcess::ClearIcon(DWORD dwIndexOrCmdID, BOOL bByDelete/* = FALSE*/) { BOOL bContinue = FALSE; do { if ( bByDelete ) { DWORD dwNULL = 0; SendRemoteMessage(TB_DELETEBUTTON, dwIndexOrCmdID, NULL, dwNULL); bContinue = TRUE; break; }这样看似没什么问题了,但是我们看下执行的结果
看下红色框住的区域(非水印内容),“僵尸”图标的确是被删除了,但是任务栏的长度却没有变化!这是这种最优雅的方法的最失败的地方,也正是这个缺陷促使我再次寻找能彻底解决的方法。但是其实这个技术缺陷可以通过产品设计的方法来规避:我们进程启动时,清除“僵尸”图标,然后创建一个可用的图标。这样会促使通知区域重新计算区域大小,从而触发一次自动调整。
模拟鼠标方式去除“僵尸”图标
- 如何计算出“僵尸”图标的位置
- 发送哪些消息
wParam Command identifier of the button. lParam Pointer to a RECT structure that will receive the bounding rectangle information.第二个问题我们可以在计算好滑动区域的情况下,发送WM_MOUSEMOVE,对应的代码是
else { RECT rc; StBufferInfo stLocalBuffer; stLocalBuffer.lpBuffer = &rc; stLocalBuffer.dwBufferSize = sizeof(rc); DWORD dwGetRectRet = 0; if ( FALSE == SendRemoteMessage(TB_GETRECT, dwIndexOrCmdID, &stLocalBuffer, dwGetRectRet ) ) { break;; } if ( 0 == dwGetRectRet ) { break; } MouseMoveOnWindow(m_hWnd, &rc); } } while (0); return bContinue; }
VOID MouseMoveOnWindow( HWND hWnd, LPRECT lpRect ) { if ( FALSE == ::IsWindow(hWnd) || NULL == lpRect) { return; } RECT rc; memset(&rc, 0, sizeof(rc)); if ( FALSE == ::GetWindowRect(hWnd, &rc) ) { return; } // 滑动的点 POINT pt; DWORD dwStart = 0; DWORD dwEnd = 0; dwStart = lpRect->left; dwEnd = lpRect->right; pt.y = (lpRect->top + lpRect->bottom)/2; for ( DWORD loffset = dwStart; loffset < dwEnd; loffset = loffset + STOPLENGTH) { pt.x = loffset; ::PostMessage(hWnd, WM_MOUSEMOVE, 0, MAKELONG(pt.x, pt.y)); Sleep(10); } }
使用IAccessible接口枚举并删除“僵尸”图标
#include "Accessible.h" BOOL EnumAccessible( HWND hwnd, XENUMACCESSIBLEPROC lpEnumAccessibleProc ) { BOOL bRet = FALSE; _ASSERTE (::IsWindow(hwnd)); _ASSERTE(lpEnumAccessibleProc); if (::IsWindow(hwnd) && lpEnumAccessibleProc) { CComPtr<IAccessible> pIAcc; HRESULT hr = AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**)&pIAcc); if (SUCCEEDED(hr) && pIAcc) { CComVariant varChild; CComPtr<IAccessible> pIAccChild; FindChild(pIAcc, pIAccChild, varChild, lpEnumAccessibleProc); bRet = TRUE; } } return bRet; } static BOOL FindChild(CComPtr<IAccessible>& pIAccParent, CComPtr<IAccessible>& pIAccChild, CComVariant& varChild, XENUMACCESSIBLEPROC lpEnumAccessibleProc) { BOOL bSuc = FALSE; BOOL bContinue = TRUE; do { if ( NULL == pIAccParent || NULL ==lpEnumAccessibleProc) { break; } BOOL bContinue = TRUE; CComPtr<IEnumVARIANT> pEnum; HRESULT hr = pIAccParent->QueryInterface(IID_IEnumVARIANT, (PVOID*) &pEnum); if ( SUCCEEDED(hr) && pEnum) { pEnum->Reset(); } // get child count long nChildren = 0; unsigned long nFetched = 0; pIAccParent->get_accChildCount(&nChildren); for (long index = 1; (index <= nChildren) && bContinue; index++) { varChild.Clear(); if ( pEnum ) { hr = pEnum->Next(1, &varChild, &nFetched ); if ( FAILED(hr)) { bContinue = FALSE; break; } } else { varChild.vt = VT_I4; varChild.lVal = index; } // get IDispatch interface for the child CComPtr<IDispatch> pDisp; if ( VT_I4 == varChild.vt ) { hr = pIAccParent->get_accChild(varChild, &pDisp); } else if ( VT_DISPATCH == varChild.vt ) { pDisp = varChild.pdispVal; } // get IAccessible interface for the child CComPtr<IAccessible> pCAcc; if ( NULL != pDisp ) { hr = pDisp->QueryInterface(IID_IAccessible, (void**)&pCAcc); if ( FAILED(hr) ) { continue; } } // get information about the child if ( NULL != pCAcc) { varChild.Clear(); varChild.vt = VT_I4; varChild.lVal = CHILDID_SELF; pIAccChild = pCAcc; } else { pIAccChild = pIAccParent; } DWORD dwState = 0; if ( FALSE == GetObjectState(pIAccChild, varChild, dwState) ) { continue; } // check if object is available if (dwState & STATE_SYSTEM_INVISIBLE ) { continue; } HWND hwndChild = 0; WindowFromAccessibleObject(pIAccChild, &hwndChild); // call enum callback bContinue = lpEnumAccessibleProc(pIAccChild, varChild, hwndChild); if (bContinue && pCAcc) { bContinue = FindChild(pCAcc, pIAccChild, varChild, lpEnumAccessibleProc); } } bSuc = TRUE; } while (0); return bContinue; } BOOL GetObjectState( CComPtr<IAccessible>& pAcc, CComVariant& varChild, DWORD& dwState ) { BOOL bRet = FALSE; dwState = 0; do { if ( NULL == pAcc ) { break; } CComVariant varState; HRESULT hr = pAcc->get_accState(varChild, &varState); if ( FAILED(hr) || VT_I4 != varState.vt ) { break; } dwState = varState.lVal; bRet = TRUE; } while (0); return bRet; } BOOL GetObjectRole( CComPtr<IAccessible>& pAcc, CComVariant& varChild, DWORD& dwRole ) { BOOL bRet = FALSE; dwRole = 0; do { if ( NULL == pAcc ) { break; } CComVariant varRole; HRESULT hr = pAcc->get_accRole(varChild, &varRole); if ( FAILED(hr) || VT_I4 != varRole.vt ) { break; } dwRole = varRole.lVal; bRet = TRUE; } while (0); return bRet; } BOOL GetObjectName( CComPtr<IAccessible>& pAcc, CComVariant& varChild, LPVOID lpName, DWORD dwBufferSize, DWORD& dwWrite ) { BOOL bRet = FALSE; do { if ( NULL == pAcc || NULL == lpName ) { break; } CComBSTR bstrName; HRESULT hr = pAcc->get_accName(varChild, &bstrName); if ( FAILED(hr) ) { break; } if ( dwBufferSize < bstrName.ByteLength() ) { break; } if ( 0 != memcpy_s(lpName, dwBufferSize, (LPWSTR)bstrName, bstrName.ByteLength()) ) { break; } dwWrite = bstrName.ByteLength(); bRet = TRUE; } while (0); return bRet; } BOOL GetObjectRoleString( CComPtr<IAccessible>& pAcc, CComVariant& varChild, LPVOID lpBuffer, DWORD dwBufferLength, DWORD& dwWrite ) { BOOL bRet = FALSE; DWORD dwRole = 0; do { if ( NULL == pAcc || NULL == lpBuffer ) { break; } CComVariant varRole; HRESULT hr = pAcc->get_accRole(varChild, &varRole); if ( FAILED(hr) ) { break; } if ( VT_I4 == varRole.vt ) { dwRole = varRole.lVal; dwWrite = ::GetRoleText(dwRole, (LPWSTR)lpBuffer, dwBufferLength / sizeof(WCHAR)); if ( 0 == dwWrite ) { break; } dwWrite *= sizeof(WCHAR); } else if ( VT_BSTR == varRole.vt ) { CComBSTR bstrRoletext(varRole.bstrVal); if ( dwBufferLength < bstrRoletext.ByteLength() ) { break; } if ( 0 != memcpy_s(lpBuffer, dwBufferLength, (LPWSTR)bstrRoletext, bstrRoletext.ByteLength())) { break; } dwWrite = bstrRoletext.ByteLength(); } bRet = TRUE; } while (0); return bRet; } BOOL GetObjectDescription( CComPtr<IAccessible>& pAcc, CComVariant& varChild, LPVOID lpBuffer, DWORD dwBufferLength, DWORD& dwWrite ) { BOOL bRet = FALSE; do { if ( NULL == pAcc || NULL == lpBuffer ) { break; } CComBSTR bstrDescription; HRESULT hr = pAcc->get_accDescription(varChild, &bstrDescription); if ( FAILED(hr) ) { break; } if ( dwBufferLength < bstrDescription.ByteLength() ) { break; } if ( 0 != memcpy_s(lpBuffer, dwBufferLength, (LPWSTR)bstrDescription, bstrDescription.ByteLength()) ) { break; } dwWrite = bstrDescription.ByteLength(); bRet = TRUE; } while (0); return bRet; } BOOL GetObjectValue( CComPtr<IAccessible>& pAcc, CComVariant& varChild, LPVOID lpBuffer, DWORD dwBufferLength, DWORD& dwWrite ) { BOOL bRet = FALSE; do { if ( NULL == pAcc || NULL == lpBuffer ) { break; } CComBSTR bstrValue; HRESULT hr = pAcc->get_accValue(varChild, &bstrValue); if ( FAILED(hr) ) { break; } if ( dwBufferLength < bstrValue.ByteLength() ) { break; } if ( 0 != memcpy_s(lpBuffer, dwBufferLength, (LPWSTR)bstrValue, bstrValue.ByteLength() ) ) { break; } dwWrite = bstrValue.ByteLength(); bRet = TRUE; } while (0); return bRet; } BOOL GetObjectLocation( CComPtr<IAccessible>& pAcc, CComVariant& varChild, RECT& rect ) { BOOL bRet = FALSE; do { if ( NULL == pAcc) { break; } HRESULT hr = pAcc->accLocation(&rect.left, &rect.top, &rect.right, &rect.bottom, varChild); if ( FAILED(hr) ) { break; } // accLocation returns width and height rect.right += rect.left; rect.bottom += rect.top; bRet = TRUE; } while (0); return bRet; }
相关推荐
一种清除windows通知区域僵尸图标的方案-XP系统解决方案.docx
一种清除windows通知区域僵尸图标的方案-Windows7系统解决方案.docx
植物大战僵尸————小鬼僵尸
植物大战僵尸————铁门僵尸
植物大战僵尸————路障僵尸
植物大战僵尸————报纸僵尸
植物大战僵尸————旗子僵尸
植物大战僵尸————舞王僵尸
植物大战僵尸————普通僵尸
植物大战僵尸————小丑僵尸
植物大战僵尸————冰车僵尸
植物大战僵尸————铁桶僵尸
植物大战僵尸————阳光
植物大战僵尸————魅惑菇
植物大战僵尸————冰车僵尸2
植物大战僵尸————樱桃炸弹
植物大战僵尸————橄榄球僵尸(无头)
植物大战僵尸————向日葵
植物大战僵尸————墓碑
植物大战僵尸————三叶草