您的位置:首页 > 其它

获取程序在任务栏中按钮的位置的方法

2010-01-06 00:34 260 查看
本人是上海同济大学软件学院的一个学生,在做一个期末项目的时候希望能够模拟“最小化”的一个动画效果,所以需要获得程序的任务栏按钮位置。但是从网上搜索的结果出乎意料的沮丧,好像真的没有一篇文章给出了完整的答案。所以今天努力了一下,给出一个正确的方法,代码直接就贴下面了用于交流。

由于按钮不是窗口,所以除非微软提供函数接口,否则我们无法得知任务栏是如何绘制的。

//头文件:

#include"Commctrl.h"

从网上看到,对一个TabButton(任务栏)可以发送以下消息(前题是有上述头文件)

TB_GETBUTTONTEXT

TB_GETBUTTONINFO

TB_BUTTONCOUNT

等等,但是http://topic.csdn.net/t/20030423/13/1697563.html这个文章只介绍了TB_BUTTONCOUNT,原因是什么呢?大家可以自己试一下,对着一个找到的任务栏窗口发送TB_GETBUTTONINFO有什么结果,我的结果是报了一个系统错误(从后面可以知道,是访问了一块没有被开辟的内存),然后任务栏重启(句柄被修改了,说明是重启过了)。

那么仔细看msdn

TB_GETRECT wParam = (WPARAM)(INT) iID;
lParam = (LPARAM)(LPRECT) lprc;

Parameters

iID Button identifier. lprc Returns nonzero if successful, or zero otherwise. A RECT structure that receives the bounding rectangle information.
似乎没有任何问题。

我的调用方法如下(tool是窗口句柄):

DWORD si = SendMessage(tool, TB_GETRECT, (WPARAM)0, (LPARAM)prect);

一开始我以为是按钮的iID是一些特殊的整数,但是从0试到10几发现都有问题。每次都会出错重启一下任务栏(本人水平有限,不知道怎么获得报错信息,早点知道说不定早点就想到了),导致我想到一个问题,我传入的参数prect是我的进程中的地址,我之前直接通过new函数获得,但是我没有传入本进程的进程号或者已经打开了的进程句柄,任务栏对应的进程如何获得我的进程映射地址对应的内存地址?显然,TB_GETRECT 应该只对本地进程地址进行操作的。

既然这样,那我只需要为任务栏进程开辟对应的空间即可。

步骤简述:

1、找到任务栏的窗口句柄(我这里方便起见,直接用spy++抓获)

2、通过GetWindowThreadProcessId获得窗口对应的进程号

3、OpenProcess使用PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE这三个权限打开进程

4、VirtualAllocEx为此进程分配地址空间(空间必须开足够)

5、SendMessage发送消息,把分配得到的地址作为参数传入。

6、ReadProcessMemory将修改之后的内存复制一份到本地进程,用于读取。

7、(作为程序员素质的必须)VirtyalFreeEx释放内存。

代码:

for(int j = 0; j<50; j++)
{
SIZE_T len = 256;//sizeof(RECT);
TCHAR * ptb = new TCHAR[256];
SIZE_T size;
HWND tool = (HWND)0x002906C6;
DWORD proID;
GetWindowThreadProcessId(tool, &proID);
HANDLE hPro = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE , NULL, proID);
PVOID pBun = VirtualAllocEx(hPro, NULL, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
UINT buffer = len;
DWORD si = SendMessage(tool, TB_GETBUTTONTEXT , (WPARAM)j, (LPARAM)pBun);
ReadProcessMemory(hPro, pBun, (LPVOID)ptb, len, &size);
VirtualFreeEx(hPro, pBun, len, MEM_DECOMMIT);
delete ptb;
}

非常简短的几行就够了,理解起来应该不会让人头晕,其中tool这个窗口句柄的获得大家自由发挥^_^ 我说过我是直接用spy++抓取的。

最后扩展一下:

--使用此方法察看了之后发现,任务栏的“按钮”数量不只有现实的那么几个,一般程序都有两个(另外一个大小为0,不信自己看),我用VS2008编译生成的可执行程序运行出来之后也有两个,一个和进程同名,一个和窗口同名,这个大概就属于“编译器做的手脚”,因为有的程序只有一个button对应,所以应该可以修改编译设置,具体哪个有待高人解决。

--按钮ID从0开始,我上面的程序最后一个按钮返回的pBun一个指向NULL的指针,那么这个就是结束标记。

--有些消息,例如:TB_GETBUTTONINFO消息的发送,发送之前得先填充TBBUTTONINFO结构体的的cbSize属性,需要额外使用一个WriteProcessMemory把前四个字节填充掉。这些结构体都定义在Commctrl.h中,所以查一下就出来了。

--带有WS_EX_TOOLWINDOW属性的窗口没有按钮信息对应。

不好意思,似乎最重要的东西忘记了,就是怎么获得程序对应的按钮?我的方法是枚举按钮的TEXT信息(就是我上面写的),如果发现了一个和进程同名的按钮,并且紧跟着一个和窗口同名的按钮,那么这个和窗口同名的按钮的ID就是我需要的ID,然后再发送TB_GETRECT获得按钮位置。

还没完,因为这个按钮位置是相对窗口的而不是显示器,刚刚一开始不是都已经获得任务栏的句柄了吗,接下来只要通过GetWindowRect获得窗口位置,再加上按钮坐标就行了。

##完
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: