曾经写过一篇Windows下的任务栏图标编程 ,其实那篇文章讲的是如何将我们自己的程序最小化到托盘并进行操作的编程方法。前两天看到论坛 里有人讨论如何隐藏托盘图标,记得从前为朋友写过一个隐藏大智慧软件的程序,采用的方法就是将窗口最小化并隐藏大智慧在托盘上的图标,但是因为涉及到键盘钩子,最后并没有这么做,而是用添加一个虚拟桌面 的方法达到了效果。
看到这篇帖子 ,依稀想起我曾经做过的一些尝试,平时总是没有动力整理一些零碎的代码,这回碰到了也算给自己一个机会。
首先用Spy++查找一下系统托盘所在的窗口类:
可以看到我们需要的ToolbarWindow32其实是有层次的,但不能直接用FindWindow获取ToolbarWindow32句柄,而应该像下面的代码这样一层层深入查找。
- hWnd = ::FindWindow( “Shell_TrayWnd” , NULL);
- hWnd = ::FindWindowEx(hWnd, 0, “TrayNotifyWnd” , NULL);
- hWndTmp = ::FindWindowEx(hWnd, 0, “SysPager” , NULL);
- if (!hWndTmp)
- hWnd = ::FindWindowEx(hWnd, 0, “ToolbarWindow32” , NULL);
- else
- hWnd = ::FindWindowEx(hWndTmp, 0, “ToolbarWindow32” , NULL);
我们的目标是隐藏系统托盘图标,很明显是一个跨进程操作,因此要明白是哪个进程在维护这些图标,知道了窗口的句柄,获取其背后的进程就比较容易了:
- ret = ::GetWindowThreadProcessId(hWnd, &lngPID);
- hProcess = ::OpenProcess(PROCESS_ALL_ACCESS
- |PROCESS_VM_OPERATION
- |PROCESS_VM_READ
- |PROCESS_VM_WRITE,
- 0,
- lngPID);
首先获取进程ID,然后打开进程,获取进程句柄。
接着在进程内分配一段内存:
- lngAddress = VirtualAllocEx(hProcess, 0, 0x4096, MEM_COMMIT, PAGE_READWRITE);
- ret = ::SendMessage(hWnd,TB_GETBUTTON,i, long (lngAddress));
并向窗口发送TB_GETBUTTON消息,获得托盘Button的信息,这里的lngAddress中存储的是TBBUTTON结构,其内容如下:
- typedef struct _TBBUTTON {
- int iBitmap; // zero-based index of button image
- int idCommand; // command to be sent when button pressed
- BYTE fsState; // button state–see below
- BYTE fsStyle; // button style–see below
- DWORD dwData; // application-defined value
- int iString; // zero-based index of button label string
- } TBBUTTON;
当鼠标移动到系统托盘图标上后,会有一些提示信息,这些信息保存在dwData中,而idCommand则是TBBUTTON的id,我们要隐藏或者显示图标,都要针对这个图标id进行操作:
- ret = ::ReadProcessMemory(hProcess, LPVOID (lngTextAdr), strBuff, 1024, 0);
- ret = ::ReadProcessMemory(hProcess, LPVOID ( long (lngAddress) + 4), &lngButtonID, 4, 0);
strBuffer中存放提示信息,lngButtonID中存放id,有了这些信息,要操作托盘图标就非常简单了,例如我可以
根据某些条件匹配strBuffer,然后根据相应的lngButtonID隐藏图标,隐藏或者显示图标是发送TB_HIDEBUTTON消息。
- ::SendMessage(hWnd, TB_HIDEBUTTON, BID, MAKELONG ( true , 0)); //隐藏图标
- ::SendMessage(hWnd, TB_HIDEBUTTON, BID, MAKELONG ( false ,0)); //显示图标
当操作完成后,记得做清理工作:
- ::VirtualFreeEx( hProcess, lngAddress, 0x4096, MEM_RELEASE);
- ::CloseHandle(hProcess);
上面仅仅是针对一个图标的操作,其实可以发送TB_BUTTONCOUNT消息,获得图标数量,然后循环处理。这里 是我写的一个
sample,其中第一列显示的是图标id,第二列显示的是图标提示信息,在ListBox上选取,然后可以点击隐藏,恢复。
总结一下,隐藏Windows系统托盘图标基本步骤是这样的:
1:获取窗口句柄
2:打开系统进程
3:分配虚拟内存,读取进程内存获取相应信息
4:操作
5:释放内存,关闭进程句柄。