VC++检查程序托盘图标是否掩藏到沙漏区域中(附源码)

       win7及以上系统引入了托盘图标沙漏区域,创建的新托盘图标是放置在托盘沙漏区域的,默认是掩藏在该沙漏区域中的。通过向上的箭头可以展开沙漏区域,如下所示:

VC++检查程序托盘图标是否掩藏到沙漏区域中(附源码)_第1张图片

很多程序在第一次运行时会弹出一个提示框,提示程序的托盘图标被掩藏到沙漏区域中了,可以把托盘图标拖到任务栏上来,退出掩藏状态,这样方便对托盘图标进行操作,也能看到托盘图标的闪动提示(比如收到聊天消息时的闪动)。

       那么问题来了,该如何判断托盘图标掩藏到沙漏区域了呢?我们是有办法的,代码如下:

// 判断当前32进程是否运行在64位系统上
BOOL IsRunOnWin64()
{
	BOOL bWow64 = FALSE;
	BOOL bRet = IsWow64Process( GetCurrentProcess(), &bWow64 );
	if ( !bRet )
	{
		return FALSE;
	}

	return bWow64;
}

// 判断win7/win8下TrueLink托盘图标是否被掩藏
BOOL IsTrueLinkTrayHidden()
{
	HWND hWnd = NULL, hWndPager = NULL;  
	unsigned long ulPID = 0;  
	long lRet = 0, lButtonCount = 0;  
	HANDLE hProcess = NULL;  
	LPVOID pAddress = NULL;  
	long lTextAdr = 0;  
	TCHAR achBuff[1024] = { 0 };  
	const TCHAR *pTemp = NULL;  

	// 通过窗口的层次关系找到托盘区域窗口,至于窗口层次关系可以通过SPY++工具去查看
	hWnd = ::FindWindow( _T("Shell_TrayWnd"), NULL );  
	hWnd = ::FindWindowEx( hWnd, 0, _T("TrayNotifyWnd"), NULL );  
	hWndPager = ::FindWindowEx( hWnd, 0, _T("SysPager"), NULL );  
	if( !hWndPager )
	{
		hWnd = ::FindWindowEx( hWnd, 0, _T("ToolbarWindow32"), NULL ); // win2000
	}
	else  
	{
		hWnd = ::FindWindowEx( hWndPager, 0, _T("ToolbarWindow32"), NULL ); // win xp及以上系统  
	}

	// 根据TBBUTTON的结构体构成,找到iString成员的地址偏移
	int nStrOffset = sizeof(TBBUTTON) - sizeof(INT_PTR);
	if ( IsRunOnWin64() )
	{
		// 当前进程是32位进程,字节是以4字节对齐
		// 在64位系统上,托盘图标区域隶属于explorer资源管理器进程,是64位进程,在该进程中
		// TBBUTTON中的各字段的长度都变长了,所以要针对64位情况计算偏移(要考虑不同系统中
		// 的字节对齐问题,TBBUTTON结构体在64位进程中是8字节对齐),by 2014/09/10
		nStrOffset += 4; // bReserved在win64下多出来的4字节
		nStrOffset += 4; // DWORD_PTR在win64下多出来的4字节
	}

	const DWORD dwAllocSize = 0x4096; // 下面的VirtualAllocEx分配的内存大小

	// 托盘图标区域窗口隶属于explorer资源管理器进程,托盘图标的信息在explorer进程中,涉及到跨进程内存的访问
	// 要用到ReadProcessMemory
	lRet = GetWindowThreadProcessId( hWnd, &ulPID );  
	hProcess = OpenProcess( PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, 0, ulPID );
	if ( hProcess == NULL )
	{
		return FALSE;
	}

	// VirtualAllocEx跨进程到explorer进程中分配内存
	pAddress = VirtualAllocEx( hProcess, 0, dwAllocSize, MEM_COMMIT, PAGE_READWRITE );  
	lButtonCount = ::SendMessage( hWnd, TB_BUTTONCOUNT, 0, 0 ); 

	for( int i=0; i< lButtonCount; i++ )  
	{  
		// 将TBBUTTON结构体信息获取到VirtualAllocEx分配的explorer进程中的内存中
		lRet = ::SendMessage( hWnd, TB_GETBUTTON, i, long(pAddress) );  
		// 根据偏移值跨进程读取读取TBBUTTON中的iString成员的内容
		lRet = ReadProcessMemory( hProcess, LPVOID(long(pAddress) + nStrOffset), &lTextAdr, 4, 0 );  
		if( lTextAdr != -1 )
		{  
			// 将iString成员的内容读出来
			lRet = ReadProcessMemory( hProcess, LPVOID(lTextAdr), achBuff, sizeof(achBuff), 0 );   
			pTemp = _tcsstr( achBuff, _T("Test")); // 通过窗口名称来比对  
			if ( pTemp != NULL ) // 找到对应字串
			{  
				// If the dwFreeType parameter is MEM_RELEASE, dwSize must be 0 (zero).
				VirtualFreeEx( hProcess, pAddress, 0, MEM_RELEASE );  
				CloseHandle( hProcess );  
				return FALSE;
			}        
		}  
	}  

	// If the dwFreeType parameter is MEM_RELEASE, dwSize must be 0 (zero).
	VirtualFreeEx( hProcess, pAddress, 0, MEM_RELEASE );  
	CloseHandle( hProcess );  

	return TRUE;
}

       上述代码中有详细的注释,具体逻辑我就不再赘述了。

        有一点需要注意一下,我们的进程为了兼容32为操作系统,做成32位进程,对于32为程序,内存是以4字节对齐。如果操作系统是64位的,内存是以8字节对齐的,托盘图标区域隶属于explorer资源管理器进程,是64位进程,在该进程中TBBUTTON中的各字段的长度都变长了,所以要针对64位情况计算偏移。所以代码中判断了当前运行的系统是32位的,还是64位的。要考虑不同系统中的字节对齐问题,TBBUTTON结构体在64位进程中是8字节对齐。

你可能感兴趣的:(VC++常用功能代码封装,托盘图标,任务栏,沙漏区域)