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

Posted dvlinker

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VC++检查程序托盘图标是否掩藏到沙漏区域中(附源码)相关的知识,希望对你有一定的参考价值。

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

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

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

// 判断当前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++检查程序托盘图标是否掩藏到沙漏区域中(附源码)的主要内容,如果未能解决你的问题,请参考以下文章

VC6.0程序编译后如何为程序添加图标

请问怎样使程序在最小化后图标放在任务栏的托盘上?

最小化到系统托盘区是啥意思?

Qt开发实现系统托盘,托盘菜单,托盘消息

最小化到托盘程序是啥意思?通俗点哈

vc 如何实现关闭时最小化到托盘